Skip to content

Commit 00af0dd

Browse files
authored
Merge pull request #17982 from grokability/expiring-alerts-improvements
Small expiring alerts improvements
2 parents 6cf88b1 + 2467e82 commit 00af0dd

File tree

16 files changed

+1100
-911
lines changed

16 files changed

+1100
-911
lines changed

app/Console/Commands/SendExpirationAlerts.php

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,57 @@ public function handle()
6060
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $alert_interval));
6161

6262
$this->table(
63-
['ID', 'Tag', 'Model', 'Model Number', 'EOL', 'EOL Months', 'Warranty Expires', 'Warranty Months'],
64-
$assets->map(fn($item) => ['ID' => $item->id, 'Tag' => $item->asset_tag, 'Model' => $item->model->name, 'Model Number' => $item->model->model_number, 'EOL' => $item->asset_eol_date, 'EOL Months' => $item->model->eol, 'Warranty Expires' => $item->warranty_expires, 'Warranty Months' => $item->warranty_months])
65-
);
63+
[
64+
trans('general.id'),
65+
trans('admin/hardware/form.tag'),
66+
trans('admin/hardware/form.model'),
67+
trans('general.model_no'),
68+
trans('general.purchase_date'),
69+
trans('admin/hardware/form.eol_rate'),
70+
trans('admin/hardware/form.eol_date'),
71+
trans('admin/hardware/form.warranty_expires'),
72+
],
73+
$assets->map(fn($item) =>
74+
[
75+
trans('general.id') => $item->id,
76+
trans('admin/hardware/form.tag') => $item->asset_tag,
77+
trans('admin/hardware/form.model') => $item->model->name,
78+
trans('general.model_no') => $item->model->model_number,
79+
trans('general.purchase_date') => $item->purchase_date_formatted,
80+
trans('admin/hardware/form.eol_rate') => $item->model->eol,
81+
trans('admin/hardware/form.eol_date') => $item->eol_date ? $item->eol_formatted_date .' ('.$item->eol_diff_for_humans.')' : '',
82+
trans('admin/hardware/form.warranty_expires') => $item->warranty_expires ? $item->warranty_expires_formatted_date .' ('.$item->warranty_expires_diff_for_humans.')' : '',
83+
])
84+
);
6685
}
6786

6887
// Expiring licenses
69-
$licenses = License::getExpiringLicenses($alert_interval);
88+
$licenses = License::query()->ExpiringLicenses($alert_interval)
89+
->with('manufacturer','category')
90+
->orderBy('expiration_date', 'ASC')
91+
->orderBy('termination_date', 'ASC')
92+
->get();
7093
if ($licenses->count() > 0) {
7194
Mail::to($recipients)->send(new ExpiringLicenseMail($licenses, $alert_interval));
7295

7396
$this->table(
74-
['ID', 'Name', 'Expires', 'Termination Date'],
75-
$licenses->map(fn($item) => ['ID' => $item->id, 'Name' => $item->name, 'Expires' => $item->expiration_date, 'Termination Date' => $item->termination_date])
97+
[
98+
trans('general.id'),
99+
trans('general.name'),
100+
trans('general.purchase_date'),
101+
trans('admin/licenses/form.expiration'),
102+
trans('mail.expires'),
103+
trans('admin/licenses/form.termination_date'),
104+
trans('mail.terminates')],
105+
$licenses->map(fn($item) => [
106+
trans('general.id') => $item->id,
107+
trans('general.name') => $item->name,
108+
trans('general.purchase_date') => $item->purchase_date_formatted,
109+
trans('admin/licenses/form.expiration') => $item->expires_formatted_date,
110+
trans('mail.expires') => $item->expires_diff_for_humans,
111+
trans('admin/licenses/form.termination_date') => $item->terminates_formatted_date,
112+
trans('mail.terminates') => $item->terminates_diff_for_humans
113+
])
76114
);
77115
}
78116

