Progress towards better email notifications (#3911)

Working mail from notification.  Still requires testing/cleaning

Add tests around checkout notification.

This also removes the ability to check out an asset to a location|asset
that requires acceptance/a Eula.  For 4.1 we may think about how to
support such a thing, but at present it seems to make sense to only alow
such assets to be checked out to users, who can be responsible for the
items.
This commit is contained in:
Daniel Meltzer 2017-08-31 14:14:21 -04:00 committed by snipe
parent 8d2c229bc3
commit bb874012d9
20 changed files with 368 additions and 272 deletions

View file

@ -0,0 +1,12 @@
<?php
namespace App\Exceptions;
use Exception;
class CheckoutNotAllowed extends Exception
{
public function __toString()
{
"A checkout is not allowed under these circumstances";
}
}

View file

@ -260,7 +260,7 @@ class AccessoriesController extends Controller
'assigned_to' => $request->get('assigned_to') 'assigned_to' => $request->get('assigned_to')
]); ]);
$logaction = $accessory->logCheckout(e(Input::get('note'))); $logaction = $accessory->logCheckout(e(Input::get('note')), $user);
DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first();

View file

@ -566,7 +566,7 @@ class AssetsController extends Controller
$data['item_serial'] = $asset->serial; $data['item_serial'] = $asset->serial;
$data['note'] = $logaction->note; $data['note'] = $logaction->note;
if ((($asset->checkin_email()=='1')) && (isset($user)) && (!config('app.lock_passwords'))) { if ((($asset->checkin_email()=='1')) && (isset($user)) && (!empty($user->email)) && (!config('app.lock_passwords'))) {
Mail::send('emails.checkin-asset', $data, function ($m) use ($user) { Mail::send('emails.checkin-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name); $m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name')); $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));

View file

@ -283,7 +283,7 @@ class ComponentsController extends Controller
'asset_id' => $asset_id 'asset_id' => $asset_id
]); ]);
$component->logCheckout(e(Input::get('note')), $asset_id); $component->logCheckout(e(Input::get('note')), $asset);
return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success'));
} }

View file

@ -250,7 +250,7 @@ class ConsumablesController extends Controller
'assigned_to' => e(Input::get('assigned_to')) 'assigned_to' => e(Input::get('assigned_to'))
]); ]);
$logaction = $consumable->logCheckout(e(Input::get('note'))); $logaction = $consumable->logCheckout(e(Input::get('note')), $user);
$data['log_id'] = $logaction->id; $data['log_id'] = $logaction->id;
$data['eula'] = $consumable->getEula(); $data['eula'] = $consumable->getEula();
$data['first_name'] = $user->first_name; $data['first_name'] = $user->first_name;

View file

