Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored due/overdue for audit, added due/overdue for checkin API endpoint and GUI #14655

Merged
merged 43 commits into from May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
27bc7a8
Updated routes
snipe Apr 26, 2024
bfc3079
Updated badge styling
snipe Apr 26, 2024
2484a9d
Added tests
snipe Apr 26, 2024
14b6a75
Updated routes
snipe Apr 26, 2024
2e92ee8
Switch to whereBetween so tests run on sqlite
snipe Apr 26, 2024
2888dd6
Added translation
snipe Apr 26, 2024
dfaf01e
Updated asset counters
snipe Apr 26, 2024
bf058bd
Use updated scopes
snipe Apr 26, 2024
4b6d236
Added class for sidebar menu badges
snipe Apr 26, 2024
bd8737d
Changed sidenav badge class
snipe Apr 26, 2024
2d112f2
Call the asset factory directly
snipe Apr 26, 2024
9e44052
Switch to plural route name for API endpoint
snipe Apr 26, 2024
de6d71c
Use plural api endpoints in blades
snipe Apr 26, 2024
fe4dd23
Removed “all” text
snipe Apr 26, 2024
4b4e3ba
Removed hardware audit overdue blade
snipe Apr 26, 2024
6dc9ccf
Refactor api for handling audit/expected checkins
snipe Apr 26, 2024
9167f8a
Cleaned up tests
snipe Apr 26, 2024
651001b
Removed duplicate routes
snipe Apr 26, 2024
fbe9daa
Use pattern in API route
snipe Apr 26, 2024
c661d73
Refactored sidenav links for audit
snipe Apr 26, 2024
9c8411c
Added checkin due blade
snipe Apr 26, 2024
76129e9
Use trans_choice for title
snipe Apr 26, 2024
d40604b
Removed debugging, added date cast
snipe Apr 26, 2024
2117f61
More view sharing for sidebar
snipe Apr 26, 2024
3f04afe
Removed unused method
snipe Apr 26, 2024
8356b57
Added translations
snipe Apr 26, 2024
87de67e
Fixed test for checkin
snipe Apr 26, 2024
52d6a89
Check that the asset is not already checked in
snipe Apr 26, 2024
860764a
Use totals for sidebar
snipe Apr 26, 2024
50d8b02
Removed unused scope
snipe Apr 26, 2024
fc61a4b
Fixed badge HTML
snipe Apr 26, 2024
494ec5c
Added tests for due-or-overdue
snipe Apr 26, 2024
bfd0530
Fixed notification
snipe Apr 26, 2024
839db8e
Refactored due-or-overdue methods
snipe Apr 26, 2024
a398496
Added comments
snipe Apr 26, 2024
103809b
Removed debugging
snipe Apr 26, 2024
fe147ad
Refactor checkin alert
snipe Apr 26, 2024
848e1fe
Refactored audit alerts
snipe Apr 26, 2024
53cadf8
Removed assertions for factories
snipe May 2, 2024
8c65880
Changed badge to span in default blade
snipe May 2, 2024
f893b23
Refactored title blade areas
snipe May 2, 2024
4224bc0
Removed extra settings param
snipe May 2, 2024
eb9a654
Moved settings call higher
snipe May 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 14 additions & 9 deletions app/Console/Commands/SendExpectedCheckinAlerts.php
Expand Up @@ -9,7 +9,6 @@
use App\Notifications\ExpectedCheckinNotification;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class SendExpectedCheckinAlerts extends Command
{
Expand Down Expand Up @@ -43,25 +42,31 @@ public function __construct()
public function handle()
{
$settings = Setting::getSettings();
$whenNotify = Carbon::now();
$assets = Asset::with('assignedTo')->whereNotNull('assigned_to')->whereNotNull('expected_checkin')->where('expected_checkin', '<=', $whenNotify)->get();
$interval = $settings->audit_warning_days ?? 0;
snipe marked this conversation as resolved.
Show resolved Hide resolved
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval);

$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($whenNotify.' is deadline');
$this->info($assets->count().' assets');

foreach ($assets as $asset) {
if ($asset->assigned && $asset->checkedOutToUser()) {
Log::info('Sending ExpectedCheckinNotification to ' . $asset->assigned->email);
$asset->assigned->notify((new ExpectedCheckinNotification($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)));
}
}

if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
$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));

}
}
}
44 changes: 14 additions & 30 deletions app/Console/Commands/SendUpcomingAuditReport.php
Expand Up @@ -3,10 +3,8 @@
namespace App\Console\Commands;

