diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index c5c25d731..c7055ecb8 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -512,18 +512,25 @@ class AssetsController extends Controller $this->authorize('audit', Asset::class); $rules = array( - 'id' => 'required' + 'location_id' => 'exists:locations,id|nullable|numeric', + 'next_audit_date' => 'date|nullable' ); $validator = \Validator::make($request->all(), $rules); + if ($validator->fails()) { + return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all())); + } $asset = Asset::findOrFail($id); $asset->next_audit_date = $request->input('next_audit_date'); if ($asset->save()) { - $asset->logAudit(request('note')); + $asset->logAudit(request('note'),request('location_id')); return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.audit.success'))); } + + + } } diff --git a/app/Http/Controllers/Api/ReportsController.php b/app/Http/Controllers/Api/ReportsController.php index a8c12d693..373112250 100644 --- a/app/Http/Controllers/Api/ReportsController.php +++ b/app/Http/Controllers/Api/ReportsController.php @@ -19,7 +19,7 @@ class ReportsController extends Controller public function index(Request $request) { - $actionlogs = Actionlog::with('item', 'user', 'target'); + $actionlogs = Actionlog::with('item', 'user', 'target','location'); if ($request->has('search')) { $actionlogs = $actionlogs->TextSearch(e($request->input('search'))); diff --git a/app/Http/Controllers/AssetsController.php b/app/Http/Controllers/AssetsController.php index 53c45eca1..666b441c5 100755 --- a/app/Http/Controllers/AssetsController.php +++ b/app/Http/Controllers/AssetsController.php @@ -1241,22 +1241,30 @@ class AssetsController extends Controller public function audit(Request $request, $id) { $this->authorize('audit', Asset::class); - $dt = Carbon::now()->addMonths(12)->toDateString(); - $asset = Asset::findOrFail($id); - return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt); + return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list', Helper::locationsList()); } public function auditStore(Request $request, $id) { $this->authorize('audit', Asset::class); + $rules = array( + 'location_id' => 'exists:locations,id|nullable|numeric', + 'next_audit_date' => 'date|nullable' + ); + + $validator = \Validator::make($request->all(), $rules); + if ($validator->fails()) { + return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all())); + } + $asset = Asset::findOrFail($id); $asset->next_audit_date = $request->input('next_audit_date'); if ($asset->save()) { - $asset->logAudit(request('note')); + $asset->logAudit(request('note'),request('location_id')); return redirect()->to("hardware")->with('success', trans('admin/hardware/message.audit.success')); } } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 4d71cce76..2d9d5a01b 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -562,10 +562,12 @@ class SettingsController extends Controller $alert_email = rtrim($request->input('alert_email'), ','); $alert_email = trim($alert_email); - $setting->alert_email = e($alert_email); + $setting->alert_email = $alert_email; $setting->alerts_enabled = $request->input('alerts_enabled', '0'); $setting->alert_interval = $request->input('alert_interval'); $setting->alert_threshold = $request->input('alert_threshold'); + $setting->audit_interval = $request->input('audit_interval'); + $setting->audit_warning_days = $request->input('audit_warning_days'); if ($setting->save()) { return redirect()->route('settings.index') diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index 2286a3b23..80b133c57 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -2,6 +2,7 @@ namespace App\Http\Transformers; use App\Models\Actionlog; +use App\Models\Setting; use Gate; use Illuminate\Database\Eloquent\Collection; use App\Helpers\Helper; @@ -12,24 +13,32 @@ class ActionlogsTransformer public function transformActionlogs (Collection $actionlogs, $total) { $array = array(); + $settings = Setting::getSettings(); foreach ($actionlogs as $actionlog) { - $array[] = self::transformActionlog($actionlog); + $array[] = self::transformActionlog($actionlog, $settings); } return (new DatatablesTransformer)->transformDatatables($array, $total); } - public function transformActionlog (Actionlog $actionlog) + public function transformActionlog (Actionlog $actionlog, $settings = null) { $array = [ + 'id' => (int) $actionlog->id, 'icon' => $actionlog->present()->icon(), + 'image' => ($actionlog->item->getImageUrl()) ? $actionlog->item->getImageUrl() : null, 'item' => ($actionlog->item) ? [ 'id' => (int) $actionlog->item->id, 'name' => e($actionlog->item->getDisplayNameAttribute()), 'type' => e($actionlog->itemType()), ] : null, + 'location' => ($actionlog->location) ? [ + 'id' => (int) $actionlog->location->id, + 'name' => e($actionlog->location->name) + ] : null, 'created_at' => Helper::getFormattedDateObject($actionlog->created_at, 'datetime'), 'updated_at' => Helper::getFormattedDateObject($actionlog->updated_at, 'datetime'), - 'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->item->next_audit_date, 'datetime'): null, + 'next_audit_date' => ($actionlog->itemType()=='asset') ? Helper::getFormattedDateObject($actionlog->item->next_audit_date, 'date'): null, + 'days_to_next_audit' => $actionlog->daysUntilNextAudit($settings->audit_interval, $actionlog->item), 'action_type' => $actionlog->present()->actionType(), 'admin' => ($actionlog->user) ? [ 'id' => (int) $actionlog->user->id, diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php index 764c64b7e..b8bed75ce 100755 --- a/app/Models/Actionlog.php +++ b/app/Models/Actionlog.php @@ -123,6 +123,10 @@ class Actionlog extends SnipeModel return $this->belongsTo('\App\Models\ActionLog', 'thread_id'); } + public function location() { + return $this->belongsTo('\App\Models\Location', 'location_id' )->withTrashed(); + } + /** * Check if the file exists, and if it does, force a download **/ @@ -149,6 +153,44 @@ class Actionlog extends SnipeModel } } + public function daysUntilNextAudit($monthInterval = null, $asset = null) { + + // check for next_audit_date to override global + + if (!$monthInterval) { + $monthInterval = 12; + } + $last_audit_date = $this->created_at; + $next_audit_days = $last_audit_date->diffInDays($last_audit_date->copy()->addMonth($monthInterval)); + + // Override the default setting for interval if the asset has its own next audit date + if (($asset) && ($asset->next_audit_date)) { + $override_default_next = \Carbon::parse($asset->next_audit_date); + $suborder['payment_date'] = $override_default_next->format('M d Y'); + $next_audit_days = $last_audit_date->diffInDays($override_default_next); + } + + return $next_audit_days; + } + + public function calcNextAuditDate($monthInterval = null, $asset = null) { + + if (!$monthInterval) { + $monthInterval = 12; + } + + $dt = \Carbon::now()->addMonths(12)->toDateString(); + $last_audit_date = Carbon::parse($this->created_at); + + // If there is an asset-specific next date already given, + if (($asset) && ($asset->next_audit_date)) { + return \Carbon::parse($asset->next_audit_date);; + } + + $next_audit_date = \Carbon::now()->addMonths($monthInterval)->toDateString(); + $next_audit_date = $last_audit_date->diffInDays($last_audit_date->copy()->addMonth($monthInterval)); + } + /** * getListingOfActionLogsChronologicalOrder * diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 544b29b7b..d6931576a 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -66,6 +66,7 @@ class Asset extends Depreciable 'asset_tag' => 'required|min:1|max:255|unique_undeleted', 'status' => 'integer', 'purchase_cost' => 'numeric|nullable', + 'next_audit_date' => 'date|nullable', ]; /** diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index 5d0280788..f957c78b4 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -127,7 +127,7 @@ trait Loggable * @since [v4.0] * @return \App\Models\Actionlog */ - public function logAudit($note) + public function logAudit($note, $location_id) { $log = new Actionlog; if (static::class == LicenseSeat::class) { @@ -137,7 +137,7 @@ trait Loggable $log->item_type = static::class; $log->item_id = $this->id; } - $log->location_id = null; + $log->location_id = ($location_id) ? $location_id : null; $log->note = $note; $log->user_id = Auth::user()->id; $log->logaction('audit'); @@ -153,6 +153,7 @@ trait Loggable } + /** * @author Daniel Meltzer integer('audit_interval')->nullable()->default(NULL); + $table->integer('audit_warning_days')->nullable()->default(NULL); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('audit_interval'); + $table->dropColumn('audit_warning_days'); + }); + } +} diff --git a/resources/lang/en/admin/settings/general.php b/resources/lang/en/admin/settings/general.php index 43195baa4..73fd052a5 100644 --- a/resources/lang/en/admin/settings/general.php +++ b/resources/lang/en/admin/settings/general.php @@ -10,6 +10,10 @@ return array( 'alert_interval' => 'Expiring Alerts Threshold (in days)', 'alert_inv_threshold' => 'Inventory Alert Threshold', 'asset_ids' => 'Asset IDs', + 'audit_interval' => 'Audit Interval', + 'audit_interval_help' => 'If you are required to regularly physically audit your assets, enter the interval in months.', + 'audit_warning_days' => 'Audit Warning Threshold', + 'audit_warning_days_help' => 'How many days in advance should we warn you when assets are due for auditing?', 'auto_increment_assets' => 'Generate auto-incrementing asset IDs', 'auto_increment_prefix' => 'Prefix (optional)', 'auto_incrementing_help' => 'Enable auto-incrementing asset IDs first to set this', diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php index a752eb41f..d3656d96e 100644 --- a/resources/lang/en/general.php +++ b/resources/lang/en/general.php @@ -54,6 +54,8 @@ 'current' => 'Current', 'custom_report' => 'Custom Asset Report', 'dashboard' => 'Dashboard', + 'days' => 'days', + 'days_to_next_audit' => 'Days to Next Audit', 'date' => 'Date', 'debug_warning' => 'Warning!', 'debug_warning_text' => 'This application is running in production mode with debugging enabled. This can expose sensitive data if your application is accessible to the outside world. Disable debug mode by setting the APP_DEBUG value in your .env file to false.', diff --git a/resources/views/hardware/audit.blade.php b/resources/views/hardware/audit.blade.php index 935b88c0b..7fc9afdca 100644 --- a/resources/views/hardware/audit.blade.php +++ b/resources/views/hardware/audit.blade.php @@ -44,11 +44,20 @@ + +
+ {{ Form::label('location_id', trans('general.location'), array('class' => 'col-md-3 control-label')) }} +
+ {{ Form::select('location_id', $locations_list , Input::old('location_id'), array('class'=>'select2', 'id'=>'location_id', 'style'=>'width:100%')) }} + + {!! $errors->first('location_id', ' :message') !!} +
+
- {{ Form::label('name', trans('admin/hardware/form.checkout_date'), array('class' => 'col-md-3 control-label')) }} + {{ Form::label('name', trans('general.next_audit_date'), array('class' => 'col-md-3 control-label')) }}
@@ -73,7 +82,7 @@
diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index e60602973..849919801 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -98,6 +98,15 @@ $('.snipe-table').bootstrapTable({ }); + + function dateRowCheckStyle(value) { + if ((value.days_to_next_audit) && (value.days_to_next_audit < {{ $snipeSettings->audit_warning_days }})) { + return { classes : "danger" } + } + return {}; + } + + // Handle whether or not the edit button should be disabled $('.snipe-table').on('check.bs.table', function () { $('#bulkEdit').removeAttr('disabled'); diff --git a/resources/views/reports/audit.blade.php b/resources/views/reports/audit.blade.php index ac7c8569e..c36b12027 100644 --- a/resources/views/reports/audit.blade.php +++ b/resources/views/reports/audit.blade.php @@ -15,22 +15,25 @@
+ data-cookie-id-table="activityReportTable" + data-row-style="dateRowCheckStyle"> - - + + - - + + + + - +
{{ trans('general.date') }}{{ trans('admin/hardware/table.image') }}{{ trans('general.audit') }} {{ trans('general.admin') }}{{ trans('general.action') }}{{ trans('general.item') }}{{ trans('general.item') }}{{ trans('general.location') }}{{ trans('general.next_audit_date') }}{{ trans('general.days_to_next_audit') }}{{ trans('general.notes') }}{{ trans('general.notes') }}
@@ -42,5 +45,5 @@ @section('moar_scripts') - @include ('partials.bootstrap-table', ['exportFile' => 'activity-export', 'search' => true]) + @include ('partials.bootstrap-table', ['exportFile' => 'audit-export', 'search' => false]) @stop diff --git a/resources/views/settings/alerts.blade.php b/resources/views/settings/alerts.blade.php index 70c2c6012..29e3ea54c 100644 --- a/resources/views/settings/alerts.blade.php +++ b/resources/views/settings/alerts.blade.php @@ -90,6 +90,41 @@
+ + +
+
+ {{ Form::label('audit_interval', trans('admin/settings/general.audit_interval')) }} +
+
+ {{ Form::text('audit_interval', Input::old('audit_interval', $setting->audit_interval), array('class' => 'form-control','placeholder' => '12', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} + {{ trans('general.months') }} +
+
+ {!! $errors->first('audit_interval', ':message') !!} +

{{ trans('admin/settings/general.audit_interval_help') }}

+
+
+ + +
+
+ {{ Form::label('audit_warning_days', trans('admin/settings/general.audit_warning_days')) }} +
+
+ {{ Form::text('audit_warning_days', Input::old('audit_warning_days', $setting->audit_warning_days), array('class' => 'form-control','placeholder' => '14', 'maxlength'=>'3', 'style'=>'width: 60px;')) }} + {{ trans('general.days') }} + + + + +
+
+ {!! $errors->first('audit_warning_days', ':message') !!} +

{{ trans('admin/settings/general.audit_warning_days_help') }}

+
+
+