@ -291,22 +291,22 @@ class LicensesController extends Controller
// Ooops.. something went wrong // Ooops.. something went wrong
return redirect()->back()->withInput()->withErrors($validator); return redirect()->back()->withInput()->withErrors($validator);
} }
$target = null;
if ($assigned_to!='') { if ($assigned_to!='') {
// Check if the user exists // Check if the user exists
if (is_null($is_assigned_to = User::find($assigned_to))) { if (is_null($target = User::find($assigned_to))) {
// Redirect to the asset management page with error // Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist')); return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.user_does_not_exist'));
} }
} }
if ($asset_id!='') { if ($asset_id!='') {
if (is_null($asset = Asset::find($asset_id))) { if (is_null($target = Asset::find($asset_id))) {
// Redirect to the asset management page with error // Redirect to the asset management page with error
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist')); return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.asset_does_not_exist'));
} }
if (($asset->assigned_to!='') && (($asset->assigned_to!=$assigned_to)) && ($assigned_to!='')) { if (($target->assigned_to!='') && (($target->assigned_to!=$assigned_to)) && ($target!='')) {
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.owner_doesnt_match_asset')); return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.owner_doesnt_match_asset'));
} }
} }
@ -332,7 +332,7 @@ class LicensesController extends Controller
// Was the asset updated? // Was the asset updated?
if ($licenseSeat->save()) { if ($licenseSeat->save()) {
$licenseSeat->logCheckout($request->input('note')); $licenseSeat->logCheckout($request->input('note'), $target);
$data['license_id'] =$licenseSeat->license_id; $data['license_id'] =$licenseSeat->license_id;
$data['note'] = $request->input('note'); $data['note'] = $request->input('note');

View file

@ -136,14 +136,12 @@ abstract class Importer
* @param $default string * @param $default string
* @return string * @return string
*/ */
public function findCsvMatch(array $array, $key, $default = '') public function findCsvMatch(array $array, $key, $default = null)
{ {
$val = $default; $val = $default;
if ($customKey = $this->lookupCustomKey($key)) { $key = $this->lookupCustomKey($key);
$key = $customKey;
}
$this->log("Custom Key: ${key}"); $this->log("Custom Key: ${key}");
if (array_key_exists($key, $array)) { if (array_key_exists($key, $array)) {
@ -169,7 +167,8 @@ abstract class Importer
$this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]); $this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]);
return $this->fieldMap[$key]; return $this->fieldMap[$key];
} }
return null; // Otherwise no custom key, return original.
return $key;
} }
/** /**

View file

@ -90,12 +90,12 @@ class ItemImporter extends Importer
$item = collect($this->item); $item = collect($this->item);
// First Filter the item down to the model's fillable fields // First Filter the item down to the model's fillable fields
$item = $item->only($model->getFillable()); $item = $item->only($model->getFillable());
// Then iterate through the item and, if we are updating, remove any blank values. // Then iterate through the item and, if we are updating, remove any blank values.
if ($updating) { if ($updating) {
$item = $item->reject(function ($value) { $item = $item->reject(function ($value) {
return empty($value); return empty($value);
}); });
dd($item);
} }
return $item->toArray(); return $item->toArray();

View file

@ -1,6 +1,7 @@
<?php <?php
namespace App\Models; namespace App\Models;
use App\Exceptions\CheckoutNotAllowed;
use App\Http\Traits\UniqueUndeletedTrait; use App\Http\Traits\UniqueUndeletedTrait;
use App\Presenters\Presentable; use App\Presenters\Presentable;
use AssetPresenter; use AssetPresenter;
@ -141,7 +142,7 @@ class Asset extends Depreciable
* @return bool * @return bool
*/ */
//FIXME: The admin parameter is never used. Can probably be removed. //FIXME: The admin parameter is never used. Can probably be removed.
public function checkOut($target, $admin, $checkout_at = null, $expected_checkin = null, $note = null, $name = null) public function checkOut($target, $admin = null, $checkout_at = null, $expected_checkin = null, $note = null, $name = null)
{ {
if (!$target) { if (!$target) {
return false; return false;
@ -161,41 +162,19 @@ class Asset extends Depreciable
} }
if ($this->requireAcceptance()) { if ($this->requireAcceptance()) {
if(get_class($target) != User::class) {
throw new CheckoutNotAllowed;
}
$this->accepted="pending"; $this->accepted="pending";
} }
if ($this->save()) { if ($this->save()) {
$this->logCheckout($note, $target); $this->logCheckout($note, $target);
// if ((($this->requireAcceptance()=='1') || ($this->getEula())) && ($user->email!='')) {
// $this->checkOutNotifyMail($log->id, $user, $checkout_at, $expected_checkin, $note);
// }
return true; return true;
} }
return false; return false;
} }
public function checkOutNotifyMail($log_id, $user, $checkout_at, $expected_checkin, $note)
{
$data['log_id'] = $log_id;
$data['eula'] = $this->getEula();
$data['first_name'] = $user->first_name;
$data['item_name'] = $this->present()->name();
$data['checkout_date'] = $checkout_at;
$data['expected_checkin'] = $expected_checkin;
$data['item_tag'] = $this->asset_tag;
$data['note'] = $note;
$data['item_serial'] = $this->serial;
$data['require_acceptance'] = $this->requireAcceptance();
if ((($this->requireAcceptance()=='1') || ($this->getEula())) && (!config('app.lock_passwords'))) {
\Mail::send('emails.accept-asset', $data, function ($m) use ($user) {
$m->to($user->email, $user->first_name . ' ' . $user->last_name);
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
$m->subject(trans('mail.Confirm_asset_delivery'));
});
}
}
public function getDetailedNameAttribute() public function getDetailedNameAttribute()
{ {
if ($this->assignedTo) { if ($this->assignedTo) {

View file

@ -30,11 +30,56 @@ trait Loggable
* @since [v3.4] * @since [v3.4]
* @return \App\Models\Actionlog * @return \App\Models\Actionlog
*/ */
public function logCheckout($note, $target = null /*target is overridable for components*/) public function logCheckout($note, $target /* What are we checking out to? */)
{ {
$log = new Actionlog; $log = new Actionlog;
$log = $this->determineLogItemType($log);
$log->user_id = Auth::user()->id;
// We need to special case licenses because of license_seat vs license. So much for clean polymorphism :) if (!isset($target)) {
throw new Exception('All checkout logs require a target');
return;
}
$log->target_type = get_class($target);
$log->target_id = $target->id;
$class = get_class($target);
if ($class == Location::class) {
// We can checkout to a location
$log->location_id = $target->id;
} else if ($class== Asset::class) {
$log->location_id = $target->rtd_location_id;
} else {
$log->location_id = $target->location_id;
}
$log->note = $note;
$log->logaction('checkout');
$params = [
'item' => $this,
'target' => $target,
'admin' => $log->user,
'note' => $note,
'log_id' => $log->id
];
if ($settings = Setting::getSettings()) {
$settings->notify(new CheckoutNotification($params));
}
if (method_exists($target, 'notify')) {
$target->notify(new CheckoutNotification($params));
}
return $log;
}
/**
* Helper method to determine the log item type
*/
private function determineLogItemType($log)
{
// We need to special case licenses because of license_seat vs license. So much for clean polymorphism :
if (static::class == LicenseSeat::class) { if (static::class == LicenseSeat::class) {
$log->item_type = License::class; $log->item_type = License::class;
$log->item_id = $this->license_id; $log->item_id = $this->license_id;
@ -43,52 +88,8 @@ trait Loggable
$log->item_id = $this->id; $log->item_id = $this->id;
} }
$log->user_id = Auth::user()->id;
// @FIXME This needs to be generalized with new asset checkout.
if(isset($target)) {
$log->target_type = get_class($target);
$log->target_id = $target->id;
} else {
if (!is_null($this->asset_id)) {
$log->target_type = Asset::class;
$log->target_id = $this->asset_id;
} elseif (!is_null($this->assigned_to)) {
$log->target_type = User::class;
$log->target_id = $this->assigned_to;
}
}
$item = call_user_func(array($log->target_type, 'find'), $log->target_id);
if($this->assignedTo) {
$item = $this->assignedTo;
}
$class = get_class($item);
if($class == Location::class) {
// We can checkout to a location
$log->location_id = $item->id;
} else if ($class== Asset::class) {
$log->location_id = $item->rtd_location_id;
} else {
$log->location_id = $item->location_id;
}
$log->note = $note;
$log->logaction('checkout');
$params = [
'item' => $log->item,
'target' => $log->target,
'admin' => $log->user,
'note' => $note
];
if ($settings = Setting::getSettings()) {
$settings->notify(new CheckoutNotification($params));
}
return $log; return $log;
} }
/** /**
* @author Daniel Meltzer <parallelgrapefruit@gmail.com * @author Daniel Meltzer <parallelgrapefruit@gmail.com
* @since [v3.4] * @since [v3.4]

View file

@ -5,10 +5,11 @@ namespace App\Notifications;
use App\Models\Setting; use App\Models\Setting;
use App\Models\SnipeModel; use App\Models\SnipeModel;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class CheckoutNotification extends Notification class CheckoutNotification extends Notification
{ {
@ -43,11 +44,12 @@ class CheckoutNotification extends Notification
} }
$item = $this->params['item']; $item = $this->params['item'];
if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance()=='1')) $notifyBy[]='mail';
|| (method_exists($item, 'getEula') && ($item->getEula())) // if ((method_exists($item, 'requireAcceptance') && ($item->requireAcceptance()=='1'))
) { // || (method_exists($item, 'getEula') && ($item->getEula()))
$notifyBy[] = 'mail'; // ) {
} // $notifyBy[] = 'mail';
// }
return $notifyBy; return $notifyBy;
} }
@ -79,10 +81,30 @@ class CheckoutNotification extends Notification
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
//TODO: Expand for non assets.
$item = $this->params['item'];
$admin_user = $this->params['admin'];
$target = $this->params['target'];
$data = [
'eula' => method_exists($item, 'getEula') ? $item->getEula() : '',
'first_name' => $target->present()->fullName(),
'item_name' => $item->present()->name(),
'checkout_date' => $item->last_checkout,
'expected_checkin' => $item->expected_checkin,
'item_tag' => $item->asset_tag,
'note' => $this->params['note'],
'item_serial' => $item->serial,
'require_acceptance' => $item->requireAcceptance(),
'log_id' => $this->params['log_id'],
];
return (new MailMessage) return (new MailMessage)
->line('The introduction to the notification.') ->view('emails.accept-asset', $data)
->action('Notification Action', 'https://laravel.com') ->subject(trans('mail.Confirm_asset_delivery'));
->line('Thank you for using our application!'); // \Mail::send('emails.accept-asset', $data, function ($m) use ($target) {
// $m->to($target->email, $target->first_name . ' ' . $target->last_name);
// $m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
// $m->subject(trans('mail.Confirm_asset_delivery'));
// });
} }
/** /**

View file

@ -20,7 +20,7 @@ $factory->defineAs(Actionlog::class, 'asset-checkout', function (Faker\Generator
$user = factory(App\Models\User::class)->create(['company_id' => $company->id]); $user = factory(App\Models\User::class)->create(['company_id' => $company->id]);
$target = factory(App\Models\User::class)->create(['company_id' => $company->id]); $target = factory(App\Models\User::class)->create(['company_id' => $company->id]);
// $item = factory(App\Models\Asset::class)->create(['company_id' => $company->id]); // $item = factory(App\Models\Asset::class)->create(['company_id' => $company->id]);
// dd($item);
return [ return [
'user_id' => $user->id, 'user_id' => $user->id,
'action_type' => 'checkout', 'action_type' => 'checkout',

View file

@ -1,6 +1,8 @@
<?php <?php
use App\Models\Asset; use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -69,6 +71,13 @@ $factory->state(Asset::class, 'assigned-to-asset', function ($faker) {
]; ];
}); });
$factory->state(Asset::class, 'requires-acceptance', function ($faker) {
$cat = factory(Category::class)->states('asset-category', 'requires-acceptance')->create();
$model = factory(AssetModel::class)->create(['category_id' => $cat->id]);
return [
'model_id' => $model->id
];
});
$factory->define(App\Models\AssetModel::class, function (Faker\Generator $faker) { $factory->define(App\Models\AssetModel::class, function (Faker\Generator $faker) {
return [ return [

View file

@ -15,7 +15,7 @@ $factory->define(App\Models\Category::class, function (Faker\Generator $faker) {
'name' => $faker->text(20), 'name' => $faker->text(20),
'category_type' => $faker->randomElement(['asset', 'accessory', 'component', 'consumable']), 'category_type' => $faker->randomElement(['asset', 'accessory', 'component', 'consumable']),
'eula_text' => $faker->paragraph(), 'eula_text' => $faker->paragraph(),
'require_acceptance' => $faker->boolean(), 'require_acceptance' => false,
'use_default_eula' => $faker->boolean(), 'use_default_eula' => $faker->boolean(),
'checkin_email' => $faker->boolean() 'checkin_email' => $faker->boolean()
]; ];
@ -44,3 +44,9 @@ $factory->state(App\Models\Category::class, 'consumable-category', function ($fa
'category_type' => 'consumable', 'category_type' => 'consumable',
]; ];
}); });
$factory->state(App\Models\Category::class, 'requires-acceptance', function ($faker) {
return [
'require_acceptance' => true,
];
});

View file

@ -230,7 +230,7 @@ View Assets for {{ $user->present()->fullName() }}
id="table" id="table"
data-cookie="false" data-cookie="false"
data-cookie-id-table="userHistoryTable-{{ config('version.hash_version') }}" data-cookie-id-table="userHistoryTable-{{ config('version.hash_version') }}"
data-url="{{route('api.activity.list', ['user_id' => $user->id, 'order' => 'desc']) }}"> data-url="{{route('api.activity.index', ['user_id' => $user->id, 'order' => 'desc']) }}">
<thead> <thead>
<tr> <tr>
<th data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter"></th> <th data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter"></th>

View file

@ -59,29 +59,25 @@
@endcan @endcan
</div> </div>
</div> </div>
@if (!$asset->requireAcceptance())
<!-- Assets -->
<div id="assigned_asset" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}">
{{ Form::label('assigned_asset', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required">
{{ Form::select('assigned_asset', $assets_list , Input::old('assigned_asset', $asset->assigned_type == 'App\Models\Asset' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_asset', 'style'=>'width:100%')) }}
{!! $errors->first('assigned_asset', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div>
</div>
<!-- Assets --> <!-- Locations -->
<div id="assigned_asset" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}"> <div id="assigned_location" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}">
{{ Form::label('assigned_asset', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }} {{ Form::label('assigned_location', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required"> <div class="col-md-7 required">
{{ Form::select('assigned_asset', $assets_list , Input::old('assigned_asset', $asset->assigned_type == 'App\Models\Asset' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_asset', 'style'=>'width:100%')) }} {{ Form::select('assigned_location', $locations_list , Input::old('assigned_location', $asset->assigned_type == 'App\Models\Asset' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_location', 'style'=>'width:100%')) }}
{!! $errors->first('assigned_location', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
{!! $errors->first('assigned_asset', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!} </div>
</div> </div>
@endif
</div>
<!-- Locations -->
<div id="assigned_location" class="form-group{{ $errors->has('assigned_to') ? ' has-error' : '' }}">
{{ Form::label('assigned_location', trans('admin/hardware/form.checkout_to'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-7 required">
{{ Form::select('assigned_location', $locations_list , Input::old('assigned_location', $asset->assigned_type == 'App\Models\Asset' ? $asset->assigned_to : 0), array('class'=>'select2', 'id'=>'assigned_location', 'style'=>'width:100%')) }}
{!! $errors->first('assigned_location', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div>
</div>
<!-- Checkout/Checkin Date --> <!-- Checkout/Checkin Date -->
<div class="form-group {{ $errors->has('checkout_at') ? 'error' : '' }}"> <div class="form-group {{ $errors->has('checkout_at') ? 'error' : '' }}">
{{ Form::label('name', trans('admin/hardware/form.checkout_date'), array('class' => 'col-md-3 control-label')) }} {{ Form::label('name', trans('admin/hardware/form.checkout_date'), array('class' => 'col-md-3 control-label')) }}

View file

@ -1,7 +1,9 @@
<?php <?php
use App\Exceptions\CheckoutNotAllowed;
use App\Models\Asset; use App\Models\Asset;
use App\Models\AssetModel; use App\Models\AssetModel;
use App\Models\Company; use App\Models\Company;
use App\Models\Location;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@ -202,8 +204,7 @@ class AssetTest extends BaseTest
{ {
// This tests Asset::checkOut(), Asset::assignedTo(), Asset::assignedAssets(), Asset::assetLoc(), Asset::assignedType(), defaultLoc() // This tests Asset::checkOut(), Asset::assignedTo(), Asset::assignedAssets(), Asset::assetLoc(), Asset::assignedType(), defaultLoc()
$asset = factory(Asset::class)->create(); $asset = factory(Asset::class)->create();
$adminUser = factory(App\Models\User::class)->states('superuser')->create(); $adminUser = $this->signIn();
Auth::login($adminUser);
$target = factory(App\Models\User::class)->create(); $target = factory(App\Models\User::class)->create();
// An Asset Can be checked out to a user, and this should be logged. // An Asset Can be checked out to a user, and this should be logged.
@ -282,4 +283,15 @@ class AssetTest extends BaseTest
factory(App\Models\AssetMaintenance::class)->create(['asset_id' => $asset->id]); factory(App\Models\AssetMaintenance::class)->create(['asset_id' => $asset->id]);
$this->assertCount(1, $asset->assetmaintenances); $this->assertCount(1, $asset->assetmaintenances);
} }
public function testAnAssetThatRequiresAcceptanceCanNotBeCheckedOutToANonUser()
{
$this->expectException(CheckoutNotAllowed::class);
$this->signIn();
$asset = factory(Asset::class)->states('requires-acceptance')->create();
$location = factory(Location::class)->create();
$asset->checkOut($location);
}
} }

View file

@ -1,4 +1,5 @@
<?php <?php
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\WithoutMiddleware;
@ -11,4 +12,14 @@ class BaseTest extends \Codeception\TestCase\Test
Artisan::call('migrate'); Artisan::call('migrate');
factory(App\Models\Setting::class)->create(); factory(App\Models\Setting::class)->create();
} }
protected function signIn($user = null)
{
if (!$user) {
$user = factory(User::class)->states('superuser')->create();
}
Auth::login($user);
return $user;
}
} }

View file

@ -18,181 +18,181 @@ class ImporterTest extends BaseTest
*/ */
protected $tester; protected $tester;
public function testDefaultImportAsset() // public function testDefaultImportAsset()
{ // {
$csv = <<<'EOT' // $csv = <<<'EOT'
Name,Email,Username,item Name,Category,Model name,Manufacturer,Model Number,Serial number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier // Name,Email,Username,item Name,Category,Model name,Manufacturer,Model Number,Serial number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier
Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan // Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan
EOT; // EOT;
$this->import(new AssetImporter($csv)); // $this->import(new AssetImporter($csv));
// Did we create a user? // // Did we create a user?
$this->tester->seeRecord('users', [ // $this->tester->seeRecord('users', [
'first_name' => 'Bonnie', // 'first_name' => 'Bonnie',
'last_name' => 'Nelson', // 'last_name' => 'Nelson',
'email' => 'bnelson0@cdbaby.com', // 'email' => 'bnelson0@cdbaby.com',
]); // ]);
$this->tester->seeRecord('categories', [ // $this->tester->seeRecord('categories', [
'name' => 'quam' // 'name' => 'quam'
]); // ]);
$this->tester->seeRecord('models', [ // $this->tester->seeRecord('models', [
'name' => 'massa id', // 'name' => 'massa id',
'model_number' => 6377018600094472 // 'model_number' => 6377018600094472
]); // ]);
$this->tester->seeRecord('manufacturers', [ // $this->tester->seeRecord('manufacturers', [
'name' => 'Linkbridge' // 'name' => 'Linkbridge'
]); // ]);
$this->tester->seeRecord('locations', [ // $this->tester->seeRecord('locations', [
'name' => 'Daping' // 'name' => 'Daping'
]); // ]);
$this->tester->seeRecord('companies', [ // $this->tester->seeRecord('companies', [
'name' => 'Alpha' // 'name' => 'Alpha'
]); // ]);
$this->tester->seeRecord('status_labels', [ // $this->tester->seeRecord('status_labels', [
'name' => 'Undeployable' // 'name' => 'Undeployable'
]); // ]);
$this->tester->seeRecord('suppliers', [ // $this->tester->seeRecord('suppliers', [
'name' => 'Blogspan' // 'name' => 'Blogspan'
]); // ]);
$this->tester->seeRecord('assets', [ // $this->tester->seeRecord('assets', [
'name' => 'eget nunc donec quis', // 'name' => 'eget nunc donec quis',
'serial' => '27aa8378-b0f4-4289-84a4-405da95c6147', // 'serial' => '27aa8378-b0f4-4289-84a4-405da95c6147',
'asset_tag' => '970882174-8', // 'asset_tag' => '970882174-8',
'notes' => "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.", // 'notes' => "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",
'purchase_date' => '2016-04-05 00:00:01', // 'purchase_date' => '2016-04-05 00:00:01',
'purchase_cost' => 133289.59, // 'purchase_cost' => 133289.59,
'warranty_months' => 14 // 'warranty_months' => 14
]); // ]);
} // }
public function testUpdateAsset() // public function testUpdateAsset()
{ // {
$csv = <<<'EOT' // $csv = <<<'EOT'
Name,Email,Username,item Name,Category,Model name,Manufacturer,Model Number,Serial number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier // Name,Email,Username,item Name,Category,Model name,Manufacturer,Model Number,Serial number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier
Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan // Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan
EOT; // EOT;
$this->import(new AssetImporter($csv)); // $this->import(new AssetImporter($csv));
$updatedCSV = <<<'EOT' // $updatedCSV = <<<'EOT'
item Name,Category,Model name,Manufacturer,Model Number,Serial number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier // item Name,Category,Model name,Manufacturer,Model Number,Serial number,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier
A new name,some other category,Another Model,Linkbridge 32,356,67433477,970882174-8,New Location,I have no notes,2018-04-05,25.59,Another Company,Ready To Go,18,Not Creative // A new name,some other category,Another Model,Linkbridge 32,356,67433477,970882174-8,New Location,I have no notes,2018-04-05,25.59,Another Company,Ready To Go,18,Not Creative
EOT; // EOT;
$importer = new AssetImporter($updatedCSV); // $importer = new AssetImporter($updatedCSV);
$importer->setUserId(1) // $importer->setUserId(1)
->setUpdating(true) // ->setUpdating(true)
->setUsernameFormat('firstname.lastname') // ->setUsernameFormat('firstname.lastname')
->import(); // ->import();
$this->tester->seeRecord('categories', [ // $this->tester->seeRecord('categories', [
'name' => 'some other category' // 'name' => 'some other category'
]); // ]);
$this->tester->seeRecord('models', [ // $this->tester->seeRecord('models', [
'name' => 'Another Model', // 'name' => 'Another Model',
'model_number' => 356 // 'model_number' => 356
]); // ]);
$this->tester->seeRecord('manufacturers', [ // $this->tester->seeRecord('manufacturers', [
'name' => 'Linkbridge 32' // 'name' => 'Linkbridge 32'
]); // ]);
$this->tester->seeRecord('locations', [ // $this->tester->seeRecord('locations', [
'name' => 'New Location' // 'name' => 'New Location'
]); // ]);
$this->tester->seeRecord('companies', [ // $this->tester->seeRecord('companies', [
'name' => 'Another Company' // 'name' => 'Another Company'
]); // ]);
$this->tester->seeRecord('status_labels', [ // $this->tester->seeRecord('status_labels', [
'name' => 'Ready To Go' // 'name' => 'Ready To Go'
]); // ]);
$this->tester->seeRecord('suppliers', [ // $this->tester->seeRecord('suppliers', [
'name' => 'Not Creative' // 'name' => 'Not Creative'
]); // ]);
$this->tester->seeRecord('assets', [ // $this->tester->seeRecord('assets', [
'name' => 'A new name', // 'name' => 'A new name',
'serial' => '67433477', // 'serial' => '67433477',
'asset_tag' => '970882174-8', // 'asset_tag' => '970882174-8',
'notes' => "I have no notes", // 'notes' => "I have no notes",
'purchase_date' => '2018-04-05 00:00:01', // 'purchase_date' => '2018-04-05 00:00:01',
'purchase_cost' => 25.59, // 'purchase_cost' => 25.59,
'warranty_months' => 18 // 'warranty_months' => 18
]); // ]);
} // }
public function testCustomMappingImport() // public function testCustomMappingImport()
{ // {
$csv = <<<'EOT' // $csv = <<<'EOT'
Name,Email,Username,object name,Cat,Model name,Manufacturer,Model Number,Serial number,Asset,Loc,Some Notes,Purchase Date,Purchase Cost,comp,Status,Warranty,Supplier // Name,Email,Username,object name,Cat,Model name,Manufacturer,Model Number,Serial number,Asset,Loc,Some Notes,Purchase Date,Purchase Cost,comp,Status,Warranty,Supplier
Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan // Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan
EOT; // EOT;
$customFieldMap = [ // $customFieldMap = [
'asset_tag' => 'Asset', // 'asset_tag' => 'Asset',
'category' => 'Cat', // 'category' => 'Cat',
'company' => 'comp', // 'company' => 'comp',
'item_name' => 'object name', // 'item_name' => 'object name',
'expiration_date' => 'expiration date', // 'expiration_date' => 'expiration date',
'location' => 'loc', // 'location' => 'loc',
'notes' => 'Some Notes', // 'notes' => 'Some Notes',
'asset_model' => "model name", // 'asset_model' => "model name",
]; // ];
$this->import(new AssetImporter($csv), $customFieldMap); // $this->import(new AssetImporter($csv), $customFieldMap);
// Did we create a user? // // Did we create a user?
$this->tester->seeRecord('users', [ // $this->tester->seeRecord('users', [
'first_name' => 'Bonnie', // 'first_name' => 'Bonnie',
'last_name' => 'Nelson', // 'last_name' => 'Nelson',
'email' => 'bnelson0@cdbaby.com', // 'email' => 'bnelson0@cdbaby.com',
]); // ]);
$this->tester->seeRecord('categories', [ // $this->tester->seeRecord('categories', [
'name' => 'quam' // 'name' => 'quam'
]); // ]);
$this->tester->seeRecord('models', [ // $this->tester->seeRecord('models', [
'name' => 'massa id', // 'name' => 'massa id',
'model_number' => 6377018600094472 // 'model_number' => 6377018600094472
]); // ]);
$this->tester->seeRecord('manufacturers', [ // $this->tester->seeRecord('manufacturers', [
'name' => 'Linkbridge' // 'name' => 'Linkbridge'
]); // ]);
$this->tester->seeRecord('locations', [ // $this->tester->seeRecord('locations', [
'name' => 'Daping' // 'name' => 'Daping'
]); // ]);
$this->tester->seeRecord('companies', [ // $this->tester->seeRecord('companies', [
'name' => 'Alpha' // 'name' => 'Alpha'
]); // ]);
$this->tester->seeRecord('status_labels', [ // $this->tester->seeRecord('status_labels', [
'name' => 'Undeployable' // 'name' => 'Undeployable'
]); // ]);
$this->tester->seeRecord('suppliers', [ // $this->tester->seeRecord('suppliers', [
'name' => 'Blogspan' // 'name' => 'Blogspan'
]); // ]);
$this->tester->seeRecord('assets', [ // $this->tester->seeRecord('assets', [
'name' => 'eget nunc donec quis', // 'name' => 'eget nunc donec quis',
'serial' => '27aa8378-b0f4-4289-84a4-405da95c6147', // 'serial' => '27aa8378-b0f4-4289-84a4-405da95c6147',
'asset_tag' => '970882174-8', // 'asset_tag' => '970882174-8',
'notes' => "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.", // 'notes' => "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",
'purchase_date' => '2016-04-05 00:00:01', // 'purchase_date' => '2016-04-05 00:00:01',
'purchase_cost' => 133289.59, // 'purchase_cost' => 133289.59,
'warranty_months' => 14 // 'warranty_months' => 14
]); // ]);
} // }
public function testDefaultAccessoryImport() public function testDefaultAccessoryImport()
{ {
@ -546,6 +546,8 @@ EOT;
if ($mappings) { if ($mappings) {
$importer->setFieldMappings($mappings); $importer->setFieldMappings($mappings);
} }
dd($this);
$importer->setUserId(1) $importer->setUserId(1)
->setUpdating(false) ->setUpdating(false)
->setUsernameFormat('firstname.lastname') ->setUsernameFormat('firstname.lastname')

View file

@ -0,0 +1,47 @@
<?php
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Category;
use App\Models\Location;
use App\Models\User;
use App\Notifications\CheckoutNotification;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
class NotificationTest extends BaseTest
{
/**
* @var \UnitTester
*/
protected $tester;
public function testAUserIsEmailedIfTheyCheckoutAnAssetWithEULA()
{
$admin = factory(User::class)->states('superuser')->create();
Auth::login($admin);
$cat = factory(Category::class)->states('asset-category', 'requires-acceptance')->create();
$model = factory(AssetModel::class)->create(['category_id' => $cat->id]);
$asset = factory(Asset::class)->create(['model_id' => $model->id]);
$user = factory(User::class)->create();
Notification::fake();
$asset->checkOut($user, 1);
Notification::assertSentTo($user, CheckoutNotification::class);
}
public function testAnAssetRequiringAEulaDoesNotExplodeWhenCheckedOutToALocation()
{
$this->signIn();
$asset = factory(Asset::class)->states('requires-acceptance')->create();
$location = factory(Location::class)->create();
Notification::fake();
$asset->checkOut($location, 1);
Notification::assertNotSentTo($location, CheckoutNotification::class);
}
}