use App\Models\Asset;
use App\Models\License;
use App\Models\Recipients;
use App\Models\Recipients\AlertRecipient;
use App\Models\Setting;
use App\Notifications\ExpiringAssetsNotification;
use App\Notifications\SendUpcomingAuditNotification;
use Carbon\Carbon;
use DB;
Expand Down Expand Up @@ -45,40 +43,26 @@ public function __construct()
*/
public function handle()
{

$interval = $settings->audit_warning_days ?? 0;
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval);

$settings = Setting::getSettings();
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
$this->info($assets->count().' assets must be audited in on or before '.$interval_date.' is deadline');

if (($settings->alert_email != '') && ($settings->audit_warning_days) && ($settings->alerts_enabled == 1)) {

if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
// Send a rollup to the admin, if settings dictate
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item, $key) {
return new \App\Models\Recipients\AlertRecipient($item);
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
return new AlertRecipient($item);
});

// Assets due for auditing
$this->info('Sending Admin SendUpcomingAuditNotification to: '.$settings->alert_email);
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));

$assets = Asset::whereNotNull('next_audit_date')
->DueOrOverdueForAudit($settings)
->orderBy('last_audit_date', 'asc')->get();

if ($assets->count() > 0) {
$this->info(trans_choice('mail.upcoming-audits', $assets->count(),
['count' => $assets->count(), 'threshold' => $settings->audit_warning_days]));
\Notification::send($recipients, new SendUpcomingAuditNotification($assets, $settings->audit_warning_days));
$this->info('Audit report sent to '.$settings->alert_email);
} else {
$this->info('No assets to be audited. No report sent.');
}
} elseif ($settings->alert_email == '') {
$this->error('Could not send email. No alert email configured in settings');
} elseif (! $settings->audit_warning_days) {
$this->error('No audit warning days set in Admin Notifications. No mail will be sent.');
} elseif ($settings->alerts_enabled != 1) {
$this->info('Alerts are disabled in the settings. No mail will be sent');
} else {
$this->error('Something went wrong. :( ');
$this->error('Admin Notifications Email Setting: '.$settings->alert_email);
$this->error('Admin Audit Warning Setting: '.$settings->audit_warning_days);
$this->error('Admin Alerts Emnabled: '.$settings->alerts_enabled);
}

}
}
49 changes: 38 additions & 11 deletions app/Http/Controllers/Api/AssetsController.php
Expand Up @@ -59,7 +59,7 @@ class AssetsController extends Controller
* @since [v4.0]
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request, $audit = null)
public function index(Request $request, $action = null, $upcoming_status = null)
{

$filter_non_deprecable_assets = false;
Expand Down Expand Up @@ -155,17 +155,44 @@ public function index(Request $request, $audit = null)
$assets->TextSearch($request->input('search'));
}