app/Http/Controllers/Api/LicensesController.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Http\Transformers\LicensesTransformer;
88
use App\Http\Transformers\SelectlistTransformer;
99
use App\Models\License;
10+
use App\Models\Setting;
1011
use Illuminate\Http\Request;
1112
use Illuminate\Support\Facades\DB;
1213
use Illuminate\Http\JsonResponse;
@@ -25,9 +26,12 @@ public function index(Request $request) : JsonResponse | array
2526
$this->authorize('view', License::class);
2627

2728
$licenses = License::with('company', 'manufacturer', 'supplier','category', 'adminuser')->withCount('freeSeats as free_seats_count');
29+
$settings = Setting::getSettings();
2830

2931
if ($request->input('status')=='inactive') {
3032
$licenses->ExpiredLicenses();
33+
} elseif ($request->input('status')=='expiring') {
34+
$licenses->ExpiringLicenses($settings->alert_interval);
3135
} else {
3236
$licenses->ActiveLicenses();
3337
}

app/Models/Asset.php

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ public function setExpectedCheckinAttribute($value)
227227
}
228228

229229

230-
231230
public function customFieldValidationRules()
232231
{
233232

@@ -266,7 +265,6 @@ public function save(array $params = [])
266265
return parent::save($params);
267266
}
268267

269-
270268
public function getDisplayNameAttribute()
271269
{
272270
return $this->present()->name();
@@ -277,20 +275,79 @@ public function getDisplayNameAttribute()
277275
*
278276
* @return \Carbon\Carbon|null
279277
*/
280-
public function getWarrantyExpiresAttribute()
278+
279+
280+
protected function warrantyExpires(): Attribute
281281
{
282-
if (isset($this->attributes['warranty_months']) && isset($this->attributes['purchase_date'])) {
283-
if (is_string($this->attributes['purchase_date']) || is_string($this->attributes['purchase_date'])) {
284-
$purchase_date = \Carbon\Carbon::parse($this->attributes['purchase_date']);
285-
} else {
286-
$purchase_date = \Carbon\Carbon::instance($this->attributes['purchase_date']);
282+
return Attribute:: make(
283+
get: fn(mixed $value, array $attributes) => ($attributes['warranty_months'] && $attributes['purchase_date']) ? Carbon::parse($attributes['purchase_date'])->addMonths($attributes['warranty_months']) : null,
284+
);
285+
}
286+
287+
protected function warrantyExpiresFormattedDate(): Attribute
288+
{
289+
290+
return Attribute:: make(
291+
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->warrantyExpires, 'date', false)
292+
);
293+
}
294+
295+
protected function warrantyExpiresDiff(): Attribute
296+
{
297+
return Attribute:: make(
298+
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? round((Carbon::now()->diffInDays($this->warrantyExpires))) : null,
299+
);
300+
301+
}
302+
303+
protected function warrantyExpiresDiffForHumans(): Attribute
304+
{
305+
return Attribute:: make(
306+
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? Carbon::parse($this->warrantyExpires)->diffForHumans() : null,
307+
);
308+
309+
}
310+
311+
protected function eolDate(): Attribute
312+
{
313+
314+
return Attribute:: make(
315+
get: function(mixed $value, array $attributes) {
316+
if ($attributes['asset_eol_date'] && $attributes['eol_explicit'] == '1') {
317+
return Carbon::parse($attributes['asset_eol_date']);
318+
} elseif ($attributes['purchase_date'] && $this->model && ((int) $this->model->eol > 0)) {
319+
return Carbon::parse($attributes['purchase_date'])->addMonths((int) $this->model->eol);
320+
} else {
321+
return null;
322+
}
287323
}
288-
$purchase_date->setTime(0, 0, 0);
324+
);
289325

290-
return $purchase_date->addMonths((int) $this->attributes['warranty_months']);
291-
}
326+
}
327+
328+
329+
protected function eolFormattedDate(): Attribute
330+
{
331+
return Attribute:: make(
332+
get: fn(mixed $value, array $attributes) => $this->eolDate ? Helper::getFormattedDateObject($this->eolDate, 'date', false) : null,
333+
);
334+
}
335+
336+
protected function eolDiffInDays(): Attribute
337+
{
338+
return Attribute:: make(
339+
get: fn(mixed $value, array $attributes) => $this->eolDate ? round((Carbon::now()->diffInDays(Carbon::parse($this->eolDate), false, 1))) : null,
340+
);
341+
342+
}
343+
344+
protected function eolDiffForHumans(): Attribute
345+
{
346+
347+
return Attribute:: make(
348+
get: fn(mixed $value, array $attributes) => $this->eolDate ? Carbon::parse($this->eolDate)->diffForHumans() : null,
349+
);
292350

293-
return null;
294351
}
295352

296353

app/Models/License.php

Lines changed: 66 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\Models\Traits\Searchable;
99
use App\Presenters\Presentable;
1010
use Carbon\Carbon;
11+
use Illuminate\Database\Eloquent\Casts\Attribute;
1112
use Illuminate\Database\Eloquent\Factories\HasFactory;
1213
use Illuminate\Database\Eloquent\SoftDeletes;
1314
use Illuminate\Support\Facades\DB;
@@ -156,6 +157,29 @@ function ($license) {
156157
);
157158
}
158159

