From d1a492f953dfbb744f69fa1d9b77b64277875904 Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 12 Nov 2025 15:38:47 +0000 Subject: [PATCH] Improved overdue checkin alert --- .../Commands/SendExpectedCheckinAlerts.php | 44 ++++++++++++++++--- app/Models/Asset.php | 7 +++ .../ExpectedCheckinNotification.php | 6 ++- app/Presenters/AssetPresenter.php | 2 +- resources/lang/en-US/mail.php | 4 +- .../markdown/expected-checkin.blade.php | 13 ++++-- 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/app/Console/Commands/SendExpectedCheckinAlerts.php b/app/Console/Commands/SendExpectedCheckinAlerts.php index e042e8b088..3c83110b7a 100644 --- a/app/Console/Commands/SendExpectedCheckinAlerts.php +++ b/app/Console/Commands/SendExpectedCheckinAlerts.php @@ -9,6 +9,7 @@ use App\Notifications\ExpectedCheckinAdminNotification; use App\Notifications\ExpectedCheckinNotification; use Carbon\Carbon; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Notification; class SendExpectedCheckinAlerts extends Command { @@ -17,7 +18,7 @@ class SendExpectedCheckinAlerts extends Command * * @var string */ - protected $name = 'snipeit:expected-checkin'; + protected $signature = 'snipeit:expected-checkin {--with-output : Display the results in a table in your console in addition to sending the email}'; /** * The console command description. @@ -45,16 +46,44 @@ class SendExpectedCheckinAlerts extends Command $interval = $settings->audit_warning_days ?? 0; $today = Carbon::now(); $interval_date = $today->copy()->addDays($interval); - + $count = 0; + + if (!$this->option('with-output')) { + $this->info('Run this command with the --with-output option to see the full list in the console.'); + } + $assets = Asset::whereNull('deleted_at')->DueOrOverdueForCheckin($settings)->orderBy('assets.expected_checkin', 'desc')->get(); - $this->info($assets->count().' assets must be checked in on or before '.$interval_date.' is deadline'); + $this->info($assets->count().' assets must be checked on or before '.$interval_date); foreach ($assets as $asset) { if ($asset->assignedTo && (isset($asset->assignedTo->email)) && ($asset->assignedTo->email!='') && $asset->checkedOutToUser()) { - $this->info('Sending User ExpectedCheckinNotification to: '.$asset->assignedTo->email); $asset->assignedTo->notify((new ExpectedCheckinNotification($asset))); + $count++; + } + } + + if ($this->option('with-output')) { + if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) { + $this->table( + [ + trans('general.id'), + trans('admin/hardware/form.tag'), + trans('admin/hardware/form.model'), + trans('general.model_no'), + trans('general.purchase_date'), + trans('admin/hardware/form.expected_checkin'), + ], + $assets->map(fn($assets) => [ + trans('general.id') => $assets->id, + trans('admin/hardware/form.tag') => $assets->asset_tag, + trans('admin/hardware/form.model') => $assets->model->name, + trans('general.model_no') => $assets->model->model_number, + trans('general.purchase_date') => $assets->purchase_date_formatted, + trans('admin/hardware/form.eol_date') => $assets->expected_checkin_formattedDate ? $assets->expected_checkin_formattedDate . ' (' . $assets->expected_checkin_diff_for_humans . ')' : '', + ]) + ); } } @@ -63,10 +92,11 @@ class SendExpectedCheckinAlerts extends Command $recipients = collect(explode(',', $settings->alert_email))->map(function ($item) { return new AlertRecipient($item); }); - - $this->info('Sending Admin ExpectedCheckinNotification to: '.$settings->alert_email); - \Notification::send($recipients, new ExpectedCheckinAdminNotification($assets)); + Notification::send($recipients, new ExpectedCheckinAdminNotification($assets)); } + + $this->info('Sent checkin reminders to to '.$count.' users.'); + } } diff --git a/app/Models/Asset.php b/app/Models/Asset.php index d588690ba4..46385c2330 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -408,6 +408,13 @@ class Asset extends Depreciable ); } + protected function expectedCheckinDiffForHumans(): Attribute + { + return Attribute:: make( + get: fn(mixed $value, array $attributes) => array_key_exists('expected_checkin', $attributes) ? Carbon::parse($this->expected_checkin)->diffForHumans() : null, + ); + } + /** * Establishes the asset -> company relationship * diff --git a/app/Notifications/ExpectedCheckinNotification.php b/app/Notifications/ExpectedCheckinNotification.php index cf8d71f131..3d7ec54d15 100644 --- a/app/Notifications/ExpectedCheckinNotification.php +++ b/app/Notifications/ExpectedCheckinNotification.php @@ -3,6 +3,7 @@ namespace App\Notifications; use App\Helpers\Helper; +use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -47,14 +48,17 @@ class ExpectedCheckinNotification extends Notification */ public function toMail() { + $today = Carbon::now(); + $message = (new MailMessage)->markdown('notifications.markdown.expected-checkin', [ + 'expected_checkin_date' => $this->params->expected_checkin, 'date' => Helper::getFormattedDateObject($this->params->expected_checkin, 'date', false), 'asset' => $this->params->display_name, 'serial' => $this->params->serial, 'asset_tag' => $this->params->asset_tag, ]) - ->subject(trans('mail.Expected_Checkin_Notification', ['name' => $this->params->display_name])); + ->subject(($today > $this->params->expected_checkin) ? trans('mail.Expected_Checkin_Notification_Pastdue', ['name' => $this->params->display_name]) : trans('mail.Expected_Checkin_Notification', ['name' => $this->params->display_name])); return $message; } diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php index 6f467644fd..bf3b5ab73f 100644 --- a/app/Presenters/AssetPresenter.php +++ b/app/Presenters/AssetPresenter.php @@ -519,7 +519,7 @@ class AssetPresenter extends Presenter // Asset tag if ($this->asset_tag) { - $str .= ' ('.$this->model->asset_tag.')'; + $str .= ' #'.$this->model->asset_tag; } // Asset Model name diff --git a/resources/lang/en-US/mail.php b/resources/lang/en-US/mail.php index f9e9b38cba..4f338bf8dd 100644 --- a/resources/lang/en-US/mail.php +++ b/resources/lang/en-US/mail.php @@ -18,8 +18,10 @@ return [ 'Component_checkout_notification' => 'Component checked out', 'Component_checkin_notification' => 'Component checked in', 'Days' => 'Days', - 'Expected_Checkin_Date' => 'An asset checked out to you is due to be checked back in on :date', + 'Expected_Checkin_Date' => 'An asset checked out to you is due to be checked back in on :date.', + 'Expected_Checkin_Date_Past' => 'An asset checked out to you was due to be checked back in on :date.', 'Expected_Checkin_Notification' => 'Reminder: :name checkin deadline approaching', + 'Expected_Checkin_Notification_Pastdue' => 'OVERDUE: :name is overdue for checkin', 'Expected_Checkin_Report' => 'Expected asset checkin report', 'Expiring_Assets_Report' => 'Expiring Assets Report', 'Expiring_Licenses_Report' => 'Expiring Licenses Report', diff --git a/resources/views/notifications/markdown/expected-checkin.blade.php b/resources/views/notifications/markdown/expected-checkin.blade.php index 182f88ec1e..89bac39bcc 100644 --- a/resources/views/notifications/markdown/expected-checkin.blade.php +++ b/resources/views/notifications/markdown/expected-checkin.blade.php @@ -1,14 +1,21 @@ @component('mail::message') # {{ trans('mail.hello') }}, +@if ($expected_checkin_date > now()) {{ trans('mail.Expected_Checkin_Date', ['date' => $date]) }} +@else +{{ trans('mail.Expected_Checkin_Date_Past', ['date' => $date]) }} +@endif @if ((isset($asset)) && ($asset!='')) -{{ trans('mail.asset_name') }} {{ $asset }} +{{ trans('mail.asset_name') }}: {{ $asset }} + @endif -{{ trans('mail.asset_tag') }} {{ $asset_tag }} +{{ trans('mail.asset_tag') }}: {{ $asset_tag }} + @if (isset($serial)) -{{ trans('mail.serial') }}: {{ $serial }} +{{ trans('mail.serial') }}: {{ $serial }} + @endif **[{{ trans('mail.your_assets') }}]({{ route('view-assets') }})**