// This is used by the audit reporting routes
if (Gate::allows('audit', Asset::class)) {
switch ($audit) {
case 'due':
$assets->DueOrOverdueForAudit($settings);
break;
case 'overdue':
$assets->overdueForAudit($settings);
break;

/**
* Handle due and overdue audits and checkin dates
*/
switch ($action) {
case 'audits':

switch ($upcoming_status) {
case 'due':
$assets->DueForAudit($settings);
break;
case 'overdue':
$assets->OverdueForAudit();
break;
case 'due-or-overdue':
$assets->DueOrOverdueForAudit($settings);
break;
}
break;

case 'checkins':
switch ($upcoming_status) {
case 'due':
$assets->DueForCheckin($settings);
break;
case 'overdue':
$assets->OverdueForCheckin();
break;
case 'due-or-overdue':
$assets->DueOrOverdueForCheckin($settings);
break;
}
break;
}
}

/**
* End handling due and overdue audits and checkin dates
*/


// This is used by the sidenav, mostly
Expand Down
6 changes: 3 additions & 3 deletions app/Http/Controllers/Assets/AssetsController.php
Expand Up @@ -854,11 +854,11 @@ public function dueForAudit()
return view('hardware/audit-due');
}

public function overdueForAudit()
public function dueForCheckin()
{
$this->authorize('audit', Asset::class);
$this->authorize('checkin', Asset::class);

return view('hardware/audit-overdue');
return view('hardware/checkin-due');
}


Expand Down
46 changes: 46 additions & 0 deletions app/Http/Middleware/AssetCountForSidebar.php
Expand Up @@ -5,6 +5,7 @@
use App\Models\Asset;
use Auth;
use Closure;
use App\Models\Setting;

class AssetCountForSidebar
{
Expand All @@ -24,6 +25,13 @@ public function handle($request, Closure $next)
\Log::debug($e);
}

try {
$total_assets = Asset::RTD()->count();
view()->share('total_assets', $total_assets);
} catch (\Exception $e) {
\Log::debug($e);
}

try {
$total_deployed_sidebar = Asset::Deployed()->count();
view()->share('total_deployed_sidebar', $total_deployed_sidebar);
Expand Down Expand Up @@ -59,6 +67,44 @@ public function handle($request, Closure $next)
\Log::debug($e);
}

try {
$settings = Setting::getSettings();
view()->share('settings', $settings);
} catch (\Exception $e) {
\Log::debug($e);
}

try {
$total_due_for_audit = Asset::DueForAudit($settings)->count();
view()->share('total_due_for_audit', $total_due_for_audit);
} catch (\Exception $e) {
\Log::debug($e);
}

try {
$total_overdue_for_audit = Asset::OverdueForAudit()->count();
view()->share('total_overdue_for_audit', $total_overdue_for_audit);
} catch (\Exception $e) {
\Log::debug($e);
}

try {
$total_due_for_checkin = Asset::DueForCheckin($settings)->count();
view()->share('total_due_for_checkin', $total_due_for_checkin);
} catch (\Exception $e) {
\Log::debug($e);
}

try {
$total_overdue_for_checkin = Asset::OverdueForCheckin()->count();
view()->share('total_overdue_for_checkin', $total_overdue_for_checkin);
} catch (\Exception $e) {
\Log::debug($e);
}

view()->share('total_due_and_overdue_for_checkin', ($total_due_for_checkin + $total_overdue_for_checkin));
view()->share('total_due_and_overdue_for_audit', ($total_due_for_audit + $total_overdue_for_audit));

return $next($request);
}
}
70 changes: 63 additions & 7 deletions app/Models/Asset.php
Expand Up @@ -74,9 +74,9 @@ public function declinedCheckout(User $declinedBy, $signature)
'eol_explicit' => 'boolean',
'last_checkout' => 'datetime',
'last_checkin' => 'datetime',
'expected_checkin' => 'date',
'expected_checkin' => 'datetime:m-d-Y',
'last_audit_date' => 'datetime',
'next_audit_date' => 'date',
'next_audit_date' => 'datetime:m-d-Y',
'model_id' => 'integer',
'status_id' => 'integer',
'company_id' => 'integer',
Expand Down Expand Up @@ -1163,10 +1163,11 @@ public function scopeNotArchived($query)
public function scopeDueForAudit($query, $settings)
{
$interval = $settings->audit_warning_days ?? 0;
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval)->format('Y-m-d');

