diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index 8903330166..c5c25d7310 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -31,6 +31,7 @@ use TCPDF; use Validator; use View; + /** * This class controls all actions related to assets for * the Snipe-IT Asset Management application. @@ -496,4 +497,33 @@ class AssetsController extends Controller return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error'))); } + + + /** + * Mark an asset as audited + * + * @author [A. Gianotto] [] + * @param int $id + * @since [v4.0] + * @return JsonResponse + */ + public function audit(Request $request, $id) { + + $this->authorize('audit', Asset::class); + + $rules = array( + 'id' => 'required' + ); + + $validator = \Validator::make($request->all(), $rules); + + $asset = Asset::findOrFail($id); + $asset->next_audit_date = $request->input('next_audit_date'); + + if ($asset->save()) { + $asset->logAudit(request('note')); + 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 09f73f4cd1..a8c12d6937 100644 --- a/app/Http/Controllers/Api/ReportsController.php +++ b/app/Http/Controllers/Api/ReportsController.php @@ -36,6 +36,10 @@ class ReportsController extends Controller ->where('item_type','=',"App\\Models\\".ucwords($request->input('item_type'))); } + if ($request->has('action_type')) { + $actionlogs = $actionlogs->where('action_type','=',$request->input('action_type'))->orderBy('created_at', 'desc'); + } + $allowed_columns = [ 'id', 'created_at' diff --git a/app/Http/Controllers/AssetsController.php b/app/Http/Controllers/AssetsController.php index 30bf8bb209..53c45eca11 100755 --- a/app/Http/Controllers/AssetsController.php +++ b/app/Http/Controllers/AssetsController.php @@ -595,9 +595,12 @@ class AssetsController extends Controller */ public function show($assetId = null) { + $asset = Asset::withTrashed()->find($assetId); - $settings = Setting::getSettings(); $this->authorize('view', $asset); + $settings = Setting::getSettings(); + $audit_log = Actionlog::where('action_type','=','audit')->where('item_id','=',$assetId)->where('item_type','=',Asset::class)->orderBy('created_at','DESC')->first(); + if (isset($asset)) { @@ -617,7 +620,8 @@ class AssetsController extends Controller 'url' => route('qr_code/hardware', $asset->id) ); - return view('hardware/view', compact('asset', 'qr_code', 'settings'))->with('use_currency', $use_currency); + return view('hardware/view', compact('asset', 'qr_code', 'settings')) + ->with('use_currency', $use_currency)->with('audit_log',$audit_log); } return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist', compact('id'))); @@ -1233,4 +1237,29 @@ class AssetsController extends Controller // Redirect to the asset management page with error return redirect()->to("hardware/bulk-checkout")->with('error', trans('admin/hardware/message.checkout.error'))->withErrors($errors); } + + 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); + } + + public function auditStore(Request $request, $id) + { + $this->authorize('audit', Asset::class); + + $asset = Asset::findOrFail($id); + $asset->next_audit_date = $request->input('next_audit_date'); + + if ($asset->save()) { + $asset->logAudit(request('note')); + return redirect()->to("hardware")->with('success', trans('admin/hardware/message.audit.success')); + } + } + + } diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 7dd8799801..3e6d329ff5 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -271,6 +271,20 @@ class ReportsController extends Controller } + + /** + * Displays audit report. + * + * @author [A. Gianotto] [] + * @since [v4.0] + * @return View + */ + public function audit() + { + return view('reports/audit'); + } + + /** * Displays activity report. * diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index 72369defa2..2286a3b232 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -29,6 +29,7 @@ class ActionlogsTransformer ] : 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, 'action_type' => $actionlog->present()->actionType(), 'admin' => ($actionlog->user) ? [ 'id' => (int) $actionlog->user->id, diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index d8bc7f07ca..5d02807888 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -7,6 +7,7 @@ use App\Models\Asset; use App\Models\CheckoutRequest; use App\Models\User; use App\Notifications\CheckinNotification; +use App\Notifications\AuditNotification; use App\Notifications\CheckoutNotification; use Illuminate\Support\Facades\Auth; @@ -120,6 +121,38 @@ trait Loggable return $log; } + + /** + * @author A. Gianotto + * @since [v4.0] + * @return \App\Models\Actionlog + */ + public function logAudit($note) + { + $log = new Actionlog; + if (static::class == LicenseSeat::class) { + $log->item_type = License::class; + $log->item_id = $this->license_id; + } else { + $log->item_type = static::class; + $log->item_id = $this->id; + } + $log->location_id = null; + $log->note = $note; + $log->user_id = Auth::user()->id; + $log->logaction('audit'); + + $params = [ + 'item' => $log->item, + 'admin' => $log->user, + 'note' => $note + ]; + Setting::getSettings()->notify(new AuditNotification($params)); + + return $log; + } + + /** * @author Daniel Meltzer params = $params; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + $notifyBy = []; + if (Setting::getSettings()->slack_endpoint) { + $notifyBy[] = 'slack'; + } + + return $notifyBy; + } + + public function toSlack($notifiable) + { + + return (new SlackMessage) + ->success() + ->content(class_basename(get_class($this->params['item'])) . " Audited") + ->attachment(function ($attachment) use ($notifiable) { + $item = $this->params['item']; + $admin_user = $this->params['admin']; + $fields = [ + 'By' => '<'.$admin_user->present()->viewUrl().'|'.$admin_user->present()->fullName().'>' + ]; + array_key_exists('note', $this->params) && $fields['Notes'] = $this->params['note']; + + $attachment->title($item->name, $item->present()->viewUrl()) + ->fields($fields); + }); + } + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', 'https://laravel.com') + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Observers/AssetObserver.php b/app/Observers/AssetObserver.php index f0c7a5a230..3066057d1c 100644 --- a/app/Observers/AssetObserver.php +++ b/app/Observers/AssetObserver.php @@ -20,6 +20,7 @@ class AssetObserver if ((isset($asset->getOriginal()['assigned_to'])) && ($asset->getAttributes()['assigned_to'] == $asset->getOriginal()['assigned_to']) + && ($asset->getAttributes()['next_audit_date'] == $asset->getOriginal()['next_audit_date']) && ($asset->getAttributes()['last_checkout'] == $asset->getOriginal()['last_checkout']) && ($asset->getAttributes()['status_id'] == $asset->getOriginal()['status_id'])) { diff --git a/config/permissions.php b/config/permissions.php index 8988d2132c..6e578ae9d8 100644 --- a/config/permissions.php +++ b/config/permissions.php @@ -85,8 +85,8 @@ return array( array( 'permission' => 'assets.audit', 'label' => 'Audit ', - 'note' => '', - 'display' => false, + 'note' => 'Allows the user to mark an asset as physically inventoried.', + 'display' => true, ), diff --git a/database/migrations/2017_08_25_074822_add_auditing_tables.php b/database/migrations/2017_08_25_074822_add_auditing_tables.php new file mode 100644 index 0000000000..e8a6f5209a --- /dev/null +++ b/database/migrations/2017_08_25_074822_add_auditing_tables.php @@ -0,0 +1,32 @@ +date('next_audit_date')->nullable()->default(NULL); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('assets', function (Blueprint $table) { + $table->dropColumn('next_audit_date'); + }); + } +} diff --git a/resources/lang/en/admin/hardware/message.php b/resources/lang/en/admin/hardware/message.php index df20960b2d..112cf77ded 100644 --- a/resources/lang/en/admin/hardware/message.php +++ b/resources/lang/en/admin/hardware/message.php @@ -24,6 +24,12 @@ return array( 'success' => 'Asset restored successfully.' ), + 'audit' => array( + 'error' => 'Asset audit was unsuccessful. Please try again.', + 'success' => 'Asset audit successfully logged.' + ), + + 'deletefile' => array( 'error' => 'File not deleted. Please try again.', 'success' => 'File successfully deleted.', diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php index 5a5fc5f7da..a752eb41fe 100644 --- a/resources/lang/en/general.php +++ b/resources/lang/en/general.php @@ -18,6 +18,8 @@ 'asset_report' => 'Asset Report', 'asset_tag' => 'Asset Tag', 'assets_available' => 'assets available', + 'audit' => 'Audit', + 'audit_report' => 'Audit Log', 'assets' => 'Assets', 'avatar_delete' => 'Delete Avatar', 'avatar_upload' => 'Upload Avatar', @@ -117,6 +119,8 @@ 'moreinfo' => 'More Info', 'name' => 'Name', 'next' => 'Next', + 'next_audit_date' => 'Next Audit Date', + 'last_audit' => 'Last Audit', 'new' => 'new!', 'no_depreciation' => 'No Depreciation', 'no_results' => 'No Results.', diff --git a/resources/views/hardware/audit.blade.php b/resources/views/hardware/audit.blade.php new file mode 100644 index 0000000000..935b88c0bb --- /dev/null +++ b/resources/views/hardware/audit.blade.php @@ -0,0 +1,82 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') + {{ trans('general.audit') }} + @parent +@stop + +{{-- Page content --}} +@section('content') + + + +
+ +
+
+
+
+

{{ trans('admin/hardware/form.tag') }} {{ $asset->asset_tag }}

+
+
+ {{csrf_field()}} + @if ($asset->model->name) + +
+ {{ Form::label('name', trans('admin/hardware/form.model'), array('class' => 'col-md-3 control-label')) }} +
+

{{ $asset->model->name }}

+
+
+ @endif + + +
+ {{ Form::label('name', trans('admin/hardware/form.name'), array('class' => 'col-md-3 control-label')) }} +
+

{{ $asset->name }}

+
+
+ + + + +
+ {{ Form::label('name', trans('admin/hardware/form.checkout_date'), array('class' => 'col-md-3 control-label')) }} +
+
+ + +
+ {!! $errors->first('next_audit_date', ' :message') !!} +
+
+ + + +
+ {{ Form::label('note', trans('admin/hardware/form.notes'), array('class' => 'col-md-3 control-label')) }} +
+ + {!! $errors->first('note', ' :message') !!} +
+
+ + + +
+ +
+
+
+
+@stop diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 7af5a1267e..b559198671 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -23,6 +23,7 @@ @endif
  • {{ trans('admin/hardware/general.edit') }}
  • {{ trans('admin/hardware/general.clone') }}
  • +
  • {{ trans('general.audit') }}
  • @endcan @@ -112,6 +113,18 @@ {{ $asset->serial }} @endif + @if ($audit_log->created_at) + + {{ trans('general.last_audit') }} + {{ \App\Helpers\Helper::getFormattedDateObject($audit_log->created_at, 'date', false) }} (by {{ link_to_route('users.show', $audit_log->user->present()->fullname(), [$audit_log->user->id]) }}) + + @endif + @if ($asset->next_audit_date) + + {{ trans('general.next_audit_date') }} + {{ \App\Helpers\Helper::getFormattedDateObject($asset->next_audit_date, 'date', false) }} + + @endif @if ($asset->model->manufacturer) diff --git a/resources/views/reports/audit.blade.php b/resources/views/reports/audit.blade.php new file mode 100644 index 0000000000..ac7c8569e9 --- /dev/null +++ b/resources/views/reports/audit.blade.php @@ -0,0 +1,46 @@ +@extends('layouts/default') + +{{-- Page title --}} +@section('title') + {{ trans('general.audit_report') }} + @parent +@stop + +{{-- Page content --}} +@section('content') + +
    +
    +
    +
    + + + + + + + + + + + + + +
    {{ trans('general.date') }}{{ trans('general.admin') }}{{ trans('general.action') }}{{ trans('general.item') }}{{ trans('general.notes') }}
    +
    +
    +
    +
    +@stop + + +@section('moar_scripts') + @include ('partials.bootstrap-table', ['exportFile' => 'activity-export', 'search' => true]) +@stop diff --git a/routes/api.php b/routes/api.php index 8e9b3e405f..0d93aad60d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -214,6 +214,12 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () { Route::group(['prefix' => 'hardware'], function () { + Route::post('audit/{id}', [ + 'as' => 'api.asset.audit', + 'uses' => 'AssetsController@audit' + ]); + + Route::post('{asset_id}/checkout', [ 'as' => 'api.assets.checkout', diff --git a/routes/web.php b/routes/web.php index 50c37f16ac..1f09080307 100644 --- a/routes/web.php +++ b/routes/web.php @@ -274,6 +274,11 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () { Route::group(['middleware' => ['auth']], function () { + Route::get('reports/audit', [ + 'as' => 'reports.audit', + 'uses' => 'ReportsController@audit' + ]); + Route::get( 'reports/depreciation', [ 'as' => 'reports/depreciation', 'uses' => 'ReportsController@getDeprecationReport' ] @@ -316,7 +321,7 @@ Route::group(['middleware' => ['auth']], function () { Route::get( 'reports/activity', - [ 'as' => 'reports/activity', 'uses' => 'ReportsController@getActivityReport' ] + [ 'as' => 'reports.activity', 'uses' => 'ReportsController@getActivityReport' ] ); diff --git a/routes/web/hardware.php b/routes/web/hardware.php index 8a3453709a..c0775e8fe6 100644 --- a/routes/web/hardware.php +++ b/routes/web/hardware.php @@ -17,6 +17,16 @@ Route::group( 'parameters' => ['maintenance' => 'maintenance_id', 'asset' => 'asset_id'] ]); + Route::get('audit/{id}', [ + 'as' => 'asset.audit.create', + 'uses' => 'AssetsController@audit' + ]); + + Route::post('audit/{id}', [ + 'as' => 'asset.audit.store', + 'uses' => 'AssetsController@auditStore' + ]); + Route::get('history', [ 'as' => 'asset.import-history', @@ -111,6 +121,8 @@ Route::group( 'as' => 'hardware/bulkcheckout', 'uses' => 'AssetsController@postBulkCheckout' ]); + + });