160+
161+
protected function terminatesFormattedDate(): Attribute
162+
{
163+
return Attribute:: make(
164+
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Helper::getFormattedDateObject($attributes['termination_date'], 'date', false) : null,
165+
);
166+
}
167+
168+
protected function terminatesDiffInDays(): Attribute
169+
{
170+
return Attribute:: make(
171+
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Carbon::now()->diffInDays($attributes['termination_date']) : null,
172+
);
173+
}
174+
175+
protected function terminatesDiffForHumans(): Attribute
176+
{
177+
return Attribute:: make(
178+
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Carbon::parse($attributes['termination_date'])->diffForHumans() : null,
179+
);
180+
}
181+
182+
159183
public function prepareLimitChangeRule($parameters, $field)
160184
{
161185
$actual_seat_count = $this->licenseseats()->count(); //we use the *actual* seat count here, in case your license has gone wonky
@@ -706,8 +730,41 @@ public function freeSeats()
706730
return $this->hasMany(\App\Models\LicenseSeat::class)->whereNull('assigned_to')->whereNull('deleted_at')->whereNull('asset_id');
707731
}
708732

733+
public function scopeActiveLicenses($query)
734+
{
735+
736+
return $query->whereNull('licenses.deleted_at')
737+
738+
// The termination date is null or within range
739+
->where(function ($query) {
740+
$query->whereNull('termination_date')
741+
->orWhereDate('termination_date', '>', [Carbon::now()]);
742+
})
743+
->where(function ($query) {
744+
$query->whereNull('expiration_date')
745+
->orWhereDate('expiration_date', '>', [Carbon::now()]);
746+
});
747+
}
748+
749+
/**
750+
* Expiried/terminated licenses scope
751+
*
752+
* @author A. Gianotto <[email protected]>
753+
* @since [v1.0]
754+
* @return \Illuminate\Database\Eloquent\Relations\Relation
755+
* @see \App\Console\Commands\SendExpiringLicenseNotifications
756+
*/
757+
public function scopeExpiredLicenses($query)
758+
{
759+
return $query->whereDate('termination_date', '<=', Carbon::now())// The termination date is null or within range
760+
->orWhere(function ($query) {
761+
$query->whereDate('expiration_date', '<=', Carbon::now());
762+
})
763+
->whereNull('deleted_at');
764+
}
765+
709766
/**
710-
* Returns expiring licenses.
767+
* Expiring/terminating licenses scope
711768
*
712769
* This checks if:
713770
*
@@ -721,63 +778,25 @@ public function freeSeats()
721778
* @return \Illuminate\Database\Eloquent\Relations\Relation
722779
* @see \App\Console\Commands\SendExpiringLicenseNotifications
723780
*/
724-
public static function getExpiringLicenses($days = 60)
781+
public function scopeExpiringLicenses($query, $days = 60)
725782
{
726-
727-
return self::whereNull('licenses.deleted_at')
728-
729-
// The termination date is null or within range
730-
->where(function ($query) use ($days) {
731-
$query->whereNull('termination_date')
732-
->orWhereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
733-
})
783+
return $query// The termination date is null or within range
784+
->where(function ($query) use ($days) {
785+
$query->whereNull('termination_date')
786+
->orWhereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
787+
})
734788
->where(function ($query) use ($days) {
735789
$query->whereNotNull('expiration_date')
736-
// Handle expired licenses without termination dates
790+
// Handle expiring licenses without termination dates
737791
->where(function ($query) use ($days) {
738792
$query->whereNull('termination_date')
739793
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)]);
740794
})
741795