return $query->whereNotNull('assets.next_audit_date')
->where('assets.next_audit_date', '>=', Carbon::now())
->whereRaw("DATE_SUB(assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
->whereBetween('assets.next_audit_date', [$today->format('Y-m-d'), $interval_date])
->where('assets.archived', '=', 0)
->NotArchived();
}
Expand All @@ -1188,7 +1189,7 @@ public function scopeDueForAudit($query, $settings)
public function scopeOverdueForAudit($query)
{
return $query->whereNotNull('assets.next_audit_date')
->where('assets.next_audit_date', '<', Carbon::now())
->where('assets.next_audit_date', '<', Carbon::now()->format('Y-m-d'))
->where('assets.archived', '=', 0)
->NotArchived();
}
Expand All @@ -1208,15 +1209,70 @@ public function scopeOverdueForAudit($query)
*/

public function scopeDueOrOverdueForAudit($query, $settings)
{

return $query->where(function ($query) {
$query->OverdueForAudit();
})->orWhere(function ($query) use ($settings) {
$query->DueForAudit($settings);
});
}


/**
* Query builder scope for Assets that are DUE for checkin, based on the assets.expected_checkin
* and settings.audit_warning_days. It checks to see if assets.expected_checkin is now
*
* @author A. Gianotto <snipe@snipe.net>
* @since v6.4.0
* @return \Illuminate\Database\Query\Builder Modified query builder
*/

public function scopeDueForCheckin($query, $settings)
{
$interval = $settings->audit_warning_days ?? 0;
$today = Carbon::now();
$interval_date = $today->copy()->addDays($interval)->format('Y-m-d');

return $query->whereNotNull('assets.next_audit_date')
->whereRaw('DATE_SUB('.DB::getTablePrefix()."assets.next_audit_date, INTERVAL $interval DAY) <= '".Carbon::now()."'")
return $query->whereNotNull('assets.expected_checkin')
->whereBetween('assets.expected_checkin', [$today->format('Y-m-d'), $interval_date])
->where('assets.archived', '=', 0)
->whereNotNull('assets.assigned_to')
->NotArchived();
}

/**
* Query builder scope for Assets that are overdue for checkin OR overdue
*
* @author A. Gianotto <snipe@snipe.net>
* @since v6.4.0
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeOverdueForCheckin($query)
{
return $query->whereNotNull('assets.expected_checkin')
->where('assets.expected_checkin', '<', Carbon::now()->format('Y-m-d'))
->where('assets.archived', '=', 0)
->whereNotNull('assets.assigned_to')
->NotArchived();
}

/**
* Query builder scope for Assets that are due for checkin OR overdue
*
* @author A. Gianotto <snipe@snipe.net>
* @since v6.4.0
* @return \Illuminate\Database\Query\Builder Modified query builder
*/
public function scopeDueOrOverdueForCheckin($query, $settings)
{
return $query->where(function ($query) {
$query->OverdueForCheckin();
})->orWhere(function ($query) use ($settings) {
$query->DueForCheckin($settings);
});
}


/**
* Query builder scope for Archived assets counting
Expand Down
5 changes: 5 additions & 0 deletions public/css/build/app.css
Expand Up @@ -1154,4 +1154,9 @@ input[type="radio"]:checked::before {
.datepicker.dropdown-menu {
z-index: 1030 !important;
}
.sidebar-menu > li .badge {
margin-top: 0px;
filter: brightness(70%);
font-size: 70%;
}

5 changes: 5 additions & 0 deletions public/css/build/overrides.css
Expand Up @@ -787,4 +787,9 @@ input[type="radio"]:checked::before {
.datepicker.dropdown-menu {
z-index: 1030 !important;
}
.sidebar-menu > li .badge {
margin-top: 0px;
filter: brightness(70%);
font-size: 70%;
}