mirror of
https://github.com/snipe/snipe-it.git
synced 2025-02-02 08:21:09 -08:00
Auduting improvements
This commit is contained in:
parent
af835d6efc
commit
51d74ac06d
|
@ -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')));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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')));
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 <parallelgrapefruit@gmail.com
|
||||
* @since [v3.5]
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddAuditingToSettings extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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 <code>APP_DEBUG</code> value in your <code>.env</code> file to <code>false</code>.',
|
||||
|
|
|
@ -44,11 +44,20 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Locations -->
|
||||
<div id="location_id" class="form-group{{ $errors->has('location_id') ? ' has-error' : '' }}">
|
||||
{{ Form::label('location_id', trans('general.location'), array('class' => 'col-md-3 control-label')) }}
|
||||
<div class="col-md-9">
|
||||
{{ Form::select('location_id', $locations_list , Input::old('location_id'), array('class'=>'select2', 'id'=>'location_id', 'style'=>'width:100%')) }}
|
||||
|
||||
{!! $errors->first('location_id', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Next Audit -->
|
||||
<div class="form-group {{ $errors->has('next_audit_date') ? 'error' : '' }}">
|
||||
{{ 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')) }}
|
||||
<div class="col-md-9">
|
||||
<div class="input-group date col-md-5" data-provide="datepicker" data-date-format="yyyy-mm-dd">
|
||||
<input type="text" class="form-control" placeholder="{{ trans('general.next_audit_date') }}" name="next_audit_date" id="next_audit_date" value="{{ Input::old('next_audit_date', $next_audit_date) }}">
|
||||
|
@ -73,7 +82,7 @@
|
|||
</div> <!--/.box-body-->
|
||||
<div class="box-footer">
|
||||
<a class="btn btn-link" href="{{ URL::previous() }}"> {{ trans('button.cancel') }}</a>
|
||||
<button type="submit" class="btn btn-success pull-right"><i class="fa fa-check icon-white"></i> {{ trans('general.checkout') }}</button>
|
||||
<button type="submit" class="btn btn-success pull-right"><i class="fa fa-check icon-white"></i> {{ trans('general.audit') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -15,22 +15,25 @@
|
|||
<div class="box-body">
|
||||
|
||||
<table
|
||||
name="activityReport"
|
||||
name="auditReport"
|
||||
data-toolbar="#toolbar"
|
||||
class="table table-striped snipe-table"
|
||||
id="table"
|
||||
data-url="{{ route('api.activity.index', ['action_type' => 'audit']) }}"
|
||||
data-cookie="true"
|
||||
data-cookie-id-table="activityReportTable">
|
||||
data-cookie-id-table="activityReportTable"
|
||||
data-row-style="dateRowCheckStyle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter"></th>
|
||||
<th class="col-sm-3" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||
<th class="col-sm-1" data-field="image" data-visible="false" data-formatter="imageFormatter">{{ trans('admin/hardware/table.image') }}</th>
|
||||
<th class="col-sm-2" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.audit') }}</th>
|
||||
<th class="col-sm-2" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th class="col-sm-2" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th class="col-sm-3" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th class="col-sm-2" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th class="col-sm-1" data-field="location" data-formatter="locationsLinkObjFormatter">{{ trans('general.location') }}</th>
|
||||
<th class="col-sm-2" data-field="next_audit_date" data-formatter="dateDisplayFormatter">{{ trans('general.next_audit_date') }}</th>
|
||||
<th class="col-sm-1" data-field="days_to_next_audit">{{ trans('general.days_to_next_audit') }}</th>
|
||||
|
||||
<th class="col-sm-1" data-field="note">{{ trans('general.notes') }}</th>
|
||||
<th class="col-sm-2" data-field="note">{{ trans('general.notes') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
@ -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
|
||||
|
|
|
@ -90,6 +90,41 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Alert interval -->
|
||||
<div class="form-group {{ $errors->has('audit_interval') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('audit_interval', trans('admin/settings/general.audit_interval')) }}
|
||||
</div>
|
||||
<div class="input-group col-md-2">
|
||||
{{ Form::text('audit_interval', Input::old('audit_interval', $setting->audit_interval), array('class' => 'form-control','placeholder' => '12', 'maxlength'=>'3', 'style'=>'width: 60px;')) }}
|
||||
<span class="input-group-addon">{{ trans('general.months') }}</span>
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
{!! $errors->first('audit_interval', '<span class="alert-msg">:message</span>') !!}
|
||||
<p class="help-block">{{ trans('admin/settings/general.audit_interval_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alert threshold -->
|
||||
<div class="form-group {{ $errors->has('audit_warning_days') ? 'error' : '' }}">
|
||||
<div class="col-md-3">
|
||||
{{ Form::label('audit_warning_days', trans('admin/settings/general.audit_warning_days')) }}
|
||||
</div>
|
||||
<div class="input-group col-md-2">
|
||||
{{ 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;')) }}
|
||||
<span class="input-group-addon">{{ trans('general.days') }}</span>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
{!! $errors->first('audit_warning_days', '<span class="alert-msg">:message</span>') !!}
|
||||
<p class="help-block">{{ trans('admin/settings/general.audit_warning_days_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div> <!--/.box-body-->
|
||||
|
|
Loading…
Reference in a new issue