742-
// Handle expired licenses with termination dates in the future
796+
// Handle expiring licenses with termination dates in the future
743797
->orWhere(function ($query) use ($days) {
744798
$query->whereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
745799
});
746-
})
747-
->orderBy('expiration_date', 'ASC')
748-
->orderBy('termination_date', 'ASC')
749-
->get();
750-
}
751-
752-
public function scopeActiveLicenses($query)
753-
{
754-
755-
return $query->whereNull('licenses.deleted_at')
756-
757-
// The termination date is null or within range
758-
->where(function ($query) {
759-
$query->whereNull('termination_date')
760-
->orWhereDate('termination_date', '>', [Carbon::now()]);
761-
})
762-
->where(function ($query) {
763-
$query->whereNull('expiration_date')
764-
->orWhereDate('expiration_date', '>', [Carbon::now()]);
765-
});
766-
}
767-
768-
public function scopeExpiredLicenses($query)
769-
{
770-
771-
return $query->whereNull('licenses.deleted_at')
772-
773-
// The termination date is null or within range
774-
->where(function ($query) {
775-
$query->whereNull('termination_date')
776-
->orWhereDate('termination_date', '<=', [Carbon::now()]);
777-
})
778-
->orWhere(function ($query) {
779-
$query->whereNull('expiration_date')
780-
->orWhereDate('expiration_date', '<=', [Carbon::now()]);
781800
});
782801
}
783802

app/Models/SnipeModel.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Models;
44

55
use App\Helpers\Helper;
6+
use Carbon\Carbon;
67
use Illuminate\Database\Eloquent\Builder;
78
use Illuminate\Database\Eloquent\Casts\Attribute;
89
use Illuminate\Database\Eloquent\Model;
@@ -19,6 +20,37 @@ public function setPurchaseDateAttribute($value)
1920
$this->attributes['purchase_date'] = $value;
2021
}
2122

23+
24+
protected function purchaseDateFormatted(): Attribute
25+
{
26+
return Attribute:: make(
27+
get: fn(mixed $value, array $attributes) => $attributes['purchase_date'] ? Helper::getFormattedDateObject(Carbon::parse($attributes['purchase_date']), 'date', false) : null,
28+
);
29+
}
30+
31+
32+
protected function expiresDiffInDays(): Attribute
33+
{
34+
return Attribute:: make(
35+
get: fn(mixed $value, array $attributes) => $attributes['expiration_date'] ? Carbon::now()->diffInDays($attributes['expiration_date']) : null,
36+
);
37+
}
38+
39+
40+
protected function expiresDiffForHumans(): Attribute
41+
{
42+
return Attribute:: make(
43+
get: fn(mixed $value, array $attributes) => $attributes['expiration_date'] ? Carbon::parse($attributes['expiration_date'])->diffForHumans() : null,
44+
);
45+
}
46+
47+
protected function expiresFormattedDate(): Attribute
48+
{
49+
return Attribute:: make(
50+
get: fn(mixed $value, array $attributes) => $attributes['expiration_date'] ? Helper::getFormattedDateObject($attributes['expiration_date'], 'date', false) : null,
51+
);
52+
}
53+
2254
/**
2355
* @param $value
2456
*/
@@ -180,6 +212,7 @@ protected function displayName(): Attribute
180212
);
181213
}
182214

215+
183216
public function getEula()
184217
{
185218

0 commit comments

Comments
 (0)