From 92a2a5ccbc8c5380a64ef48ffef6ca520bac90d6 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 13:54:30 +0200 Subject: [PATCH 01/24] Adds listeners for checking/checkout events --- .../SendingCheckInNotificationsListener.php | 18 ++++++++++++++++++ .../SendingCheckOutNotificationsListener.php | 17 +++++++++++++++++ app/Providers/EventServiceProvider.php | 11 +++++++++++ 3 files changed, 46 insertions(+) create mode 100644 app/Listeners/SendingCheckInNotificationsListener.php create mode 100644 app/Listeners/SendingCheckOutNotificationsListener.php diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php new file mode 100644 index 0000000000..cb5a32d67f --- /dev/null +++ b/app/Listeners/SendingCheckInNotificationsListener.php @@ -0,0 +1,18 @@ + Date: Sat, 21 Jul 2018 14:00:17 +0200 Subject: [PATCH 02/24] Adds checkin events --- app/Events/AccessoryCheckedIn.php | 33 +++++++++++++++++ app/Events/AssetCheckedIn.php | 33 +++++++++++++++++ app/Events/ComponentCheckedIn.php | 35 +++++++++++++++++++ app/Events/LicenseCheckedIn.php | 33 +++++++++++++++++ .../Assets/AssetCheckinController.php | 3 ++ 5 files changed, 137 insertions(+) create mode 100644 app/Events/AccessoryCheckedIn.php create mode 100644 app/Events/AssetCheckedIn.php create mode 100644 app/Events/ComponentCheckedIn.php create mode 100644 app/Events/LicenseCheckedIn.php diff --git a/app/Events/AccessoryCheckedIn.php b/app/Events/AccessoryCheckedIn.php new file mode 100644 index 0000000000..812e072b28 --- /dev/null +++ b/app/Events/AccessoryCheckedIn.php @@ -0,0 +1,33 @@ +accessory = $accessory; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Events/AssetCheckedIn.php b/app/Events/AssetCheckedIn.php new file mode 100644 index 0000000000..455b2b5d4e --- /dev/null +++ b/app/Events/AssetCheckedIn.php @@ -0,0 +1,33 @@ +asset = $asset; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Events/ComponentCheckedIn.php b/app/Events/ComponentCheckedIn.php new file mode 100644 index 0000000000..283ea6e358 --- /dev/null +++ b/app/Events/ComponentCheckedIn.php @@ -0,0 +1,35 @@ +component = $component; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->quantity = $quantity; + $this->note = $note; + } +} diff --git a/app/Events/LicenseCheckedIn.php b/app/Events/LicenseCheckedIn.php new file mode 100644 index 0000000000..36562296b7 --- /dev/null +++ b/app/Events/LicenseCheckedIn.php @@ -0,0 +1,33 @@ +license = $license; + $this->checkedOutTo = $checkedOutTo; + $this->checkedInBy = $checkedInBy; + $this->note = $note; + } +} diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index 7eed360a6a..26f77e7fcd 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Assets; +use App\Events\AssetCheckedIn; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetCheckinRequest; @@ -85,6 +86,8 @@ class AssetCheckinController extends Controller $asset->logCheckin($target, e(request('note'))); + event(new AssetCheckedIn($asset, $target, Auth::user(), $request->input('note'))); + if ($backto=='user') { return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success')); } From ea64abc6072161be72ecefd9ca3042ae89c96c1f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:01:30 +0200 Subject: [PATCH 03/24] Adds checkout events --- app/Events/AccessoryCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/AssetCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/ComponentCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/ConsumableCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Events/LicenseCheckedOut.php | 31 +++++++++++++++++++++++++++++ app/Models/Asset.php | 6 +++++- 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 app/Events/AccessoryCheckedOut.php create mode 100644 app/Events/AssetCheckedOut.php create mode 100644 app/Events/ComponentCheckedOut.php create mode 100644 app/Events/ConsumableCheckedOut.php create mode 100644 app/Events/LicenseCheckedOut.php diff --git a/app/Events/AccessoryCheckedOut.php b/app/Events/AccessoryCheckedOut.php new file mode 100644 index 0000000000..397500238d --- /dev/null +++ b/app/Events/AccessoryCheckedOut.php @@ -0,0 +1,31 @@ +accessory = $accessory; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/AssetCheckedOut.php b/app/Events/AssetCheckedOut.php new file mode 100644 index 0000000000..6d2fdea3a0 --- /dev/null +++ b/app/Events/AssetCheckedOut.php @@ -0,0 +1,31 @@ +asset = $asset; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/ComponentCheckedOut.php b/app/Events/ComponentCheckedOut.php new file mode 100644 index 0000000000..81826a4d2f --- /dev/null +++ b/app/Events/ComponentCheckedOut.php @@ -0,0 +1,31 @@ +component = $component; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/ConsumableCheckedOut.php b/app/Events/ConsumableCheckedOut.php new file mode 100644 index 0000000000..df97561c38 --- /dev/null +++ b/app/Events/ConsumableCheckedOut.php @@ -0,0 +1,31 @@ +consumable = $consumable; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Events/LicenseCheckedOut.php b/app/Events/LicenseCheckedOut.php new file mode 100644 index 0000000000..5d1b8335ec --- /dev/null +++ b/app/Events/LicenseCheckedOut.php @@ -0,0 +1,31 @@ +license = $license; + $this->checkedOutTo = $checkedOutTo; + $this->logEntry = $logEntry; + } +} diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 4805602f34..288cb04068 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -1,6 +1,7 @@ save()) { - $this->logCheckout($note, $target); + $loggedAction = $this->logCheckout($note, $target); + + event(new AssetCheckedOut($this, $target, $loggedAction)); + $this->increment('checkout_counter', 1); return true; } From 4a71542f233b655ec3283559a3a3206554aba85f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:02:56 +0200 Subject: [PATCH 04/24] Cleanup checkin notification constructors --- .../CheckinAccessoryNotification.php | 25 ++++++------------- .../CheckinAssetNotification.php | 23 ++++++++--------- .../CheckinLicenseNotification.php | 19 ++++++-------- 3 files changed, 24 insertions(+), 43 deletions(-) diff --git a/app/Notifications/CheckinAccessoryNotification.php b/app/Notifications/CheckinAccessoryNotification.php index 052279892e..ee3bcac6ab 100644 --- a/app/Notifications/CheckinAccessoryNotification.php +++ b/app/Notifications/CheckinAccessoryNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Accessory; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -15,31 +16,19 @@ use Illuminate\Support\Facades\Mail; class CheckinAccessoryNotification extends Notification { use Queueable; - /** - * @var - */ - private $params; /** * Create a new notification instance. * * @param $params */ - public function __construct($params) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedInby, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->note = ''; - $this->target_type = $params['target']; - $this->settings = $params['settings']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } - - - + $this->item = $accessory; + $this->target = $checkedOutTo; + $this->admin = $checkedInby; + $this->note = $note; + $this->settings = Setting::getSettings(); } /** diff --git a/app/Notifications/CheckinAssetNotification.php b/app/Notifications/CheckinAssetNotification.php index 78f3f00664..4053f22ffc 100644 --- a/app/Notifications/CheckinAssetNotification.php +++ b/app/Notifications/CheckinAssetNotification.php @@ -2,13 +2,14 @@ namespace App\Notifications; +use App\Models\Asset; use App\Models\Setting; -use Illuminate\Bus\Queueable; use App\Models\User; -use Illuminate\Notifications\Notification; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; +use Illuminate\Notifications\Notification; class CheckinAssetNotification extends Notification { @@ -20,19 +21,15 @@ class CheckinAssetNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(Asset $asset, $checkedOutTo, User $checkedInBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->note = ''; - $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; + $this->target = $checkedOutTo; + $this->item = $asset; + $this->admin = $checkedInBy; + $this->note = $note; - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } + $this->settings = Setting::getSettings(); + $this->expected_checkin = ''; if ($this->item->expected_checkin) { $this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date', diff --git a/app/Notifications/CheckinLicenseNotification.php b/app/Notifications/CheckinLicenseNotification.php index f8a52ccf3c..3ff06d2ca3 100644 --- a/app/Notifications/CheckinLicenseNotification.php +++ b/app/Notifications/CheckinLicenseNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\License; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -25,19 +26,13 @@ class CheckinLicenseNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(License $license, $checkedOutTo, User $checkedInBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->note = ''; - $this->settings = $params['settings']; - $this->target_type = $params['target_type']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } - + $this->target = $checkedOutTo; + $this->item = $license; + $this->admin = $checkedInBy; + $this->note = $note; + $this->settings = Setting::getSettings(); } /** From ef76908fce31bd0e06fb8479da0d3b23f439e41c Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:03:26 +0200 Subject: [PATCH 05/24] Listen for checkin events and send the appropriate notifications --- .../SendingCheckInNotificationsListener.php | 103 +++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php index cb5a32d67f..8a84c42fad 100644 --- a/app/Listeners/SendingCheckInNotificationsListener.php +++ b/app/Listeners/SendingCheckInNotificationsListener.php @@ -2,9 +2,95 @@ namespace App\Listeners; +use App\Models\Recipients\AdminRecipient; +use App\Models\Setting; +use App\Models\User; +use App\Notifications\CheckinAccessoryNotification; +use App\Notifications\CheckinAssetNotification; +use App\Notifications\CheckinLicenseNotification; +use App\Notifications\CheckoutAccessoryNotification; +use App\Notifications\CheckoutAssetNotification; +use App\Notifications\CheckoutConsumableNotification; +use App\Notifications\CheckoutLicenseNotification; +use Illuminate\Support\Facades\Notification; + class SendingCheckInNotificationsListener { - + /** + * Notify the user about the checked in accessory + */ + public function onAccessoryCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckinAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedInBy, $event->note) + ); + } + + /** + * Notify the user about the checked in asset + */ + public function onAssetCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckinAssetNotification($event->asset, $event->checkedOutTo, $event->checkedInBy, $event->note) + ); + } + + /** + * Notify the user about the checked in license + */ + public function onLicenseCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckinLicenseNotification($event->license, $event->checkedOutTo, $event->checkedInBy, $event->note) + ); + } + + /** + * Gets the entities to be notified of the passed event + * + * @param Event $event + * @return Collection + */ + private function getNotifiables($event) { + $notifiables = collect(); + + /** + * Notify the user who checked out the item + */ + $notifiables->push($event->checkedOutTo); + + /** + * Notify Admin users if the settings is activated + */ + if (Setting::getSettings()->admin_cc_email != '') { + $notifiables->push(new AdminRecipient()); + } + + return $notifiables; + } + /** * Register the listeners for the subscriber. * @@ -12,7 +98,20 @@ class SendingCheckInNotificationsListener */ public function subscribe($events) { - + $events->listen( + 'App\Events\AccessoryCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onAccessoryCheckedIn' + ); + + $events->listen( + 'App\Events\AssetCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onAssetCheckedIn' + ); + + $events->listen( + 'App\Events\LicenseCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onLicenseCheckedIn' + ); } } \ No newline at end of file From 112a532618d28cd172f80e16e221d1f7c7b7821f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:03:44 +0200 Subject: [PATCH 06/24] Listen for checkout events and send appropriate notifications --- .../SendingCheckOutNotificationsListener.php | 90 ++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php index f9d61eef02..195962c498 100644 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ b/app/Listeners/SendingCheckOutNotificationsListener.php @@ -2,8 +2,78 @@ namespace App\Listeners; +use App\Models\Recipients\AdminRecipient; +use App\Models\Setting; +use App\Models\User; +use App\Notifications\CheckoutAccessoryNotification; +use App\Notifications\CheckoutAssetNotification; +use App\Notifications\CheckoutConsumableNotification; +use App\Notifications\CheckoutLicenseNotification; + class SendingCheckOutNotificationsListener { + /** + * Handle user login events. + */ + public function onConsumableCheckedOut($event) { + /** + * Notify the user about the checked out consumable + */ + $this->sendNotification(CheckoutConsumableNotification::class, $event->logEntry); + } + + public function onAccessoryCheckedOut($event) { + /** + * Notify the user about the checked out accessory + */ + $this->sendNotification(CheckoutAccessoryNotification::class, $event->logEntry); + } + + public function onLicenseCheckedOut($event) { + /** + * Notify the user about the checked out license + */ + $this->sendNotification(CheckoutLicenseNotification::class, $event->logEntry); + } + + public function onAssetCheckedOut($event) { + /** + * Notify the user about the checked out asset + */ + $this->sendNotification(CheckoutAssetNotification::class, $event->logEntry); + } + + private function sendNotification($notificationClass, $logEntry) { + /** + * When the item isn't checked out to a user, we can't send notifications + */ + if(! $logEntry->target instanceof User) { + return; + } + + $params = [ + 'log_id' => $logEntry->id, + 'item' => $logEntry->item, + 'target_type' => $logEntry->target_type, + 'admin' => $logEntry->user, + + 'target' => $logEntry->target, + 'note' => $logEntry->note, + 'settings' => Setting::getSettings(), + ]; + + $logEntry->target->notify(new $notificationClass($params)); + + /** + * Notify Admin users if the settings is activated + */ + if (Setting::getSettings()->admin_cc_email != '') { + $recipient = new AdminRecipient(); + + $recipient->notify(new $notificationClass($params)); + } + } + /** * Register the listeners for the subscriber. * @@ -11,7 +81,25 @@ class SendingCheckOutNotificationsListener */ public function subscribe($events) { - + $events->listen( + 'App\Events\ConsumableCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onConsumableCheckedOut' + ); + + $events->listen( + 'App\Events\AccessoryCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onAccessoryCheckedOut' + ); + + $events->listen( + 'App\Events\LicenseCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseCheckedOut' + ); + + $events->listen( + 'App\Events\AssetCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onAssetCheckedOut' + ); } } \ No newline at end of file From 722f032895010e608299a9c4b59d75707c5da06d Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:06:51 +0200 Subject: [PATCH 07/24] Remove notification sending from loggable trait --- app/Models/Loggable.php | 55 ----------------------------------------- 1 file changed, 55 deletions(-) diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index 61b1d6deb7..f3914d9db5 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -6,14 +6,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\CheckoutRequest; use App\Models\User; -use App\Notifications\CheckinAssetNotification; use App\Notifications\AuditNotification; -use App\Notifications\CheckoutAssetNotification; -use App\Notifications\CheckoutAccessoryNotification; -use App\Notifications\CheckinAccessoryNotification; -use App\Notifications\CheckoutConsumableNotification; -use App\Notifications\CheckoutLicenseNotification; -use App\Notifications\CheckinLicenseNotification; use Illuminate\Support\Facades\Auth; @@ -38,7 +31,6 @@ trait Loggable */ public function logCheckout($note, $target /* What are we checking out to? */) { - $settings = Setting::getSettings(); $log = new Actionlog; $log = $this->determineLogItemType($log); $log->user_id = Auth::user()->id; @@ -63,29 +55,6 @@ trait Loggable $log->note = $note; $log->logaction('checkout'); - $params = [ - 'item' => $log->item, - 'target_type' => $log->target_type, - 'target' => $target, - 'admin' => $log->user, - 'note' => $note, - 'log_id' => $log->id, - 'settings' => $settings, - ]; - - $checkoutClass = null; - - if (method_exists($target, 'notify')) { - $target->notify(new static::$checkoutClass($params)); - } - - // Send to the admin, if settings dictate - $recipient = new \App\Models\Recipients\AdminRecipient(); - - if (($settings->admin_cc_email!='') && (static::$checkoutClass!='')) { - $recipient->notify(new static::$checkoutClass($params)); - } - return $log; } @@ -112,7 +81,6 @@ trait Loggable */ public function logCheckin($target, $note) { - $settings = Setting::getSettings(); $log = new Actionlog; $log->target_type = get_class($target); $log->target_id = $target->id; @@ -140,29 +108,6 @@ trait Loggable $log->user_id = Auth::user()->id; $log->logaction('checkin from'); - $params = [ - 'target' => $target, - 'item' => $log->item, - 'admin' => $log->user, - 'note' => $note, - 'target_type' => $log->target_type, - 'settings' => $settings, - ]; - - - $checkinClass = null; - - if (method_exists($target, 'notify')) { - $target->notify(new static::$checkinClass($params)); - } - - // Send to the admin, if settings dictate - $recipient = new \App\Models\Recipients\AdminRecipient(); - - if (($settings->admin_cc_email!='') && (static::$checkinClass!='')) { - $recipient->notify(new static::$checkinClass($params)); - } - return $log; } From 775e46288e6df0db7e092dbc4697582f096e65e8 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 21 Jul 2018 14:07:06 +0200 Subject: [PATCH 08/24] Cleanup of model attributes --- app/Models/Accessory.php | 7 ------- app/Models/Asset.php | 5 ----- app/Models/Component.php | 7 ------- app/Models/Consumable.php | 6 ------ app/Models/License.php | 6 ------ app/Models/LicenseSeat.php | 6 ------ 6 files changed, 37 deletions(-) diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index 8fa27515e9..cdba5f3206 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -47,13 +47,6 @@ class Accessory extends SnipeModel 'supplier' => ['name'], 'location' => ['name'] ]; - - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutAccessoryNotification::class; - public static $checkinClass = CheckinAccessoryNotification::class; - /** * Accessory validation rules diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 288cb04068..051596ad2e 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -34,11 +34,6 @@ class Asset extends Depreciable const USER = 'user'; const ACCEPTANCE_PENDING = 'pending'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutAssetNotification::class; - public static $checkinClass = CheckinAssetNotification::class; /** diff --git a/app/Models/Component.php b/app/Models/Component.php index 8ccc3c3070..e880234ba7 100644 --- a/app/Models/Component.php +++ b/app/Models/Component.php @@ -20,13 +20,6 @@ class Component extends SnipeModel protected $dates = ['deleted_at', 'purchase_date']; protected $table = 'components'; - - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = null; - public static $checkinClass = null; - /** * Category validation rules diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index b1d3fe222c..1b3a0da156 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -20,12 +20,6 @@ class Consumable extends SnipeModel 'requestable' => 'boolean' ]; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutConsumableNotification::class; - public static $checkinClass = null; - /** * Category validation rules diff --git a/app/Models/License.php b/app/Models/License.php index a3ac4c5985..c953bf9dd5 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -18,12 +18,6 @@ class License extends Depreciable { protected $presenter = 'App\Presenters\LicensePresenter'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutLicenseNotification::class; - public static $checkinClass = CheckinLicenseNotification::class; - use SoftDeletes; use CompanyableTrait; diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index 500391041d..c2abc20ea9 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -17,12 +17,6 @@ class LicenseSeat extends Model implements ICompanyableChild protected $guarded = 'id'; protected $table = 'license_seats'; - /** - * Set static properties to determine which checkout/checkin handlers we should use - */ - public static $checkoutClass = CheckoutLicenseNotification::class; - public static $checkinClass = CheckinLicenseNotification::class; - public function getCompanyableParents() { return ['asset', 'license']; From 17fc59f98911e997597196a8b3afe6e8ef00354e Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Wed, 25 Jul 2018 10:02:06 +0200 Subject: [PATCH 09/24] Adds back the checkin/checkout events after #5916 --- .../Controllers/Accessories/AccessoryCheckinController.php | 5 ++++- .../Accessories/AccessoryCheckoutController.php | 5 ++++- .../Controllers/Components/ComponentCheckinController.php | 3 +++ .../Controllers/Components/ComponentCheckoutController.php | 6 +++++- .../Consumables/ConsumableCheckoutController.php | 7 +++++-- app/Http/Controllers/Licenses/LicenseCheckinController.php | 6 +++++- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php index 83d59ab774..7acfe855cd 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Accessories; +use App\Events\AccessoryCheckedIn; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -46,7 +47,7 @@ class AccessoryCheckinController extends Controller * @throws \Illuminate\Auth\Access\AuthorizationException * @internal param int $accessoryId */ - public function store($accessoryUserId = null, $backto = null) + public function store(Request $request, $accessoryUserId = null, $backto = null) { // Check if the accessory exists if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) { @@ -63,6 +64,8 @@ class AccessoryCheckinController extends Controller $return_to = e($accessory_user->assigned_to); $accessory->logCheckin(User::find($return_to), e(Input::get('note'))); + event(new AccessoryCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); + return redirect()->route("accessories.show", $accessory->id)->with('success', trans('admin/accessories/message.checkin.success')); } // Redirect to the accessory management page with error diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 50c174f0c1..830dbef366 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Accessories; +use App\Events\AccessoryCheckedOut; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -77,10 +78,12 @@ class AccessoryCheckoutController extends Controller 'assigned_to' => $request->get('assigned_to') ]); - $accessory->logCheckout(e(Input::get('note')), $user); + $logaction = $accessory->logCheckout(e(Input::get('note')), $user); DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); + event(new AccessoryCheckedOut($accessory, $user, $logaction)); + // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); } diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index 89eab1f5fa..cd070a5067 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\ComponentCheckedIn; use App\Http\Controllers\Controller; use App\Models\Actionlog; use App\Models\Asset; @@ -102,6 +103,8 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } + event(new ComponentCheckedIn($component, $component_assets, $request->input('checkin_qty'), $request->input('note'))); + return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index 8922317faf..e2c6adad4f 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\ComponentCheckedOut; use App\Http\Controllers\Controller; use App\Models\Asset; use App\Models\Component; @@ -86,7 +87,10 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - $component->logCheckout(e(Input::get('note')), $asset); + $logaction = $component->logCheckout(e(Input::get('note')), $asset); + + event(new ComponentCheckedOut($component, $asset, $logaction)); + return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index 6de35ff9e1..b1d75634f9 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -2,10 +2,11 @@ namespace App\Http\Controllers\Consumables; +use App\Events\ConsumableCheckedOut; +use App\Http\Controllers\Controller; use App\Models\Consumable; use App\Models\User; use Illuminate\Http\Request; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; @@ -67,7 +68,9 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - $consumable->logCheckout(e(Input::get('note')), $user); + $logaction = $consumable->logCheckout(e(Input::get('note')), $user); + + event(new ConsumableCheckedOut($consumable, $user, $logaction)); // Redirect to the new consumable page return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index f942aa1e7e..6144c35062 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Licenses; +use App\Events\LicenseCheckedIn; use App\Models\Asset; use App\Models\License; use App\Models\LicenseSeat; @@ -49,7 +50,7 @@ class LicenseCheckinController extends Controller * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function store($seatId = null, $backTo = null) + public function store(Request $request, $seatId = null, $backTo = null) { // Check if the asset exists if (is_null($licenseSeat = LicenseSeat::find($seatId))) { @@ -89,6 +90,9 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { $licenseSeat->logCheckin($return_to, e(request('note'))); + + event(new LicenseCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); + if ($backTo=='user') { return redirect()->route("users.show", $return_to->id)->with('success', trans('admin/licenses/message.checkin.success')); } From e24f292a1a90abe670348dc06d1ae1f86a8be0fa Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:05:45 +0200 Subject: [PATCH 10/24] Updates checkout events to not depend on log --- app/Events/AccessoryCheckedOut.php | 5 +++-- app/Events/AssetCheckedOut.php | 5 +++-- app/Events/ComponentCheckedOut.php | 8 ++++---- app/Events/ConsumableCheckedOut.php | 5 +++-- app/Events/LicenseCheckedOut.php | 5 +++-- .../Accessories/AccessoryCheckoutController.php | 2 +- .../Components/ComponentCheckoutController.php | 2 +- .../Consumables/ConsumableCheckoutController.php | 4 ++-- .../Controllers/Licenses/LicenseCheckoutController.php | 7 +++++++ 9 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/Events/AccessoryCheckedOut.php b/app/Events/AccessoryCheckedOut.php index 397500238d..faf30adb36 100644 --- a/app/Events/AccessoryCheckedOut.php +++ b/app/Events/AccessoryCheckedOut.php @@ -22,10 +22,11 @@ class AccessoryCheckedOut * * @return void */ - public function __construct(Accessory $accessory, $checkedOutTo, Actionlog $logEntry) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $note) { $this->accessory = $accessory; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/AssetCheckedOut.php b/app/Events/AssetCheckedOut.php index 6d2fdea3a0..1cd0669ad8 100644 --- a/app/Events/AssetCheckedOut.php +++ b/app/Events/AssetCheckedOut.php @@ -22,10 +22,11 @@ class AssetCheckedOut * * @return void */ - public function __construct(Asset $asset, $checkedOutTo, Actionlog $logEntry) + public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note) { $this->asset = $asset; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/ComponentCheckedOut.php b/app/Events/ComponentCheckedOut.php index 81826a4d2f..f74e7d70f4 100644 --- a/app/Events/ComponentCheckedOut.php +++ b/app/Events/ComponentCheckedOut.php @@ -2,7 +2,6 @@ namespace App\Events; -use App\Models\Actionlog; use App\Models\Component; use App\Models\User; use Illuminate\Broadcasting\Channel; @@ -15,17 +14,18 @@ class ComponentCheckedOut public $component; public $checkedOutTo; - public $logEntry; + public $checkedOutBy; /** * Create a new event instance. * * @return void */ - public function __construct(Component $component, $checkedOutTo, Actionlog $logEntry) + public function __construct(Component $component, $checkedOutTo, $quantity, User $checkedOutBy) { $this->component = $component; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->quantity = $quantity; + $this->checkedOutBy = $checkedOutBy; } } diff --git a/app/Events/ConsumableCheckedOut.php b/app/Events/ConsumableCheckedOut.php index df97561c38..ca002cc2f8 100644 --- a/app/Events/ConsumableCheckedOut.php +++ b/app/Events/ConsumableCheckedOut.php @@ -22,10 +22,11 @@ class ConsumableCheckedOut * * @return void */ - public function __construct(Consumable $consumable, $checkedOutTo, Actionlog $logEntry) + public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $note) { $this->consumable = $consumable; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/LicenseCheckedOut.php b/app/Events/LicenseCheckedOut.php index 5d1b8335ec..a04b20b7bb 100644 --- a/app/Events/LicenseCheckedOut.php +++ b/app/Events/LicenseCheckedOut.php @@ -22,10 +22,11 @@ class LicenseCheckedOut * * @return void */ - public function __construct(License $license, $checkedOutTo, Actionlog $logEntry) + public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) { $this->license = $license; $this->checkedOutTo = $checkedOutTo; - $this->logEntry = $logEntry; + $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 830dbef366..30e5fd25d8 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -82,7 +82,7 @@ class AccessoryCheckoutController extends Controller DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); - event(new AccessoryCheckedOut($accessory, $user, $logaction)); + event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note')); // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index e2c6adad4f..c979f86186 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -89,7 +89,7 @@ class ComponentCheckoutController extends Controller $logaction = $component->logCheckout(e(Input::get('note')), $asset); - event(new ComponentCheckedOut($component, $asset, $logaction)); + event(new ComponentCheckedOut($component, $asset, $request->input('assigned_qty'), Auth::user())); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index b1d75634f9..ef2d30beb3 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -42,7 +42,7 @@ class ConsumableCheckoutController extends Controller * @return \Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function store($consumableId) + public function store(Request $request, $consumableId) { if (is_null($consumable = Consumable::find($consumableId))) { return redirect()->route('consumables.index')->with('error', trans('admin/consumables/message.not_found')); @@ -70,7 +70,7 @@ class ConsumableCheckoutController extends Controller $logaction = $consumable->logCheckout(e(Input::get('note')), $user); - event(new ConsumableCheckedOut($consumable, $user, $logaction)); + event(new ConsumableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); // Redirect to the new consumable page return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index 570cd6b410..acde2d6205 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Licenses; +use App\Events\LicenseCheckedOut; use App\Http\Requests\LicenseCheckoutRequest; use App\Models\Asset; use App\Models\License; @@ -104,6 +105,9 @@ class LicenseCheckoutController extends Controller } if ($licenseSeat->save()) { $licenseSeat->logCheckout(request('note'), $target); + + event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + return true; } return false; @@ -119,6 +123,9 @@ class LicenseCheckoutController extends Controller if ($licenseSeat->save()) { $licenseSeat->logCheckout(request('note'), $target); + + event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + return true; } return false; From e0423418d2ca391018e74db1c303dddda6ab9df3 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:23:58 +0200 Subject: [PATCH 11/24] Moves logging checkin/checkout to separate listener --- .../AccessoryCheckinController.php | 2 +- .../AccessoryCheckoutController.php | 2 - .../Assets/AssetCheckinController.php | 5 +- .../Components/ComponentCheckinController.php | 11 +-- .../ComponentCheckoutController.php | 2 - .../ConsumableCheckoutController.php | 2 - .../Licenses/LicenseCheckinController.php | 2 +- .../Licenses/LicenseCheckoutController.php | 2 - app/Listeners/LogListener.php | 92 +++++++++++++++++++ app/Models/Asset.php | 3 +- app/Providers/EventServiceProvider.php | 2 + 11 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 app/Listeners/LogListener.php diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php index 7acfe855cd..e64d24f169 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -7,6 +7,7 @@ use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Input; @@ -62,7 +63,6 @@ class AccessoryCheckinController extends Controller // Was the accessory updated? if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) { $return_to = e($accessory_user->assigned_to); - $accessory->logCheckin(User::find($return_to), e(Input::get('note'))); event(new AccessoryCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 30e5fd25d8..02107bcd2f 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -78,8 +78,6 @@ class AccessoryCheckoutController extends Controller 'assigned_to' => $request->get('assigned_to') ]); - $logaction = $accessory->logCheckout(e(Input::get('note')), $user); - DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note')); diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index 26f77e7fcd..da141f8b3d 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -7,6 +7,7 @@ use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetCheckinRequest; use App\Models\Asset; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Redirect; use Illuminate\Support\Facades\View; @@ -83,9 +84,7 @@ class AssetCheckinController extends Controller // Was the asset updated? if ($asset->save()) { - $asset->logCheckin($target, e(request('note'))); - - + event(new AssetCheckedIn($asset, $target, Auth::user(), $request->input('note'))); if ($backto=='user') { diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index cd070a5067..24c684c4b6 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -8,6 +8,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\Component; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; @@ -87,16 +88,6 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', $component_asset_id)->update(['assigned_qty' => $qty_remaining_in_checkout]); - $log = new Actionlog(); - $log->user_id = auth()->id(); - $log->action_type = 'checkin from'; - $log->target_type = Asset::class; - $log->target_id = $component_assets->asset_id; - $log->item_id = $component_assets->component_id; - $log->item_type = Component::class; - $log->note = $request->input('note'); - $log->save(); - // If the checked-in qty is exactly the same as the assigned_qty, // we can simply delete the associated components_assets record if ($qty_remaining_in_checkout == 0) { diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index c979f86186..c7e7fe3db5 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -87,8 +87,6 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - $logaction = $component->logCheckout(e(Input::get('note')), $asset); - event(new ComponentCheckedOut($component, $asset, $request->input('assigned_qty'), Auth::user())); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index ef2d30beb3..0b3d8c6c91 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -68,8 +68,6 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - $logaction = $consumable->logCheckout(e(Input::get('note')), $user); - event(new ConsumableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); // Redirect to the new consumable page diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 6144c35062..9612028465 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -9,6 +9,7 @@ use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Http\Request; use App\Http\Controllers\Controller; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Validator; @@ -89,7 +90,6 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { - $licenseSeat->logCheckin($return_to, e(request('note'))); event(new LicenseCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index acde2d6205..1dd8935f68 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -104,7 +104,6 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = $target->assigned_to; } if ($licenseSeat->save()) { - $licenseSeat->logCheckout(request('note'), $target); event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); @@ -122,7 +121,6 @@ class LicenseCheckoutController extends Controller $licenseSeat->assigned_to = request('assigned_to'); if ($licenseSeat->save()) { - $licenseSeat->logCheckout(request('note'), $target); event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php new file mode 100644 index 0000000000..c3dfe76220 --- /dev/null +++ b/app/Listeners/LogListener.php @@ -0,0 +1,92 @@ +accessory->logCheckin($event->checkedOutTo, $event->note); + } + + public function onAccessoryCheckedOut(AccessoryCheckedOut $event) { + $event->accessory->logCheckout($event->note, $event->checkedOutTo); + } + + public function onAssetCheckedIn(AssetCheckedIn $event) { + $event->asset->logCheckin($event->checkedOutTo, $event->note); + } + + public function onAssetCheckedOut(AssetCheckedOut $event) { + $event->asset->logCheckout($event->note, $event->checkedOutTo); + } + + public function onComponentCheckedIn(ComponentCheckedIn $event) { + $log = new Actionlog(); + $log->user_id = $event->checkedInBy->id; + $log->action_type = 'checkin from'; + $log->target_type = Asset::class; + $log->target_id = $event->checkedOutTo->asset_id; + $log->item_id = $event->checkedOutTo->component_id; + $log->item_type = Component::class; + $log->note = $event->note; + $log->save(); + } + + public function onComponentCheckedOut(ComponentCheckedOut $event) { + $event->component->logCheckout($event->note, $event->checkedOutTo); + } + + public function onConsumableCheckedOut(ConsumableCheckedOut $event) { + $event->consumable->logCheckout($event->note, $event->checkedOutTo); + } + + public function onLicenseCheckedIn(LicenseCheckedIn $event) { + $event->license->logCheckin($event->checkedOutTo, $event->note); + } + + public function onLicenseCheckedOut(LicenseCheckedOut $event) { + $event->license->logCheckout($event->note, $event->checkedOutTo); + } + + /** + * Register the listeners for the subscriber. + * + * @param Illuminate\Events\Dispatcher $events + */ + public function subscribe($events) + { + $list = [ + 'AccessoryCheckedIn', + 'AccessoryCheckedout', + 'AssetCheckedIn', + 'AssetCheckedOut', + 'ComponentCheckedIn', + 'ComponentCheckedOut', + 'ConsumableCheckedOut', + 'LicenseCheckedIn', + 'LicenseCheckedOut', + ]; + + foreach($list as $event) { + $events->listen( + 'App\Events\\' . $event, + 'App\Listeners\LogListener@on' . $event + ); + } + } + +} \ No newline at end of file diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 051596ad2e..68d2cf8453 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -262,9 +262,8 @@ class Asset extends Depreciable } if ($this->save()) { - $loggedAction = $this->logCheckout($note, $target); - event(new AssetCheckedOut($this, $target, $loggedAction)); + event(new AssetCheckedOut($this, $target, Auth::user(), $note)); $this->increment('checkout_counter', 1); return true; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 822f82c8cf..df3c643b24 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\Facades\Event; +use App\Listeners\LogListener; use App\Listeners\SendingCheckInNotificationsListener; use App\Listeners\SendingCheckOutNotificationsListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -33,6 +34,7 @@ class EventServiceProvider extends ServiceProvider protected $subscribe = [ SendingCheckOutNotificationsListener::class, SendingCheckInNotificationsListener::class, + LogListener::class ]; /** From 72b43b6526650bb97c8529de2e74444a3218cb8e Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:27:19 +0200 Subject: [PATCH 12/24] Updates checkout notifications to use new routes for accepting --- .../SendingCheckOutNotificationsListener.php | 119 +++++++++++------- .../CheckoutAccessoryNotification.php | 30 ++--- .../CheckoutAssetNotification.php | 29 ++--- .../CheckoutConsumableNotification.php | 24 ++-- .../CheckoutLicenseNotification.php | 24 ++-- 5 files changed, 112 insertions(+), 114 deletions(-) diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php index 195962c498..1968c13d1a 100644 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ b/app/Listeners/SendingCheckOutNotificationsListener.php @@ -9,69 +9,100 @@ use App\Notifications\CheckoutAccessoryNotification; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseNotification; +use Illuminate\Support\Facades\Notification; class SendingCheckOutNotificationsListener { /** - * Handle user login events. + * Notify the user about the checked out consumable */ public function onConsumableCheckedOut($event) { /** - * Notify the user about the checked out consumable + * When the item wasn't checked out to a user, we can't send notifications */ - $this->sendNotification(CheckoutConsumableNotification::class, $event->logEntry); - } - - public function onAccessoryCheckedOut($event) { - /** - * Notify the user about the checked out accessory - */ - $this->sendNotification(CheckoutAccessoryNotification::class, $event->logEntry); - } - - public function onLicenseCheckedOut($event) { - /** - * Notify the user about the checked out license - */ - $this->sendNotification(CheckoutLicenseNotification::class, $event->logEntry); - } - - public function onAssetCheckedOut($event) { - /** - * Notify the user about the checked out asset - */ - $this->sendNotification(CheckoutAssetNotification::class, $event->logEntry); - } - - private function sendNotification($notificationClass, $logEntry) { - /** - * When the item isn't checked out to a user, we can't send notifications - */ - if(! $logEntry->target instanceof User) { + if(! $event->checkedOutTo instanceof User) { return; } - $params = [ - 'log_id' => $logEntry->id, - 'item' => $logEntry->item, - 'target_type' => $logEntry->target_type, - 'admin' => $logEntry->user, + Notification::send( + $this->getNotifiables($event), + new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } + + /** + * Notify the user about the checked out accessory + */ + public function onAccessoryCheckedOut($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } - 'target' => $logEntry->target, - 'note' => $logEntry->note, - 'settings' => Setting::getSettings(), - ]; + Notification::send( + $this->getNotifiables($event), + new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } - $logEntry->target->notify(new $notificationClass($params)); + /** + * Notify the user about the checked out license + */ + public function onLicenseCheckedOut($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckoutLicenseNotification($event->license, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } + + /** + * Notify the user about the checked out asset + */ + public function onAssetCheckedOut($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(! $event->checkedOutTo instanceof User) { + return; + } + + Notification::send( + $this->getNotifiables($event), + new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $event->note) + ); + } + + /** + * Gets the entities to be notified of the passed event + * + * @param Event $event + * @return Collection + */ + private function getNotifiables($event) { + $notifiables = collect(); + + /** + * Notify the user who checked out the item + */ + $notifiables->push($event->checkedOutTo); /** * Notify Admin users if the settings is activated */ if (Setting::getSettings()->admin_cc_email != '') { - $recipient = new AdminRecipient(); + $notifiables->push(new AdminRecipient()); + } - $recipient->notify(new $notificationClass($params)); - } + return $notifiables; } /** diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 4e8950619d..3b29a21984 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Accessory; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -15,33 +16,20 @@ use Illuminate\Support\Facades\Mail; class CheckoutAccessoryNotification extends Notification { use Queueable; - /** - * @var - */ - private $params; /** * Create a new notification instance. - * - * @param $params */ - public function __construct($params) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; - $this->last_checkout = ''; - $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } + $this->item = $accessory; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->settings = Setting::getSettings(); } @@ -140,7 +128,7 @@ class CheckoutAccessoryNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['accessory', $this->item->id]), ]) ->subject(trans('mail.Confirm_accessory_delivery')); diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index 8451d6dcd9..f0323342f0 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Asset; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; @@ -13,31 +14,24 @@ use Illuminate\Contracts\Queue\ShouldQueue; class CheckoutAssetNotification extends Notification { use Queueable; - /** - * @var - */ - private $params; /** * Create a new notification instance. * * @param $params */ - public function __construct($params) + public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; + + $this->item = $asset; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + + $this->settings = Setting::getSettings(); + $this->last_checkout = ''; $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } if ($this->item->last_checkout) { $this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date', @@ -151,12 +145,11 @@ class CheckoutAssetNotification extends Notification 'item' => $this->item, 'admin' => $this->admin, 'note' => $this->note, - 'log_id' => $this->note, 'target' => $this->target, 'fields' => $fields, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['asset', $this->item->id]), 'last_checkout' => $this->last_checkout, 'expected_checkin' => $this->expected_checkin, ]) diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index d9a80bfc3c..7c6dfb7b3f 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\Consumable; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -25,21 +26,15 @@ class CheckoutConsumableNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; - $this->last_checkout = ''; - $this->expected_checkin = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } + $this->item = $consumable; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + + $this->settings = Setting::getSettings(); } @@ -131,11 +126,10 @@ class CheckoutConsumableNotification extends Notification 'item' => $this->item, 'admin' => $this->admin, 'note' => $this->note, - 'log_id' => $this->note, 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['consumable', $this->item->id]), ]) ->subject(trans('mail.Confirm_consumable_delivery')); diff --git a/app/Notifications/CheckoutLicenseNotification.php b/app/Notifications/CheckoutLicenseNotification.php index 93530e2cde..e5c697b2a0 100644 --- a/app/Notifications/CheckoutLicenseNotification.php +++ b/app/Notifications/CheckoutLicenseNotification.php @@ -2,6 +2,7 @@ namespace App\Notifications; +use App\Models\License; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -25,23 +26,14 @@ class CheckoutLicenseNotification extends Notification * * @param $params */ - public function __construct($params) + public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) { - $this->target = $params['target']; - $this->item = $params['item']; - $this->admin = $params['admin']; - $this->log_id = $params['log_id']; - $this->note = ''; - $this->target_type = $params['target_type']; - $this->settings = $params['settings']; - $this->target_type = $params['target_type']; - - if (array_key_exists('note', $params)) { - $this->note = $params['note']; - } - - + $this->item = $license; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->settings = Setting::getSettings(); } /** @@ -133,7 +125,7 @@ class CheckoutLicenseNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => url('/').'/account/accept-asset/'.$this->log_id, + 'accept_url' => route('account.accept.item', ['license', $this->item->id]), ]) ->subject(trans('mail.Confirm_license_delivery')); From 39e6b59335f58ca4ea60dd64582e9e724b858429 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 00:41:31 +0200 Subject: [PATCH 13/24] Fixes some typos --- .../Controllers/Accessories/AccessoryCheckoutController.php | 2 +- .../Controllers/Components/ComponentCheckinController.php | 2 +- app/Listeners/LogListener.php | 5 +++-- app/Notifications/CheckoutAccessoryNotification.php | 5 ++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 02107bcd2f..13bc9e8fd2 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -80,7 +80,7 @@ class AccessoryCheckoutController extends Controller DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); - event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note')); + event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note'))); // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index 24c684c4b6..fd91daa841 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -94,7 +94,7 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } - event(new ComponentCheckedIn($component, $component_assets, $request->input('checkin_qty'), $request->input('note'))); + event(new ComponentCheckedIn($component, $component_assets, Auth::user(), $request->input('checkin_qty'), $request->input('note'))); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index c3dfe76220..2b35e63658 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -47,7 +47,8 @@ class LogListener } public function onComponentCheckedOut(ComponentCheckedOut $event) { - $event->component->logCheckout($event->note, $event->checkedOutTo); + // Since components don't have a "note" field, submit empty note + $event->component->logCheckout(null, $event->checkedOutTo); } public function onConsumableCheckedOut(ConsumableCheckedOut $event) { @@ -71,7 +72,7 @@ class LogListener { $list = [ 'AccessoryCheckedIn', - 'AccessoryCheckedout', + 'AccessoryCheckedOut', 'AssetCheckedIn', 'AssetCheckedOut', 'ComponentCheckedIn', diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index 3b29a21984..c00da8d633 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -20,15 +20,14 @@ class CheckoutAccessoryNotification extends Notification /** * Create a new notification instance. */ - public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $note) { $this->item = $accessory; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; - $this->acceptance = $acceptance; - + $this->settings = Setting::getSettings(); } From 830a6cf67e9638931153dcc3d16d43583c2801a4 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 01:15:32 +0200 Subject: [PATCH 14/24] Adds accepting/declining to new controller --- app/Events/ItemAccepted.php | 32 +++++ app/Events/ItemDeclined.php | 32 +++++ .../Account/AcceptanceController.php | 119 +++++++++++++++ app/Listeners/LogListener.php | 24 ++++ resources/views/account/accept.blade.php | 135 ++++++++++++++++++ routes/web.php | 9 ++ 6 files changed, 351 insertions(+) create mode 100644 app/Events/ItemAccepted.php create mode 100644 app/Events/ItemDeclined.php create mode 100644 app/Http/Controllers/Account/AcceptanceController.php create mode 100644 resources/views/account/accept.blade.php diff --git a/app/Events/ItemAccepted.php b/app/Events/ItemAccepted.php new file mode 100644 index 0000000000..3c854d9ea5 --- /dev/null +++ b/app/Events/ItemAccepted.php @@ -0,0 +1,32 @@ +item = $item; + $this->acceptedBy = $acceptedBy; + $this->signature = $signature; + } +} diff --git a/app/Events/ItemDeclined.php b/app/Events/ItemDeclined.php new file mode 100644 index 0000000000..a735c193ef --- /dev/null +++ b/app/Events/ItemDeclined.php @@ -0,0 +1,32 @@ +item = $item; + $this->declinedBy = $declinedBy; + $this->signature = $signature; + } +} diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php new file mode 100644 index 0000000000..ecf9924277 --- /dev/null +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -0,0 +1,119 @@ +getItemById($type, $id); + + if (is_null($item)) { + return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); + } + + if ($item->isAccepted()) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } + + if (! $item->isCheckedOutTo(Auth::user())) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); + } + + if (!Company::isCurrentUserHasAccess($item)) { + return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); + } + + return view('account/accept', compact('item')); + } + + public function update(Request $request, $type, $id) { + $item = $this->getItemById($type, $id); + + if (is_null($item)) { + return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); + } + + if ($item->isAccepted()) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } + + if (! $item->isCheckedOutTo(Auth::user())) { + return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); + } + + if (!Company::isCurrentUserHasAccess($item)) { + return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); + } + + if (!$request->filled('asset_acceptance')) { + return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); + } + + /** + * Get the signature and save it + */ + if ($request->filled('signature_output')) { + $path = config('app.private_uploads').'/signatures'; + $sig_filename = "siglog-" .Str::uuid() . '-'.date('Y-m-d-his').".png"; + $data_uri = e($request->input('signature_output')); + $encoded_image = explode(",", $data_uri); + $decoded_image = base64_decode($encoded_image[1]); + file_put_contents($path."/".$sig_filename, $decoded_image); + } + + + if ($request->input('asset_acceptance') == 'accepted') { + + $item->accept(Auth::user(), $sig_filename); + + event(new ItemAccepted($item, Auth::user(), $sig_filename)); + + $return_msg = trans('admin/users/message.accepted'); + + } else { + + $item->decline(Auth::user(), $sig_filename); + + event(new ItemDeclined($item, Auth::user(), $sig_filename)); + + $return_msg = trans('admin/users/message.declined'); + + } + + return redirect()->to('account/accept')->with('success', $return_msg); + } + + private function getItemById($type, $id) : ? Acceptable { + switch ($type) { + case 'asset': + $item = Asset::findOrFail($id); + break; + case 'consumable': + $item = Consumable::findOrFail($id); + break; + case 'license': + $item = License::findOrFail($id); + break; + default: + $item = null; + break; + } + + return $item; + } +} \ No newline at end of file diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 2b35e63658..858c9ec291 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -9,6 +9,8 @@ use App\Events\AssetCheckedOut; use App\Events\ComponentCheckedIn; use App\Events\ComponentCheckedOut; use App\Events\ConsumableCheckedOut; +use App\Events\ItemAccepted; +use App\Events\ItemDeclined; use App\Events\LicenseCheckedIn; use App\Events\LicenseCheckedOut; use App\Models\Actionlog; @@ -63,6 +65,26 @@ class LogListener $event->license->logCheckout($event->note, $event->checkedOutTo); } + public function onItemAccepted(ItemAccepted $event) { + $logaction = new Actionlog(); + $logaction->item()->associate($event->item); + $logaction->target()->associate($event->acceptedBy); + $logaction->accept_signature = $event->signature; + $logaction->action_type = 'accepted'; + + $logaction->save(); + } + + public function onItemDeclined(ItemDeclined $event) { + $logaction = new Actionlog(); + $logaction->item()->associate($event->item); + $logaction->target()->associate($event->declinedBy); + $logaction->accept_signature = $event->signature; + $logaction->action_type = 'declined'; + + $logaction->save(); + } + /** * Register the listeners for the subscriber. * @@ -80,6 +102,8 @@ class LogListener 'ConsumableCheckedOut', 'LicenseCheckedIn', 'LicenseCheckedOut', + 'ItemAccepted', + 'ItemDeclined', ]; foreach($list as $event) { diff --git a/resources/views/account/accept.blade.php b/resources/views/account/accept.blade.php new file mode 100644 index 0000000000..9073f8516a --- /dev/null +++ b/resources/views/account/accept.blade.php @@ -0,0 +1,135 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') + Accept {{ $item->present()->name() }} + @parent +@stop + + +{{-- Page content --}} +@section('content') + + + + + + + +
+ + + + +
+
+
+
+
+
+ +
+ +
+ +
+ + @if ($item->getEula()) +
+
+ {!! $item->getEula() !!} +
+
+ @endif + + @if ($snipeSettings->require_accept_signature=='1') +
+ +

Sign below to indicate that you agree to the terms of service:

+ +
+
+ + +
+
+ +
+
+
+ @endif + +
+ +
+ +
+
+
+
+ +@stop + +@section('moar_scripts') + + + +@stop diff --git a/routes/web.php b/routes/web.php index 20974f5f80..4b6d13a7cd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -274,6 +274,15 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { # Account Dashboard Route::get('/', [ 'as' => 'account', 'uses' => 'ViewAssetsController@getIndex' ]); + + Route::get('accept', 'Account\AcceptanceController@index') + ->name('account.accept'); + + Route::get('accept/{type}/{id}', 'Account\AcceptanceController@edit') + ->name('account.accept.item'); + + Route::post('accept/{type}/{id}', 'Account\AcceptanceController@update'); + }); From 43437aac14a07c40dbeeeb6076186b827a4746c8 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 01:16:42 +0200 Subject: [PATCH 15/24] Adds acceptable contract to asset --- app/Models/Asset.php | 51 ++++++++++++++++++++++++++++- app/Models/Contracts/Acceptable.php | 11 +++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 app/Models/Contracts/Acceptable.php diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 68d2cf8453..0898e51902 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -5,7 +5,9 @@ use App\Events\AssetCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Http\Traits\UniqueSerialTrait; use App\Http\Traits\UniqueUndeletedTrait; +use App\Models\Contracts\Acceptable as AcceptableContract; use App\Models\Traits\Searchable; +use App\Models\User; use App\Presenters\Presentable; use AssetPresenter; use Auth; @@ -24,7 +26,7 @@ use App\Notifications\CheckoutAssetNotification; * * @version v1.0 */ -class Asset extends Depreciable +class Asset extends Depreciable implements AcceptableContract { protected $presenter = 'App\Presenters\AssetPresenter'; use Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait, UniqueSerialTrait; @@ -35,6 +37,53 @@ class Asset extends Depreciable const ACCEPTANCE_PENDING = 'pending'; + /** + * Accept the asset + * + * @param User $acceptedBy The user who accepts the asset + * @param string $signature The filename of the signature, if provided + */ + public function accept(User $acceptedBy, $signature = null) { + $this->accepted = 'accepted'; + $this->save(); + } + + /** + * Decline the asset + * + * @param User $declinedBy The user who declines the asset + * @param string $signature The filename of the signature, if provided + */ + public function decline(User $declinedBy, $signature = null) { + $this->assigned_to = null; + $this->assigned_type = null; + $this->accepted = null; + $this->save(); + } + + /** + * Is the asset already accepted? + * + * @return boolean + */ + public function isAccepted() : bool { + return $this->accepted != 'pending'; + } + + /** + * Is the asset checked out to this user? + * + * @param User $user + * @return boolean + */ + public function isCheckedOutTo(User $user) { + if (is_null($this->assignedTo)) { + return false; + } + + return $this->assignedTo->is($user); + } + /** * The database table used by the model. diff --git a/app/Models/Contracts/Acceptable.php b/app/Models/Contracts/Acceptable.php new file mode 100644 index 0000000000..de253607ad --- /dev/null +++ b/app/Models/Contracts/Acceptable.php @@ -0,0 +1,11 @@ + Date: Sat, 28 Jul 2018 12:43:09 +0200 Subject: [PATCH 16/24] Moves license checkout stuff to the license seat Since we are really checking out a license seat instead of the whole license, we operate the checkin/checkout on the license seat instance. --- ...eCheckedIn.php => LicenseSeatCheckedIn.php} | 7 ++++--- ...heckedOut.php => LicenseSeatCheckedOut.php} | 9 +++++---- .../Licenses/LicenseCheckoutController.php | 5 +++-- .../SendingCheckInNotificationsListener.php | 8 ++++---- app/Models/LicenseSeat.php | 14 +++++++++++--- ....php => CheckinLicenseSeatNotification.php} | 7 ++++--- ...php => CheckoutLicenseSeatNotification.php} | 12 ++++++++---- app/Presenters/LicenseSeatPresenter.php | 18 ++++++++++++++++++ 8 files changed, 57 insertions(+), 23 deletions(-) rename app/Events/{LicenseCheckedIn.php => LicenseSeatCheckedIn.php} (74%) rename app/Events/{LicenseCheckedOut.php => LicenseSeatCheckedOut.php} (70%) rename app/Notifications/{CheckinLicenseNotification.php => CheckinLicenseSeatNotification.php} (93%) rename app/Notifications/{CheckoutLicenseNotification.php => CheckoutLicenseSeatNotification.php} (90%) create mode 100644 app/Presenters/LicenseSeatPresenter.php diff --git a/app/Events/LicenseCheckedIn.php b/app/Events/LicenseSeatCheckedIn.php similarity index 74% rename from app/Events/LicenseCheckedIn.php rename to app/Events/LicenseSeatCheckedIn.php index 36562296b7..80e7ecbaf4 100644 --- a/app/Events/LicenseCheckedIn.php +++ b/app/Events/LicenseSeatCheckedIn.php @@ -4,6 +4,7 @@ namespace App\Events; use App\Models\Actionlog; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; @@ -13,7 +14,7 @@ class LicenseCheckedIn { use Dispatchable, SerializesModels; - public $license; + public $licenseSeat; public $checkedOutTo; public $checkedInBy; public $note; @@ -23,9 +24,9 @@ class LicenseCheckedIn * * @return void */ - public function __construct(License $license, $checkedOutTo, User $checkedInBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedInBy, $note) { - $this->license = $license; + $this->licenseSeat = $licenseSeat; $this->checkedOutTo = $checkedOutTo; $this->checkedInBy = $checkedInBy; $this->note = $note; diff --git a/app/Events/LicenseCheckedOut.php b/app/Events/LicenseSeatCheckedOut.php similarity index 70% rename from app/Events/LicenseCheckedOut.php rename to app/Events/LicenseSeatCheckedOut.php index a04b20b7bb..6b057be43e 100644 --- a/app/Events/LicenseCheckedOut.php +++ b/app/Events/LicenseSeatCheckedOut.php @@ -4,16 +4,17 @@ namespace App\Events; use App\Models\Actionlog; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class LicenseCheckedOut +class LicenseSeatCheckedOut { use Dispatchable, SerializesModels; - public $license; + public $licenseSeat; public $checkedOutTo; public $logEntry; @@ -22,9 +23,9 @@ class LicenseCheckedOut * * @return void */ - public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $note) { - $this->license = $license; + $this->licenseSeat = $licenseSeat; $this->checkedOutTo = $checkedOutTo; $this->checkedOutBy = $checkedOutBy; $this->note = $note; diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index 1dd8935f68..a1a02c823b 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Licenses; use App\Events\LicenseCheckedOut; +use App\Events\LicenseSeatCheckedOut; use App\Http\Requests\LicenseCheckoutRequest; use App\Models\Asset; use App\Models\License; @@ -105,7 +106,7 @@ class LicenseCheckoutController extends Controller } if ($licenseSeat->save()) { - event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } @@ -122,7 +123,7 @@ class LicenseCheckoutController extends Controller if ($licenseSeat->save()) { - event(new LicenseCheckedOut($licenseSeat->license, $target, Auth::user(), request('note'))); + event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php index 8a84c42fad..aa176e19fd 100644 --- a/app/Listeners/SendingCheckInNotificationsListener.php +++ b/app/Listeners/SendingCheckInNotificationsListener.php @@ -53,7 +53,7 @@ class SendingCheckInNotificationsListener /** * Notify the user about the checked in license */ - public function onLicenseCheckedIn($event) { + public function onLicenseSeatCheckedIn($event) { /** * When the item wasn't checked out to a user, we can't send notifications */ @@ -63,7 +63,7 @@ class SendingCheckInNotificationsListener Notification::send( $this->getNotifiables($event), - new CheckinLicenseNotification($event->license, $event->checkedOutTo, $event->checkedInBy, $event->note) + new CheckinLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedInBy, $event->note) ); } @@ -109,8 +109,8 @@ class SendingCheckInNotificationsListener ); $events->listen( - 'App\Events\LicenseCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onLicenseCheckedIn' + 'App\Events\LicenseSeatCheckedIn', + 'App\Listeners\SendingCheckInNotificationsListener@onLicenseSeatCheckedIn' ); } diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index c2abc20ea9..5e387930ec 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -2,17 +2,21 @@ namespace App\Models; use App\Models\Loggable; +use App\Notifications\CheckinLicenseNotification; +use App\Notifications\CheckoutLicenseNotification; +use App\Presenters\Presentable; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use App\Notifications\CheckoutLicenseNotification; -use App\Notifications\CheckinLicenseNotification; -class LicenseSeat extends Model implements ICompanyableChild +class LicenseSeat extends SnipeModel implements ICompanyableChild { use CompanyableChildTrait; use SoftDeletes; use Loggable; + protected $presenter = 'App\Presenters\LicenseSeatPresenter'; + use Presentable; + protected $dates = ['deleted_at']; protected $guarded = 'id'; protected $table = 'license_seats'; @@ -22,6 +26,10 @@ class LicenseSeat extends Model implements ICompanyableChild return ['asset', 'license']; } + public function getEula() { + return $this->license->getEula(); + } + /** * Establishes the seat -> license relationship * diff --git a/app/Notifications/CheckinLicenseNotification.php b/app/Notifications/CheckinLicenseSeatNotification.php similarity index 93% rename from app/Notifications/CheckinLicenseNotification.php rename to app/Notifications/CheckinLicenseSeatNotification.php index 3ff06d2ca3..69554d493b 100644 --- a/app/Notifications/CheckinLicenseNotification.php +++ b/app/Notifications/CheckinLicenseSeatNotification.php @@ -3,6 +3,7 @@ namespace App\Notifications; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -13,7 +14,7 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Mail; -class CheckinLicenseNotification extends Notification +class CheckinLicenseSeatNotification extends Notification { use Queueable; /** @@ -26,10 +27,10 @@ class CheckinLicenseNotification extends Notification * * @param $params */ - public function __construct(License $license, $checkedOutTo, User $checkedInBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedInBy, $note) { $this->target = $checkedOutTo; - $this->item = $license; + $this->item = $licenseSeat->license; $this->admin = $checkedInBy; $this->note = $note; $this->settings = Setting::getSettings(); diff --git a/app/Notifications/CheckoutLicenseNotification.php b/app/Notifications/CheckoutLicenseSeatNotification.php similarity index 90% rename from app/Notifications/CheckoutLicenseNotification.php rename to app/Notifications/CheckoutLicenseSeatNotification.php index e5c697b2a0..74df6e2f46 100644 --- a/app/Notifications/CheckoutLicenseNotification.php +++ b/app/Notifications/CheckoutLicenseSeatNotification.php @@ -3,6 +3,7 @@ namespace App\Notifications; use App\Models\License; +use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\SnipeModel; use App\Models\User; @@ -13,7 +14,7 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Mail; -class CheckoutLicenseNotification extends Notification +class CheckoutLicenseSeatNotification extends Notification { use Queueable; /** @@ -26,12 +27,13 @@ class CheckoutLicenseNotification extends Notification * * @param $params */ - public function __construct(License $license, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(LicenseSeat $licenseSeat, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { - $this->item = $license; + $this->item = $licenseSeat->license; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); } @@ -117,6 +119,8 @@ class CheckoutLicenseNotification extends Notification $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + return (new MailMessage)->markdown('notifications.markdown.checkout-license', [ 'item' => $this->item, @@ -125,7 +129,7 @@ class CheckoutLicenseNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['license', $this->item->id]), + 'accept_url' => $accept_url, ]) ->subject(trans('mail.Confirm_license_delivery')); diff --git a/app/Presenters/LicenseSeatPresenter.php b/app/Presenters/LicenseSeatPresenter.php new file mode 100644 index 0000000000..5267aa7290 --- /dev/null +++ b/app/Presenters/LicenseSeatPresenter.php @@ -0,0 +1,18 @@ +model->license->name; + } +} From 8648d53d25fba99c6f2b6ffe01ec6e3ad7537e84 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 12:45:33 +0200 Subject: [PATCH 17/24] Adds checkout acceptances A checkout acceptance gets generated for every item that needs to be checked out. This resource tracks the user user who can accept the item and their signature --- ...{ItemDeclined.php => CheckoutAccepted.php} | 13 +--- ...{ItemAccepted.php => CheckoutDeclined.php} | 13 +--- .../Account/AcceptanceController.php | 73 +++++++++---------- app/Listeners/LogListener.php | 34 ++++++--- .../SendingCheckOutNotificationsListener.php | 60 +++++++++++++-- app/Models/CheckoutAcceptance.php | 50 +++++++++++++ .../CheckoutAccessoryNotification.php | 7 +- .../CheckoutAssetNotification.php | 7 +- .../CheckoutConsumableNotification.php | 7 +- ...3826_create_checkout_acceptances_table.php | 41 +++++++++++ .../create.blade.php} | 6 +- .../views/account/accept/index.blade.php | 61 ++++++++++++++++ resources/views/layouts/default.blade.php | 6 +- routes/web.php | 4 +- 14 files changed, 296 insertions(+), 86 deletions(-) rename app/Events/{ItemDeclined.php => CheckoutAccepted.php} (57%) rename app/Events/{ItemAccepted.php => CheckoutDeclined.php} (58%) create mode 100644 app/Models/CheckoutAcceptance.php create mode 100644 database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php rename resources/views/account/{accept.blade.php => accept/create.blade.php} (96%) create mode 100755 resources/views/account/accept/index.blade.php diff --git a/app/Events/ItemDeclined.php b/app/Events/CheckoutAccepted.php similarity index 57% rename from app/Events/ItemDeclined.php rename to app/Events/CheckoutAccepted.php index a735c193ef..0b26e69475 100644 --- a/app/Events/ItemDeclined.php +++ b/app/Events/CheckoutAccepted.php @@ -4,29 +4,24 @@ namespace App\Events; use App\Models\Accessory; use App\Models\Actionlog; +use App\Models\CheckoutAcceptance; use App\Models\Contracts\Acceptable; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class ItemDeclined +class CheckoutAccepted { use Dispatchable, SerializesModels; - public $item; - public $declinedBy; - public $signature; - /** * Create a new event instance. * * @return void */ - public function __construct(Acceptable $item, User $declinedBy, string $signature) + public function __construct(CheckoutAcceptance $acceptance) { - $this->item = $item; - $this->declinedBy = $declinedBy; - $this->signature = $signature; + $this->acceptance = $acceptance; } } diff --git a/app/Events/ItemAccepted.php b/app/Events/CheckoutDeclined.php similarity index 58% rename from app/Events/ItemAccepted.php rename to app/Events/CheckoutDeclined.php index 3c854d9ea5..2aae6dd914 100644 --- a/app/Events/ItemAccepted.php +++ b/app/Events/CheckoutDeclined.php @@ -4,29 +4,24 @@ namespace App\Events; use App\Models\Accessory; use App\Models\Actionlog; +use App\Models\CheckoutAcceptance; use App\Models\Contracts\Acceptable; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class ItemAccepted +class CheckoutDeclined { use Dispatchable, SerializesModels; - - public $item; - public $acceptedBy; - public $signature; /** * Create a new event instance. * * @return void */ - public function __construct(Acceptable $item, User $acceptedBy, string $signature) + public function __construct(CheckoutAcceptance $acceptance) { - $this->item = $item; - $this->acceptedBy = $acceptedBy; - $this->signature = $signature; + $this->acceptance = $acceptance; } } diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index ecf9924277..5c7235a2ae 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -1,10 +1,13 @@ pending()->get(); + + return view('account/accept.index', compact('acceptances')); } - public function edit(Request $request, $type, $id) { + public function create(Request $request, $id) { - $item = $this->getItemById($type, $id); + $acceptance = CheckoutAcceptance::find($id); - if (is_null($item)) { + if (is_null($acceptance)) { return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); } - if ($item->isAccepted()) { + if (! $acceptance->isPending()) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); } - if (! $item->isCheckedOutTo(Auth::user())) { + if (! $acceptance->isCheckedOutTo(Auth::user())) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); } - if (!Company::isCurrentUserHasAccess($item)) { + if (!Company::isCurrentUserHasAccess($acceptance->checkoutable)) { return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); } - return view('account/accept', compact('item')); + return view('account/accept.create', compact('acceptance')); } - public function update(Request $request, $type, $id) { - $item = $this->getItemById($type, $id); + public function store(Request $request, $id) { + + $acceptance = CheckoutAcceptance::find($id); - if (is_null($item)) { + if (is_null($acceptance)) { return redirect()->reoute('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); } - if ($item->isAccepted()) { + if (! $acceptance->isPending()) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.asset_already_accepted')); - } + } - if (! $item->isCheckedOutTo(Auth::user())) { + if (! $acceptance->isCheckedOutTo(Auth::user())) { return redirect()->route('account.accept')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); } - if (!Company::isCurrentUserHasAccess($item)) { + if (!Company::isCurrentUserHasAccess($acceptance->checkoutable)) { return redirect()->route('account.accept')->with('error', trans('general.insufficient_permissions')); - } + } if (!$request->filled('asset_acceptance')) { return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); @@ -79,41 +85,30 @@ class AcceptanceController extends Controller { if ($request->input('asset_acceptance') == 'accepted') { - $item->accept(Auth::user(), $sig_filename); + $acceptance->accepted_at = now(); + $acceptance->signature_filename = $sig_filename; + $acceptance->save(); - event(new ItemAccepted($item, Auth::user(), $sig_filename)); + // TODO: Update state for the checkoutable + + event(new CheckoutAccepted($acceptance)); $return_msg = trans('admin/users/message.accepted'); } else { - $item->decline(Auth::user(), $sig_filename); + $acceptance->declined_at = now(); + $acceptance->signature_filename = $sig_filename; + $acceptance->save(); - event(new ItemDeclined($item, Auth::user(), $sig_filename)); + // TODO: Update state for the checkoutable + + event(new CheckoutDeclined($acceptance)); $return_msg = trans('admin/users/message.declined'); } return redirect()->to('account/accept')->with('success', $return_msg); - } - - private function getItemById($type, $id) : ? Acceptable { - switch ($type) { - case 'asset': - $item = Asset::findOrFail($id); - break; - case 'consumable': - $item = Consumable::findOrFail($id); - break; - case 'license': - $item = License::findOrFail($id); - break; - default: - $item = null; - break; - } - - return $item; } } \ No newline at end of file diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 858c9ec291..9cd8ec2717 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -6,6 +6,8 @@ use App\Events\AccessoryCheckedIn; use App\Events\AccessoryCheckedOut; use App\Events\AssetCheckedIn; use App\Events\AssetCheckedOut; +use App\Events\CheckoutAccepted; +use App\Events\CheckoutDeclined; use App\Events\ComponentCheckedIn; use App\Events\ComponentCheckedOut; use App\Events\ConsumableCheckedOut; @@ -16,6 +18,7 @@ use App\Events\LicenseCheckedOut; use App\Models\Actionlog; use App\Models\Asset; use App\Models\Component; +use App\Models\LicenseSeat; class LogListener @@ -65,23 +68,34 @@ class LogListener $event->license->logCheckout($event->note, $event->checkedOutTo); } - public function onItemAccepted(ItemAccepted $event) { + public function onCheckoutAccepted(CheckoutAccepted $event) { $logaction = new Actionlog(); - $logaction->item()->associate($event->item); - $logaction->target()->associate($event->acceptedBy); - $logaction->accept_signature = $event->signature; + + $logaction->item()->associate($event->acceptance->checkoutable); + $logaction->target()->associate($event->acceptance->assignedTo); + $logaction->accept_signature = $event->acceptance->signature_filename; $logaction->action_type = 'accepted'; + // TODO: log the actual license seat that was checked out + if($event->acceptance->checkoutable instanceof LicenseSeat) { + $logaction->item()->associate($event->acceptance->checkoutable->license); + } + $logaction->save(); } - public function onItemDeclined(ItemDeclined $event) { + public function onCheckoutDeclined(CheckoutDeclined $event) { $logaction = new Actionlog(); - $logaction->item()->associate($event->item); - $logaction->target()->associate($event->declinedBy); - $logaction->accept_signature = $event->signature; + $logaction->item()->associate($event->acceptance->checkoutable); + $logaction->target()->associate($event->acceptance->assignedTo); + $logaction->accept_signature = $event->acceptance->signature_filename; $logaction->action_type = 'declined'; + // TODO: log the actual license seat that was checked out + if($event->acceptance->checkoutable instanceof LicenseSeat) { + $logaction->item()->associate($event->acceptance->checkoutable->license); + } + $logaction->save(); } @@ -102,8 +116,8 @@ class LogListener 'ConsumableCheckedOut', 'LicenseCheckedIn', 'LicenseCheckedOut', - 'ItemAccepted', - 'ItemDeclined', + 'CheckoutAccepted', + 'CheckoutDeclined', ]; foreach($list as $event) { diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php index 1968c13d1a..ed459c5a59 100644 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ b/app/Listeners/SendingCheckOutNotificationsListener.php @@ -2,6 +2,7 @@ namespace App\Listeners; +use App\Models\CheckoutAcceptance; use App\Models\Recipients\AdminRecipient; use App\Models\Setting; use App\Models\User; @@ -9,6 +10,7 @@ use App\Notifications\CheckoutAccessoryNotification; use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseNotification; +use App\Notifications\CheckoutLicenseSeatNotification; use Illuminate\Support\Facades\Notification; class SendingCheckOutNotificationsListener @@ -24,9 +26,20 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->consumable->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->consumable); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } @@ -41,16 +54,27 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->accessory->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->accessory); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } /** * Notify the user about the checked out license */ - public function onLicenseCheckedOut($event) { + public function onLicenseSeatCheckedOut($event) { /** * When the item wasn't checked out to a user, we can't send notifications */ @@ -58,9 +82,20 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->licenseSeat->license->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->licenseSeat); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutLicenseNotification($event->license, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } @@ -75,9 +110,20 @@ class SendingCheckOutNotificationsListener return; } + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = null; + if ($event->asset->requireAcceptance()) { + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->asset); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + } + Notification::send( $this->getNotifiables($event), - new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $event->note) + new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) ); } @@ -123,8 +169,8 @@ class SendingCheckOutNotificationsListener ); $events->listen( - 'App\Events\LicenseCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseCheckedOut' + 'App\Events\LicenseSeatCheckedOut', + 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseSeatCheckedOut' ); $events->listen( diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php new file mode 100644 index 0000000000..f3d2355e53 --- /dev/null +++ b/app/Models/CheckoutAcceptance.php @@ -0,0 +1,50 @@ +morphTo(); + } + + public function assignedTo() { + return $this->belongsTo(User::class); + } + + public function isPending() { + return $this->accepted_at == null && $this->declined_at == null; + } + + public function isCheckedOutTo(User $user) { + return $this->assignedTo->is($user); + } + + public function scopeForUser(Builder $query, User $user) { + return $query->where('assigned_to_id', $user->id); + } + + public function scopePending(Builder $query) { + return $query->whereNull('accepted_at')->whereNull('declined_at'); + } +} diff --git a/app/Notifications/CheckoutAccessoryNotification.php b/app/Notifications/CheckoutAccessoryNotification.php index c00da8d633..11368bf06c 100644 --- a/app/Notifications/CheckoutAccessoryNotification.php +++ b/app/Notifications/CheckoutAccessoryNotification.php @@ -20,13 +20,14 @@ class CheckoutAccessoryNotification extends Notification /** * Create a new notification instance. */ - public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(Accessory $accessory, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { $this->item = $accessory; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); @@ -119,6 +120,8 @@ class CheckoutAccessoryNotification extends Notification $eula = $this->item->getEula(); $req_accept = $this->item->requireAcceptance(); + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + return (new MailMessage)->markdown('notifications.markdown.checkout-accessory', [ 'item' => $this->item, @@ -127,7 +130,7 @@ class CheckoutAccessoryNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['accessory', $this->item->id]), + 'accept_url' => $accept_url, ]) ->subject(trans('mail.Confirm_accessory_delivery')); diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index f0323342f0..db4ba294f5 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -20,13 +20,14 @@ class CheckoutAssetNotification extends Notification * * @param $params */ - public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { $this->item = $asset; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); @@ -140,6 +141,8 @@ class CheckoutAssetNotification extends Notification $fields = $this->item->model->fieldset->fields; } + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + $message = (new MailMessage)->markdown('notifications.markdown.checkout-asset', [ 'item' => $this->item, @@ -149,7 +152,7 @@ class CheckoutAssetNotification extends Notification 'fields' => $fields, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['asset', $this->item->id]), + 'accept_url' => $accept_url, 'last_checkout' => $this->last_checkout, 'expected_checkin' => $this->expected_checkin, ]) diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index 7c6dfb7b3f..40780c7abc 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -26,13 +26,14 @@ class CheckoutConsumableNotification extends Notification * * @param $params */ - public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $note) + public function __construct(Consumable $consumable, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { $this->item = $consumable; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; + $this->acceptance = $acceptance; $this->settings = Setting::getSettings(); @@ -121,6 +122,8 @@ class CheckoutConsumableNotification extends Notification $eula = $this->item->getEula(); $req_accept = $this->item->requireAcceptance(); + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + return (new MailMessage)->markdown('notifications.markdown.checkout-consumable', [ 'item' => $this->item, @@ -129,7 +132,7 @@ class CheckoutConsumableNotification extends Notification 'target' => $this->target, 'eula' => $eula, 'req_accept' => $req_accept, - 'accept_url' => route('account.accept.item', ['consumable', $this->item->id]), + 'accept_url' => $accept_url, ]) ->subject(trans('mail.Confirm_consumable_delivery')); diff --git a/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php new file mode 100644 index 0000000000..6d1f1f2b3a --- /dev/null +++ b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php @@ -0,0 +1,41 @@ +increments('id'); + + $table->morphs('checkoutable'); + $table->integer('assigned_to_id')->unsigned(); + + $table->string('signature_filename'); + + $table->timestamp('accepted_at')->nullable(); + $table->timestamp('declined_at')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('checkout_acceptances'); + } +} diff --git a/resources/views/account/accept.blade.php b/resources/views/account/accept/create.blade.php similarity index 96% rename from resources/views/account/accept.blade.php rename to resources/views/account/accept/create.blade.php index 9073f8516a..67ca326e16 100644 --- a/resources/views/account/accept.blade.php +++ b/resources/views/account/accept/create.blade.php @@ -2,7 +2,7 @@ {{-- Page title --}} @section('title') - Accept {{ $item->present()->name() }} + Accept {{ $acceptance->checkoutable->present()->name() }} @parent @stop @@ -52,10 +52,10 @@ - @if ($item->getEula()) + @if ($acceptance->checkoutable->getEula())
- {!! $item->getEula() !!} + {!! $acceptance->checkoutable->getEula() !!}
@endif diff --git a/resources/views/account/accept/index.blade.php b/resources/views/account/accept/index.blade.php new file mode 100755 index 0000000000..4a699ea4f9 --- /dev/null +++ b/resources/views/account/accept/index.blade.php @@ -0,0 +1,61 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') +Accept assets {{ $user->present()->fullName() }} +@parent +@stop + +{{-- Account page content --}} +@section('content') +
+
+
+ +
+ + +
+ + + + + + + + + @foreach ($acceptances as $acceptance) + + + + + @endforeach + +
NameActions
{{ $acceptance->checkoutable->present()->name }}Accept/Decline
+
+ +
+
+
+
+ +@stop + +@section('moar_scripts') + @include ('partials.bootstrap-table') +@stop diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index 196b085bbc..874a1eef01 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -317,7 +317,11 @@ Requested Assets - +
  • + + + Accept Assets +
  • diff --git a/routes/web.php b/routes/web.php index 4b6d13a7cd..eae9209345 100644 --- a/routes/web.php +++ b/routes/web.php @@ -278,10 +278,10 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { Route::get('accept', 'Account\AcceptanceController@index') ->name('account.accept'); - Route::get('accept/{type}/{id}', 'Account\AcceptanceController@edit') + Route::get('accept/{id}', 'Account\AcceptanceController@create') ->name('account.accept.item'); - Route::post('accept/{type}/{id}', 'Account\AcceptanceController@update'); + Route::post('accept/{id}', 'Account\AcceptanceController@store'); }); From 1bdf71b58455d8f2bffbf4c73c3ffcfb6c9980fa Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 13:09:21 +0200 Subject: [PATCH 18/24] Handle side effects of accepting/declining When declining an asset, it gets checked in. --- .../Account/AcceptanceController.php | 12 +---- app/Models/Accessory.php | 3 ++ app/Models/Asset.php | 52 ++++--------------- app/Models/CheckoutAcceptance.php | 22 ++++++++ app/Models/Consumable.php | 3 ++ app/Models/Contracts/Acceptable.php | 11 ---- app/Models/LicenseSeat.php | 3 ++ app/Models/Traits/Acceptable.php | 27 ++++++++++ 8 files changed, 69 insertions(+), 64 deletions(-) delete mode 100644 app/Models/Contracts/Acceptable.php create mode 100644 app/Models/Traits/Acceptable.php diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 5c7235a2ae..3e37871aa4 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -85,11 +85,7 @@ class AcceptanceController extends Controller { if ($request->input('asset_acceptance') == 'accepted') { - $acceptance->accepted_at = now(); - $acceptance->signature_filename = $sig_filename; - $acceptance->save(); - - // TODO: Update state for the checkoutable + $acceptance->accept($sig_filename); event(new CheckoutAccepted($acceptance)); @@ -97,11 +93,7 @@ class AcceptanceController extends Controller { } else { - $acceptance->declined_at = now(); - $acceptance->signature_filename = $sig_filename; - $acceptance->save(); - - // TODO: Update state for the checkoutable + $acceptance->decline($sig_filename); event(new CheckoutDeclined($acceptance)); diff --git a/app/Models/Accessory.php b/app/Models/Accessory.php index cdba5f3206..76b50704c6 100755 --- a/app/Models/Accessory.php +++ b/app/Models/Accessory.php @@ -1,6 +1,7 @@ accepted = 'accepted'; - $this->save(); - } - - /** - * Decline the asset - * - * @param User $declinedBy The user who declines the asset - * @param string $signature The filename of the signature, if provided - */ - public function decline(User $declinedBy, $signature = null) { + * @param User $acceptedBy + * @param string $signature + */ + public function declinedCheckout(User $declinedBy, $signature) { $this->assigned_to = null; $this->assigned_type = null; $this->accepted = null; - $this->save(); - } - - /** - * Is the asset already accepted? - * - * @return boolean - */ - public function isAccepted() : bool { - return $this->accepted != 'pending'; - } - - /** - * Is the asset checked out to this user? - * - * @param User $user - * @return boolean - */ - public function isCheckedOutTo(User $user) { - if (is_null($this->assignedTo)) { - return false; - } - - return $this->assignedTo->is($user); + $this->save(); } diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php index f3d2355e53..28442a0aeb 100644 --- a/app/Models/CheckoutAcceptance.php +++ b/app/Models/CheckoutAcceptance.php @@ -40,6 +40,28 @@ class CheckoutAcceptance extends Model return $this->assignedTo->is($user); } + public function accept($signature_filename) { + $this->accepted_at = now(); + $this->signature_filename = $signature_filename; + $this->save(); + + /** + * Update state for the checked out item + */ + $this->checkoutable->acceptedCheckout($this->assignedTo, $signature_filename); + } + + public function decline($signature_filename) { + $this->declined_at = now(); + $this->signature_filename = $signature_filename; + $this->save(); + + /** + * Update state for the checked out item + */ + $this->checkoutable->declinedCheckout($this->assignedTo, $signature_filename); + } + public function scopeForUser(Builder $query, User $user) { return $query->where('assigned_to_id', $user->id); } diff --git a/app/Models/Consumable.php b/app/Models/Consumable.php index 1b3a0da156..596106c262 100644 --- a/app/Models/Consumable.php +++ b/app/Models/Consumable.php @@ -1,6 +1,7 @@ Date: Sat, 28 Jul 2018 13:21:11 +0200 Subject: [PATCH 19/24] Updates asset acceptance report to show unaccepted assets --- app/Http/Controllers/ReportsController.php | 16 +++++++++++++++- app/Models/Asset.php | 13 ------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 05e0a0f13c..79d41f7b9c 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -7,6 +7,7 @@ use App\Models\Actionlog; use App\Models\Asset; use App\Models\AssetMaintenance; use App\Models\CustomField; +use App\Models\CheckoutAcceptance; use App\Models\Depreciation; use App\Models\License; use App\Models\Setting; @@ -805,7 +806,20 @@ class ReportsController extends Controller public function getAssetAcceptanceReport() { $this->authorize('reports.view'); - $assetsForReport = Asset::notYetAccepted()->with('company')->get(); + + /** + * Get all assets with pending checkout acceptances + */ + + $acceptances = CheckoutAcceptance::pending()->get(); + + $assetsForReport = $acceptances + ->filter(function($acceptance) { + return $acceptance->checkoutable_type == 'App\Models\Asset'; + }) + ->map(function($acceptance) { + return $acceptance->checkoutable; + }); return view('reports/unaccepted_assets', compact('assetsForReport')); } diff --git a/app/Models/Asset.php b/app/Models/Asset.php index ec38931b4f..35206b41ea 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -34,7 +34,6 @@ class Asset extends Depreciable const ASSET = 'asset'; const USER = 'user'; - const ACCEPTANCE_PENDING = 'pending'; use Acceptable; /** @@ -263,18 +262,6 @@ class Asset extends Depreciable $this->location_id = $target->id; } } - - /** - * Does the user have to confirm that they accept the asset? - * - * If so, set the acceptance-status to "pending". - * This value is used in the unaccepted assets reports, for example - * - * @see https://github.com/snipe/snipe-it/issues/5772 - */ - if ($this->requireAcceptance() && $target instanceof User) { - $this->accepted = self::ACCEPTANCE_PENDING; - } if ($this->save()) { From 62195a805af48be810957f40d6cb0bc27e27b962 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Sat, 28 Jul 2018 13:32:29 +0200 Subject: [PATCH 20/24] Adding some comments --- .../Account/AcceptanceController.php | 23 +++++++++- app/Models/CheckoutAcceptance.php | 42 +++++++++++++++++++ app/Models/Traits/Acceptable.php | 6 ++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 3e37871aa4..198e11e998 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -18,12 +18,24 @@ use Illuminate\Support\Str; class AcceptanceController extends Controller { - public function index(Request $request) { + /** + * Show a listing of pending checkout acceptances for the current user + * + * @return View + */ + public function index() { $acceptances = CheckoutAcceptance::forUser(Auth::user())->pending()->get(); return view('account/accept.index', compact('acceptances')); } - public function create(Request $request, $id) { + + /** + * Shows a form to either accept or decline the checkout acceptance + * + * @param int $id + * @return mixed + */ + public function create($id) { $acceptance = CheckoutAcceptance::find($id); @@ -46,6 +58,13 @@ class AcceptanceController extends Controller { return view('account/accept.create', compact('acceptance')); } + /** + * Stores the accept/decline of the checkout acceptance + * + * @param Request $request + * @param int $id + * @return Redirect + */ public function store(Request $request, $id) { $acceptance = CheckoutAcceptance::find($id); diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php index 28442a0aeb..6ff850f41f 100644 --- a/app/Models/CheckoutAcceptance.php +++ b/app/Models/CheckoutAcceptance.php @@ -24,22 +24,48 @@ class CheckoutAcceptance extends Model 'deleted_at' ]; + /** + * The resource that was is out + * + * @return Illuminate\Database\Eloquent\Relations\MorphTo + */ public function checkoutable() { return $this->morphTo(); } + /** + * The user that the checkoutable was checked out to + * + * @return Illuminate\Database\Eloquent\Relations\BelongsTo + */ public function assignedTo() { return $this->belongsTo(User::class); } + /** + * Is this checkout acceptance pending? + * + * @return boolean + */ public function isPending() { return $this->accepted_at == null && $this->declined_at == null; } + /** + * Was the checkoutable checked out to this user? + * + * @param User $user + * @return boolean + */ public function isCheckedOutTo(User $user) { return $this->assignedTo->is($user); } + /** + * Accept the checkout acceptance + * + * @param string $signature_filename + */ public function accept($signature_filename) { $this->accepted_at = now(); $this->signature_filename = $signature_filename; @@ -51,6 +77,11 @@ class CheckoutAcceptance extends Model $this->checkoutable->acceptedCheckout($this->assignedTo, $signature_filename); } + /** + * Decline the checkout acceptance + * + * @param string $signature_filename + */ public function decline($signature_filename) { $this->declined_at = now(); $this->signature_filename = $signature_filename; @@ -62,10 +93,21 @@ class CheckoutAcceptance extends Model $this->checkoutable->declinedCheckout($this->assignedTo, $signature_filename); } + /** + * Filter checkout acceptences by the user + * @param Illuminate\Database\Eloquent\Builder $query + * @param User $user + * @return Illuminate\Database\Eloquent\Builder + */ public function scopeForUser(Builder $query, User $user) { return $query->where('assigned_to_id', $user->id); } + /** + * Filter to only get pending acceptances + * @param Illuminate\Database\Eloquent\Builder $query + * @return Illuminate\Database\Eloquent\Builder + */ public function scopePending(Builder $query) { return $query->whereNull('accepted_at')->whereNull('declined_at'); } diff --git a/app/Models/Traits/Acceptable.php b/app/Models/Traits/Acceptable.php index 0837b9a9ae..a0c93a861b 100644 --- a/app/Models/Traits/Acceptable.php +++ b/app/Models/Traits/Acceptable.php @@ -7,7 +7,11 @@ use App\Models\CustomField; use App\Models\User; use Illuminate\Database\Eloquent\Builder; - +/** + * This trait allows models to have a callback after their checkout gets accepted or declined. + * + * @author Till Deeke + */ trait Acceptable { /** * Run after the checkout acceptance was accepted by the user From 007e8fbdf988e0722103a3ac47ea78928d3a628f Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 16:40:26 +0200 Subject: [PATCH 21/24] =?UTF-8?q?simplified=20checkout=20event=20handling?= =?UTF-8?q?=20per=20@uberbrady=E2=80=99s=20suggestion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This generalizes the checkout events into the CheckoutableCheckedOut and CheckoutableCheckedIn events. --- app/Events/AccessoryCheckedOut.php | 32 --- app/Events/AssetCheckedIn.php | 33 ---- app/Events/AssetCheckedOut.php | 32 --- ...heckedIn.php => CheckoutableCheckedIn.php} | 13 +- ...ckedOut.php => CheckoutableCheckedOut.php} | 13 +- app/Events/ComponentCheckedIn.php | 35 ---- app/Events/ConsumableCheckedOut.php | 32 --- app/Events/LicenseSeatCheckedIn.php | 34 ---- app/Events/LicenseSeatCheckedOut.php | 33 ---- .../AccessoryCheckinController.php | 4 +- .../AccessoryCheckoutController.php | 4 +- .../Assets/AssetCheckinController.php | 4 +- .../Components/ComponentCheckinController.php | 5 +- .../ComponentCheckoutController.php | 3 +- .../ConsumableCheckoutController.php | 4 +- .../Licenses/LicenseCheckinController.php | 4 +- .../Licenses/LicenseCheckoutController.php | 9 +- app/Listeners/CheckoutableListener.php | 184 ++++++++++++++++++ app/Listeners/LogListener.php | 59 +----- .../SendingCheckInNotificationsListener.php | 117 ----------- .../SendingCheckOutNotificationsListener.php | 182 ----------------- app/Models/Asset.php | 3 +- app/Providers/EventServiceProvider.php | 8 +- 23 files changed, 229 insertions(+), 618 deletions(-) delete mode 100644 app/Events/AccessoryCheckedOut.php delete mode 100644 app/Events/AssetCheckedIn.php delete mode 100644 app/Events/AssetCheckedOut.php rename app/Events/{AccessoryCheckedIn.php => CheckoutableCheckedIn.php} (62%) rename app/Events/{ComponentCheckedOut.php => CheckoutableCheckedOut.php} (59%) delete mode 100644 app/Events/ComponentCheckedIn.php delete mode 100644 app/Events/ConsumableCheckedOut.php delete mode 100644 app/Events/LicenseSeatCheckedIn.php delete mode 100644 app/Events/LicenseSeatCheckedOut.php create mode 100644 app/Listeners/CheckoutableListener.php delete mode 100644 app/Listeners/SendingCheckInNotificationsListener.php delete mode 100644 app/Listeners/SendingCheckOutNotificationsListener.php diff --git a/app/Events/AccessoryCheckedOut.php b/app/Events/AccessoryCheckedOut.php deleted file mode 100644 index faf30adb36..0000000000 --- a/app/Events/AccessoryCheckedOut.php +++ /dev/null @@ -1,32 +0,0 @@ -accessory = $accessory; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Events/AssetCheckedIn.php b/app/Events/AssetCheckedIn.php deleted file mode 100644 index 455b2b5d4e..0000000000 --- a/app/Events/AssetCheckedIn.php +++ /dev/null @@ -1,33 +0,0 @@ -asset = $asset; - $this->checkedOutTo = $checkedOutTo; - $this->checkedInBy = $checkedInBy; - $this->note = $note; - } -} diff --git a/app/Events/AssetCheckedOut.php b/app/Events/AssetCheckedOut.php deleted file mode 100644 index 1cd0669ad8..0000000000 --- a/app/Events/AssetCheckedOut.php +++ /dev/null @@ -1,32 +0,0 @@ -asset = $asset; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Events/AccessoryCheckedIn.php b/app/Events/CheckoutableCheckedIn.php similarity index 62% rename from app/Events/AccessoryCheckedIn.php rename to app/Events/CheckoutableCheckedIn.php index 812e072b28..0f6a96491a 100644 --- a/app/Events/AccessoryCheckedIn.php +++ b/app/Events/CheckoutableCheckedIn.php @@ -2,30 +2,27 @@ namespace App\Events; -use App\Models\Accessory; -use App\Models\Actionlog; use App\Models\User; -use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class AccessoryCheckedIn +class CheckoutableCheckedIn { use Dispatchable, SerializesModels; - public $accessory; + public $checkoutable; public $checkedOutTo; public $checkedInBy; public $note; - + /** * Create a new event instance. * * @return void */ - public function __construct(Accessory $accessory, $checkedOutTo, User $checkedInBy, $note) + public function __construct($checkoutable, $checkedOutTo, User $checkedInBy, $note) { - $this->accessory = $accessory; + $this->checkoutable = $checkoutable; $this->checkedOutTo = $checkedOutTo; $this->checkedInBy = $checkedInBy; $this->note = $note; diff --git a/app/Events/ComponentCheckedOut.php b/app/Events/CheckoutableCheckedOut.php similarity index 59% rename from app/Events/ComponentCheckedOut.php rename to app/Events/CheckoutableCheckedOut.php index f74e7d70f4..5e6ffd243a 100644 --- a/app/Events/ComponentCheckedOut.php +++ b/app/Events/CheckoutableCheckedOut.php @@ -2,30 +2,29 @@ namespace App\Events; -use App\Models\Component; use App\Models\User; -use Illuminate\Broadcasting\Channel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class ComponentCheckedOut +class CheckoutableCheckedOut { use Dispatchable, SerializesModels; - public $component; + public $checkoutable; public $checkedOutTo; public $checkedOutBy; + public $note; /** * Create a new event instance. * * @return void */ - public function __construct(Component $component, $checkedOutTo, $quantity, User $checkedOutBy) + public function __construct($checkoutable, $checkedOutTo, User $checkedOutBy, $note) { - $this->component = $component; + $this->checkoutable = $checkoutable; $this->checkedOutTo = $checkedOutTo; - $this->quantity = $quantity; $this->checkedOutBy = $checkedOutBy; + $this->note = $note; } } diff --git a/app/Events/ComponentCheckedIn.php b/app/Events/ComponentCheckedIn.php deleted file mode 100644 index 283ea6e358..0000000000 --- a/app/Events/ComponentCheckedIn.php +++ /dev/null @@ -1,35 +0,0 @@ -component = $component; - $this->checkedOutTo = $checkedOutTo; - $this->checkedInBy = $checkedInBy; - $this->quantity = $quantity; - $this->note = $note; - } -} diff --git a/app/Events/ConsumableCheckedOut.php b/app/Events/ConsumableCheckedOut.php deleted file mode 100644 index ca002cc2f8..0000000000 --- a/app/Events/ConsumableCheckedOut.php +++ /dev/null @@ -1,32 +0,0 @@ -consumable = $consumable; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Events/LicenseSeatCheckedIn.php b/app/Events/LicenseSeatCheckedIn.php deleted file mode 100644 index 80e7ecbaf4..0000000000 --- a/app/Events/LicenseSeatCheckedIn.php +++ /dev/null @@ -1,34 +0,0 @@ -licenseSeat = $licenseSeat; - $this->checkedOutTo = $checkedOutTo; - $this->checkedInBy = $checkedInBy; - $this->note = $note; - } -} diff --git a/app/Events/LicenseSeatCheckedOut.php b/app/Events/LicenseSeatCheckedOut.php deleted file mode 100644 index 6b057be43e..0000000000 --- a/app/Events/LicenseSeatCheckedOut.php +++ /dev/null @@ -1,33 +0,0 @@ -licenseSeat = $licenseSeat; - $this->checkedOutTo = $checkedOutTo; - $this->checkedOutBy = $checkedOutBy; - $this->note = $note; - } -} diff --git a/app/Http/Controllers/Accessories/AccessoryCheckinController.php b/app/Http/Controllers/Accessories/AccessoryCheckinController.php index e64d24f169..930e9b6619 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckinController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckinController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Accessories; -use App\Events\AccessoryCheckedIn; +use App\Events\CheckoutableCheckedIn; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -64,7 +64,7 @@ class AccessoryCheckinController extends Controller if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) { $return_to = e($accessory_user->assigned_to); - event(new AccessoryCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($accessory, User::find($return_to), Auth::user(), $request->input('note'))); return redirect()->route("accessories.show", $accessory->id)->with('success', trans('admin/accessories/message.checkin.success')); } diff --git a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php index 13bc9e8fd2..5ec8a8704f 100644 --- a/app/Http/Controllers/Accessories/AccessoryCheckoutController.php +++ b/app/Http/Controllers/Accessories/AccessoryCheckoutController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Accessories; -use App\Events\AccessoryCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Http\Controllers\Controller; use App\Models\Accessory; use App\Models\User; @@ -80,7 +80,7 @@ class AccessoryCheckoutController extends Controller DB::table('accessories_users')->where('assigned_to', '=', $accessory->assigned_to)->where('accessory_id', '=', $accessory->id)->first(); - event(new AccessoryCheckedOut($accessory, $user, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedOut($accessory, $user, Auth::user(), $request->input('note'))); // Redirect to the new accessory page return redirect()->route('accessories.index')->with('success', trans('admin/accessories/message.checkout.success')); diff --git a/app/Http/Controllers/Assets/AssetCheckinController.php b/app/Http/Controllers/Assets/AssetCheckinController.php index da141f8b3d..e87a1df23c 100644 --- a/app/Http/Controllers/Assets/AssetCheckinController.php +++ b/app/Http/Controllers/Assets/AssetCheckinController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Assets; -use App\Events\AssetCheckedIn; +use App\Events\CheckoutableCheckedIn; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Http\Requests\AssetCheckinRequest; @@ -85,7 +85,7 @@ class AssetCheckinController extends Controller // Was the asset updated? if ($asset->save()) { - event(new AssetCheckedIn($asset, $target, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'))); if ($backto=='user') { return redirect()->route("users.show", $user->id)->with('success', trans('admin/hardware/message.checkin.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckinController.php b/app/Http/Controllers/Components/ComponentCheckinController.php index fd91daa841..dfba1dcf84 100644 --- a/app/Http/Controllers/Components/ComponentCheckinController.php +++ b/app/Http/Controllers/Components/ComponentCheckinController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\CheckoutableCheckedIn; use App\Events\ComponentCheckedIn; use App\Http\Controllers\Controller; use App\Models\Actionlog; @@ -94,7 +95,9 @@ class ComponentCheckinController extends Controller DB::table('components_assets')->where('id', '=', $component_asset_id)->delete(); } - event(new ComponentCheckedIn($component, $component_assets, Auth::user(), $request->input('checkin_qty'), $request->input('note'))); + $asset = Asset::find($component_assets->asset_id); + + event(new CheckoutableCheckedIn($component, $asset, Auth::user(), $request->input('note'))); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); diff --git a/app/Http/Controllers/Components/ComponentCheckoutController.php b/app/Http/Controllers/Components/ComponentCheckoutController.php index c7e7fe3db5..bfa9e8967b 100644 --- a/app/Http/Controllers/Components/ComponentCheckoutController.php +++ b/app/Http/Controllers/Components/ComponentCheckoutController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Components; +use App\Events\CheckoutableCheckedOut; use App\Events\ComponentCheckedOut; use App\Http\Controllers\Controller; use App\Models\Asset; @@ -87,7 +88,7 @@ class ComponentCheckoutController extends Controller 'asset_id' => $asset_id ]); - event(new ComponentCheckedOut($component, $asset, $request->input('assigned_qty'), Auth::user())); + event(new CheckoutableCheckedOut($component, $asset, Auth::user(), $request->input('note'))); return redirect()->route('components.index')->with('success', trans('admin/components/message.checkout.success')); } diff --git a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php index 0b3d8c6c91..e3e3e11b03 100644 --- a/app/Http/Controllers/Consumables/ConsumableCheckoutController.php +++ b/app/Http/Controllers/Consumables/ConsumableCheckoutController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Consumables; -use App\Events\ConsumableCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Http\Controllers\Controller; use App\Models\Consumable; use App\Models\User; @@ -68,7 +68,7 @@ class ConsumableCheckoutController extends Controller 'assigned_to' => e(Input::get('assigned_to')) ]); - event(new ConsumableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedOut($consumable, $user, Auth::user(), $request->input('note'))); // Redirect to the new consumable page return redirect()->route('consumables.index')->with('success', trans('admin/consumables/message.checkout.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 9612028465..4bddbd5753 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers\Licenses; -use App\Events\LicenseCheckedIn; +use App\Events\CheckoutableCheckedIn; use App\Models\Asset; use App\Models\License; use App\Models\LicenseSeat; @@ -91,7 +91,7 @@ class LicenseCheckinController extends Controller // Was the asset updated? if ($licenseSeat->save()) { - event(new LicenseCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); + event(new CheckoutableCheckedIn($license, $return_to, Auth::user(), $request->input('note'))); if ($backTo=='user') { return redirect()->route("users.show", $return_to->id)->with('success', trans('admin/licenses/message.checkin.success')); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index a1a02c823b..5d878a1506 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -2,17 +2,16 @@ namespace App\Http\Controllers\Licenses; -use App\Events\LicenseCheckedOut; -use App\Events\LicenseSeatCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Http\Requests\LicenseCheckoutRequest; use App\Models\Asset; use App\Models\License; use App\Models\LicenseSeat; use App\Models\User; use Illuminate\Http\Request; -use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; +use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Validator; class LicenseCheckoutController extends Controller @@ -106,7 +105,7 @@ class LicenseCheckoutController extends Controller } if ($licenseSeat->save()) { - event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } @@ -123,7 +122,7 @@ class LicenseCheckoutController extends Controller if ($licenseSeat->save()) { - event(new LicenseSeatCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); + event(new CheckoutableCheckedOut($licenseSeat, $target, Auth::user(), request('note'))); return true; } diff --git a/app/Listeners/CheckoutableListener.php b/app/Listeners/CheckoutableListener.php new file mode 100644 index 0000000000..e06c6e1e40 --- /dev/null +++ b/app/Listeners/CheckoutableListener.php @@ -0,0 +1,184 @@ +checkedOutTo instanceof User) { + return; + } + + /** + * Make a checkout acceptance and attach it in the notification + */ + $acceptance = $this->getCheckoutAcceptance($event); + + Notification::send( + $this->getNotifiables($event), + $this->getCheckoutNotification($event, $acceptance) + ); + } + + /** + * Notify the user about the checked in checkoutable + */ + public function onCheckedIn($event) { + /** + * When the item wasn't checked out to a user, we can't send notifications + */ + if(!$event->checkedOutTo instanceof User) { + return; + } + + /** + * Send the appropriate notification + */ + Notification::send( + $this->getNotifiables($event), + $this->getCheckinNotification($event) + ); + } + + /** + * Generates a checkout acceptance + * @param Event $event + * @return mixed + */ + private function getCheckoutAcceptance($event) { + if (!$event->checkoutable->requireAcceptance()) { + return null; + } + + $acceptance = new CheckoutAcceptance; + $acceptance->checkoutable()->associate($event->checkoutable); + $acceptance->assignedTo()->associate($event->checkedOutTo); + $acceptance->save(); + + return $acceptance; + } + + /** + * Gets the entities to be notified of the passed event + * + * @param Event $event + * @return Collection + */ + private function getNotifiables($event) { + $notifiables = collect(); + + /** + * Notify the user who checked out the item + */ + $notifiables->push($event->checkedOutTo); + + /** + * Notify Admin users if the settings is activated + */ + if (Setting::getSettings()->admin_cc_email != '') { + $notifiables->push(new AdminRecipient()); + } + + return $notifiables; + } + + /** + * Get the appropriate notification for the event + * + * @param CheckoutableCheckedIn $event + * @return Notification + */ + private function getCheckinNotification($event) { + + $model = get_class($event->checkoutable); + + $notificationClass = null; + + switch (get_class($event->checkoutable)) { + case Accessory::class: + $notificationClass = CheckinAccessoryNotification::class; + break; + case Asset::class: + $notificationClass = CheckinAssetNotification::class; + break; + case LicenseSeat::class: + $notificationClass = CheckinLicenseSeatNotification::class; + break; + } + + return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note); + } + + /** + * Get the appropriate notification for the event + * + * @param CheckoutableCheckedIn $event + * @param CheckoutAcceptance $acceptance + * @return Notification + */ + private function getCheckoutNotification($event, $acceptance) { + $notificationClass = null; + + switch (get_class($event->checkoutable)) { + case Accessory::class: + $notificationClass = CheckoutAccessoryNotification::class; + break; + case Asset::class: + $notificationClass = CheckoutAssetNotification::class; + break; + case Consumable::class: + $notificationClass = CheckoutConsumableNotification::class; + break; + case LicenseSeat::class: + $notificationClass = CheckoutLicenseSeatNotification::class; + break; + } + + return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note); + } + + /** + * Register the listeners for the subscriber. + * + * @param Illuminate\Events\Dispatcher $events + */ + public function subscribe($events) + { + $events->listen( + 'App\Events\CheckoutableCheckedIn', + 'App\Listeners\CheckoutableListener@onCheckedIn' + ); + + $events->listen( + 'App\Events\CheckoutableCheckedOut', + 'App\Listeners\CheckoutableListener@onCheckedOut' + ); + } + +} \ No newline at end of file diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 9cd8ec2717..cdb4fda1fc 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -8,6 +8,8 @@ use App\Events\AssetCheckedIn; use App\Events\AssetCheckedOut; use App\Events\CheckoutAccepted; use App\Events\CheckoutDeclined; +use App\Events\CheckoutableCheckedIn; +use App\Events\CheckoutableCheckedOut; use App\Events\ComponentCheckedIn; use App\Events\ComponentCheckedOut; use App\Events\ConsumableCheckedOut; @@ -23,51 +25,15 @@ use App\Models\LicenseSeat; class LogListener { - public function onAccessoryCheckedIn(AccessoryCheckedIn $event) { - $event->accessory->logCheckin($event->checkedOutTo, $event->note); + + public function onCheckoutableCheckedIn(CheckoutableCheckedIn $event) { + $event->checkoutable->logCheckin($event->checkedOutTo, $event->note); } - public function onAccessoryCheckedOut(AccessoryCheckedOut $event) { - $event->accessory->logCheckout($event->note, $event->checkedOutTo); + public function onCheckoutableCheckedOut(CheckoutableCheckedOut $event) { + $event->checkoutable->logCheckout($event->note, $event->checkedOutTo); } - public function onAssetCheckedIn(AssetCheckedIn $event) { - $event->asset->logCheckin($event->checkedOutTo, $event->note); - } - - public function onAssetCheckedOut(AssetCheckedOut $event) { - $event->asset->logCheckout($event->note, $event->checkedOutTo); - } - - public function onComponentCheckedIn(ComponentCheckedIn $event) { - $log = new Actionlog(); - $log->user_id = $event->checkedInBy->id; - $log->action_type = 'checkin from'; - $log->target_type = Asset::class; - $log->target_id = $event->checkedOutTo->asset_id; - $log->item_id = $event->checkedOutTo->component_id; - $log->item_type = Component::class; - $log->note = $event->note; - $log->save(); - } - - public function onComponentCheckedOut(ComponentCheckedOut $event) { - // Since components don't have a "note" field, submit empty note - $event->component->logCheckout(null, $event->checkedOutTo); - } - - public function onConsumableCheckedOut(ConsumableCheckedOut $event) { - $event->consumable->logCheckout($event->note, $event->checkedOutTo); - } - - public function onLicenseCheckedIn(LicenseCheckedIn $event) { - $event->license->logCheckin($event->checkedOutTo, $event->note); - } - - public function onLicenseCheckedOut(LicenseCheckedOut $event) { - $event->license->logCheckout($event->note, $event->checkedOutTo); - } - public function onCheckoutAccepted(CheckoutAccepted $event) { $logaction = new Actionlog(); @@ -107,15 +73,8 @@ class LogListener public function subscribe($events) { $list = [ - 'AccessoryCheckedIn', - 'AccessoryCheckedOut', - 'AssetCheckedIn', - 'AssetCheckedOut', - 'ComponentCheckedIn', - 'ComponentCheckedOut', - 'ConsumableCheckedOut', - 'LicenseCheckedIn', - 'LicenseCheckedOut', + 'CheckoutableCheckedIn', + 'CheckoutableCheckedOut', 'CheckoutAccepted', 'CheckoutDeclined', ]; diff --git a/app/Listeners/SendingCheckInNotificationsListener.php b/app/Listeners/SendingCheckInNotificationsListener.php deleted file mode 100644 index aa176e19fd..0000000000 --- a/app/Listeners/SendingCheckInNotificationsListener.php +++ /dev/null @@ -1,117 +0,0 @@ -checkedOutTo instanceof User) { - return; - } - - Notification::send( - $this->getNotifiables($event), - new CheckinAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedInBy, $event->note) - ); - } - - /** - * Notify the user about the checked in asset - */ - public function onAssetCheckedIn($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - Notification::send( - $this->getNotifiables($event), - new CheckinAssetNotification($event->asset, $event->checkedOutTo, $event->checkedInBy, $event->note) - ); - } - - /** - * Notify the user about the checked in license - */ - public function onLicenseSeatCheckedIn($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - Notification::send( - $this->getNotifiables($event), - new CheckinLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedInBy, $event->note) - ); - } - - /** - * Gets the entities to be notified of the passed event - * - * @param Event $event - * @return Collection - */ - private function getNotifiables($event) { - $notifiables = collect(); - - /** - * Notify the user who checked out the item - */ - $notifiables->push($event->checkedOutTo); - - /** - * Notify Admin users if the settings is activated - */ - if (Setting::getSettings()->admin_cc_email != '') { - $notifiables->push(new AdminRecipient()); - } - - return $notifiables; - } - - /** - * Register the listeners for the subscriber. - * - * @param Illuminate\Events\Dispatcher $events - */ - public function subscribe($events) - { - $events->listen( - 'App\Events\AccessoryCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onAccessoryCheckedIn' - ); - - $events->listen( - 'App\Events\AssetCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onAssetCheckedIn' - ); - - $events->listen( - 'App\Events\LicenseSeatCheckedIn', - 'App\Listeners\SendingCheckInNotificationsListener@onLicenseSeatCheckedIn' - ); - } - -} \ No newline at end of file diff --git a/app/Listeners/SendingCheckOutNotificationsListener.php b/app/Listeners/SendingCheckOutNotificationsListener.php deleted file mode 100644 index ed459c5a59..0000000000 --- a/app/Listeners/SendingCheckOutNotificationsListener.php +++ /dev/null @@ -1,182 +0,0 @@ -checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->consumable->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->consumable); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutConsumableNotification($event->consumable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Notify the user about the checked out accessory - */ - public function onAccessoryCheckedOut($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->accessory->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->accessory); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutAccessoryNotification($event->accessory, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Notify the user about the checked out license - */ - public function onLicenseSeatCheckedOut($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->licenseSeat->license->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->licenseSeat); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutLicenseSeatNotification($event->licenseSeat, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Notify the user about the checked out asset - */ - public function onAssetCheckedOut($event) { - /** - * When the item wasn't checked out to a user, we can't send notifications - */ - if(! $event->checkedOutTo instanceof User) { - return; - } - - /** - * Make a checkout acceptance and attach it in the notification - */ - $acceptance = null; - if ($event->asset->requireAcceptance()) { - $acceptance = new CheckoutAcceptance; - $acceptance->checkoutable()->associate($event->asset); - $acceptance->assignedTo()->associate($event->checkedOutTo); - $acceptance->save(); - } - - Notification::send( - $this->getNotifiables($event), - new CheckoutAssetNotification($event->asset, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note) - ); - } - - /** - * Gets the entities to be notified of the passed event - * - * @param Event $event - * @return Collection - */ - private function getNotifiables($event) { - $notifiables = collect(); - - /** - * Notify the user who checked out the item - */ - $notifiables->push($event->checkedOutTo); - - /** - * Notify Admin users if the settings is activated - */ - if (Setting::getSettings()->admin_cc_email != '') { - $notifiables->push(new AdminRecipient()); - } - - return $notifiables; - } - - /** - * Register the listeners for the subscriber. - * - * @param Illuminate\Events\Dispatcher $events - */ - public function subscribe($events) - { - $events->listen( - 'App\Events\ConsumableCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onConsumableCheckedOut' - ); - - $events->listen( - 'App\Events\AccessoryCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onAccessoryCheckedOut' - ); - - $events->listen( - 'App\Events\LicenseSeatCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onLicenseSeatCheckedOut' - ); - - $events->listen( - 'App\Events\AssetCheckedOut', - 'App\Listeners\SendingCheckOutNotificationsListener@onAssetCheckedOut' - ); - } - -} \ No newline at end of file diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 35206b41ea..ceb6ee6962 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -2,6 +2,7 @@ namespace App\Models; use App\Events\AssetCheckedOut; +use App\Events\CheckoutableCheckedOut; use App\Exceptions\CheckoutNotAllowed; use App\Http\Traits\UniqueSerialTrait; use App\Http\Traits\UniqueUndeletedTrait; @@ -265,7 +266,7 @@ class Asset extends Depreciable if ($this->save()) { - event(new AssetCheckedOut($this, $target, Auth::user(), $note)); + event(new CheckoutableCheckedOut($this, $target, Auth::user(), $note)); $this->increment('checkout_counter', 1); return true; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index df3c643b24..28c3aecf06 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,9 +3,8 @@ namespace App\Providers; use Illuminate\Support\Facades\Event; +use App\Listeners\CheckoutableListener; use App\Listeners\LogListener; -use App\Listeners\SendingCheckInNotificationsListener; -use App\Listeners\SendingCheckOutNotificationsListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider @@ -32,9 +31,8 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $subscribe = [ - SendingCheckOutNotificationsListener::class, - SendingCheckInNotificationsListener::class, - LogListener::class + LogListener::class, + CheckoutableListener::class ]; /** From 86f49d34c3b7b5d7c2425ff5dc48d1c9b8a09987 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 17:13:16 +0200 Subject: [PATCH 22/24] Redirects users from old acceptance screen to new overview --- app/Http/Controllers/ViewAssetsController.php | 120 +----------------- routes/web.php | 4 - 2 files changed, 1 insertion(+), 123 deletions(-) diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 2308b804a9..8022815bad 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -199,124 +199,6 @@ class ViewAssetsController extends Controller // Get the acceptance screen public function getAcceptAsset($logID = null) { - - $findlog = Actionlog::where('id', $logID)->first(); - - if (!$findlog) { - return redirect()->to('account/view-assets')->with('error', 'No matching record.'); - } - - if ($findlog->accepted_id!='') { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.asset_already_accepted')); - } - - $user = Auth::user(); - - - // TODO - Fix this for non-assets - if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); - } - - - $item = $findlog->item; - - // Check if the asset exists - if (is_null($item)) { - // Redirect to the asset management page - return redirect()->to('account')->with('error', trans('admin/hardware/message.does_not_exist')); - } - if (!Company::isCurrentUserHasAccess($item)) { - return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')); - } - return view('account/accept-asset', compact('item'))->with('findlog', $findlog)->with('item', $item); - } - - // Save the acceptance - public function postAcceptAsset(Request $request, $logID = null) - { - - // Check if the asset exists - if (is_null($findlog = Actionlog::where('id', $logID)->first())) { - // Redirect to the asset management page - return redirect()->to('account/view-assets')->with('error', trans('admin/hardware/message.does_not_exist')); - } - - - if ($findlog->accepted_id!='') { - // Redirect to the asset management page - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.asset_already_accepted')); - } - - if (!Input::has('asset_acceptance')) { - return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); - } - - $user = Auth::user(); - - if (($findlog->item_type==Asset::class) && ($user->id != $findlog->item->assigned_to)) { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); - } - - if ($request->filled('signature_output')) { - $path = config('app.private_uploads').'/signatures'; - $sig_filename = "siglog-".$findlog->id.'-'.date('Y-m-d-his').".png"; - $data_uri = e($request->get('signature_output')); - $encoded_image = explode(",", $data_uri); - $decoded_image = base64_decode($encoded_image[1]); - file_put_contents($path."/".$sig_filename, $decoded_image); - } - - - $logaction = new Actionlog(); - - if (Input::get('asset_acceptance')=='accepted') { - $logaction_msg = 'accepted'; - $accepted="accepted"; - $return_msg = trans('admin/users/message.accepted'); - } else { - $logaction_msg = 'declined'; - $accepted="rejected"; - $return_msg = trans('admin/users/message.declined'); - } - $logaction->item_id = $findlog->item_id; - $logaction->item_type = $findlog->item_type; - - // Asset - if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) { - if (Input::get('asset_acceptance')!='accepted') { - DB::table('assets') - ->where('id', $findlog->item_id) - ->update(array('assigned_to' => null)); - } - } - - $logaction->target_id = $findlog->target_id; - $logaction->target_type = User::class; - $logaction->note = e(Input::get('note')); - $logaction->updated_at = date("Y-m-d H:i:s"); - - - if (isset($sig_filename)) { - $logaction->accept_signature = $sig_filename; - } - $log = $logaction->logaction($logaction_msg); - - $update_checkout = DB::table('action_logs') - ->where('id', $findlog->id) - ->update(array('accepted_id' => $logaction->id)); - - if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) { - $affected_asset = $logaction->item; - $affected_asset->accepted = $accepted; - $affected_asset->save(); - } - - if ($update_checkout) { - return redirect()->to('account/view-assets')->with('success', $return_msg); - - } - return redirect()->to('account/view-assets')->with('error', 'Something went wrong '); - + return redirect()->route('account.accept'); } } diff --git a/routes/web.php b/routes/web.php index eae9209345..f59362c767 100644 --- a/routes/web.php +++ b/routes/web.php @@ -251,10 +251,6 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { 'accept-asset/{logID}', [ 'as' => 'account/accept-assets', 'uses' => 'ViewAssetsController@getAcceptAsset' ] ); - Route::post( - 'accept-asset/{logID}', - [ 'as' => 'account/asset-accepted', 'uses' => 'ViewAssetsController@postAcceptAsset' ] - ); # Profile Route::get( From bbd1d6059a9e9a46410ad9c51fe58ebb209611e8 Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 17:16:07 +0200 Subject: [PATCH 23/24] Fixes errors in tests --- .../2018_07_28_023826_create_checkout_acceptances_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php index 6d1f1f2b3a..67bc3b800a 100644 --- a/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php +++ b/database/migrations/2018_07_28_023826_create_checkout_acceptances_table.php @@ -19,7 +19,7 @@ class CreateCheckoutAcceptancesTable extends Migration $table->morphs('checkoutable'); $table->integer('assigned_to_id')->unsigned(); - $table->string('signature_filename'); + $table->string('signature_filename')->nullable(); $table->timestamp('accepted_at')->nullable(); $table->timestamp('declined_at')->nullable(); From e445e201f312ce8c379532ec03a241a1d161e87a Mon Sep 17 00:00:00 2001 From: Till Deeke Date: Mon, 10 Sep 2018 17:37:24 +0200 Subject: [PATCH 24/24] Adds a migration to create checkout acceptances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This creates the checkout acceptances for assets (basically what was previously shown in the „Unaccepted Assets“-report) when migrating the database --- ...kout_acceptances_for_unaccepted_assets.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php diff --git a/database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php b/database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php new file mode 100644 index 0000000000..ddf6949b69 --- /dev/null +++ b/database/migrations/2018_09_10_082212_create_checkout_acceptances_for_unaccepted_assets.php @@ -0,0 +1,43 @@ +where('assigned_type', 'App\Models\User')->where('accepted', 'pending')->get(); + + $acceptances = []; + + foreach($assets as $asset) { + $acceptances[] = [ + 'checkoutable_type' => 'App\Models\Asset', + 'checkoutable_id' => $asset->id, + 'assigned_to_id' => $asset->assigned_to, + ]; + } + + DB::table('checkout_acceptances')->insert($acceptances); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +}