Merge remote-tracking branch 'origin/develop'
Signed-off-by: snipe <snipe@snipe.net> # Conflicts: # public/css/build/app.css # public/css/build/overrides.css # public/css/dist/all.css # public/mix-manifest.json
This commit is contained in:
commit
f1d006c236
40 changed files with 596 additions and 150 deletions
|
@ -34,6 +34,7 @@ use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use App\View\Label;
|
use App\View\Label;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1064,7 +1065,7 @@ class AssetsController extends Controller
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @since [v4.0]
|
* @since [v4.0]
|
||||||
*/
|
*/
|
||||||
public function audit(Request $request): JsonResponse
|
public function audit(Request $request, Asset $asset): JsonResponse
|
||||||
|
|
||||||
{
|
{
|
||||||
$this->authorize('audit', Asset::class);
|
$this->authorize('audit', Asset::class);
|
||||||
|
@ -1072,36 +1073,15 @@ class AssetsController extends Controller
|
||||||
$settings = Setting::getSettings();
|
$settings = Setting::getSettings();
|
||||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||||
|
|
||||||
// No tag passed - return an error
|
// Allow the asset tag to be passed in the payload (legacy method)
|
||||||
if (!$request->filled('asset_tag')) {
|
if ($request->filled('asset_tag')) {
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
|
||||||
'asset_tag' => '',
|
|
||||||
'error' => trans('admin/hardware/message.no_tag'),
|
|
||||||
], trans('admin/hardware/message.no_tag')), 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
|
|
||||||
|
|
||||||
|
|
||||||
if ($asset) {
|
if ($asset) {
|
||||||
|
|
||||||
/**
|
$originalValues = $asset->getRawOriginal();
|
||||||
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
|
|
||||||
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
|
|
||||||
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
|
|
||||||
* the audit log entry we're creating through this controller.
|
|
||||||
*
|
|
||||||
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
|
|
||||||
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
|
|
||||||
* will bypass normal model-level validation that's usually handled at the observer )
|
|
||||||
*
|
|
||||||
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
|
|
||||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
|
||||||
*
|
|
||||||
* @see \App\Observers\AssetObserver::updating()
|
|
||||||
*/
|
|
||||||
$asset->unsetEventDispatcher();
|
|
||||||
$asset->next_audit_date = $dt;
|
$asset->next_audit_date = $dt;
|
||||||
|
|
||||||
if ($request->filled('next_audit_date')) {
|
if ($request->filled('next_audit_date')) {
|
||||||
|
@ -1116,33 +1096,89 @@ class AssetsController extends Controller
|
||||||
|
|
||||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
// Set up the payload for re-display in the API response
|
||||||
|
$payload = [
|
||||||
|
'id' => $asset->id,
|
||||||
|
'asset_tag' => $asset->asset_tag,
|
||||||
|
'note' => $request->input('note'),
|
||||||
|
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update custom fields in the database.
|
||||||
|
* Validation for these fields is handled through the AssetRequest form request
|
||||||
|
* $model = AssetModel::find($request->get('model_id'));
|
||||||
|
*/
|
||||||
|
if (($asset->model) && ($asset->model->fieldset)) {
|
||||||
|
$payload['custom_fields'] = [];
|
||||||
|
foreach ($asset->model->fieldset->fields as $field) {
|
||||||
|
if (($field->display_audit=='1') && ($request->has($field->db_column))) {
|
||||||
|
if ($field->field_encrypted == '1') {
|
||||||
|
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||||
|
if (is_array($request->input($field->db_column))) {
|
||||||
|
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||||
|
} else {
|
||||||
|
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_array($request->input($field->db_column))) {
|
||||||
|
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||||
|
} else {
|
||||||
|
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$payload['custom_fields'][$field->db_column] = $request->input($field->db_column);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate custom fields
|
||||||
|
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
|
||||||
|
|
||||||
|
// Validate the rest of the data before we turn off the event dispatcher
|
||||||
|
if ($asset->isInvalid()) {
|
||||||
|
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
|
||||||
|
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
|
||||||
|
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
|
||||||
|
* the audit log entry we're creating through this controller.
|
||||||
|
*
|
||||||
|
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
|
||||||
|
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
|
||||||
|
* will bypass normal model-level validation that's usually handled at the observer)
|
||||||
|
*
|
||||||
|
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
|
||||||
|
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||||
|
*
|
||||||
|
* @see \App\Observers\AssetObserver::updating()
|
||||||
|
* @see \App\Models\Asset::save()
|
||||||
|
*/
|
||||||
|
|
||||||
|
$asset->unsetEventDispatcher();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
||||||
* We have to invoke this manually because of the unsetEventDispatcher() above.)
|
* We have to invoke this manually because of the unsetEventDispatcher() above.)
|
||||||
*/
|
*/
|
||||||
if ($asset->isValid() && $asset->save()) {
|
if ($asset->isValid() && $asset->save()) {
|
||||||
$asset->logAudit(request('note'), request('location_id'));
|
$asset->logAudit(request('note'), request('location_id'), null, $originalValues);
|
||||||
|
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/hardware/message.audit.success')));
|
||||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
|
||||||
'asset_tag' => e($asset->asset_tag),
|
|
||||||
'note' => e($request->input('note')),
|
|
||||||
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
|
|
||||||
], trans('admin/hardware/message.audit.success')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asset failed validation or was not able to be saved
|
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
|
||||||
'asset_tag' => e($asset->asset_tag),
|
|
||||||
'error' => $asset->getErrors()->first(),
|
|
||||||
], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// No matching asset for the asset tag that was passed.
|
// No matching asset for the asset tag that was passed.
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||||
'asset_tag' => e($request->input('asset_tag')),
|
|
||||||
'error' => trans('admin/hardware/message.audit.error'),
|
|
||||||
], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\ImageUploadRequest;
|
use App\Http\Requests\ImageUploadRequest;
|
||||||
|
use App\Http\Requests\UpdateAssetRequest;
|
||||||
use App\Models\Actionlog;
|
use App\Models\Actionlog;
|
||||||
use App\Http\Requests\UploadFileRequest;
|
use App\Http\Requests\UploadFileRequest;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
@ -390,26 +391,26 @@ class AssetsController extends Controller
|
||||||
$asset = $request->handleImages($asset);
|
$asset = $request->handleImages($asset);
|
||||||
|
|
||||||
// Update custom fields in the database.
|
// Update custom fields in the database.
|
||||||
// Validation for these fields is handlded through the AssetRequest form request
|
|
||||||
// FIXME: No idea why this is returning a Builder error on db_column_name.
|
// FIXME: No idea why this is returning a Builder error on db_column_name.
|
||||||
// Need to investigate and fix. Using static method for now.
|
// Need to investigate and fix. Using static method for now.
|
||||||
$model = AssetModel::find($request->get('model_id'));
|
$model = AssetModel::find($request->get('model_id'));
|
||||||
if (($model) && ($model->fieldset)) {
|
if (($model) && ($model->fieldset)) {
|
||||||
foreach ($model->fieldset->fields as $field) {
|
foreach ($model->fieldset->fields as $field) {
|
||||||
|
if ($request->has($field->db_column)) {
|
||||||
if ($field->field_encrypted == '1') {
|
if ($field->field_encrypted == '1') {
|
||||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||||
if (is_array($request->input($field->db_column))) {
|
if (is_array($request->input($field->db_column))) {
|
||||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||||
} else {
|
} else {
|
||||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (is_array($request->input($field->db_column))) {
|
|
||||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
|
||||||
} else {
|
} else {
|
||||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
if (is_array($request->input($field->db_column))) {
|
||||||
|
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||||
|
} else {
|
||||||
|
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -865,13 +866,6 @@ class AssetsController extends Controller
|
||||||
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
|
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function audit(Asset $asset)
|
|
||||||
{
|
|
||||||
$settings = Setting::getSettings();
|
|
||||||
$this->authorize('audit', Asset::class);
|
|
||||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
|
||||||
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dueForAudit()
|
public function dueForAudit()
|
||||||
{
|
{
|
||||||
|
@ -888,19 +882,59 @@ class AssetsController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function audit(Asset $asset)
|
||||||
|
{
|
||||||
|
$settings = Setting::getSettings();
|
||||||
|
$this->authorize('audit', Asset::class);
|
||||||
|
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||||
|
return view('hardware/audit')->with('asset', $asset)->with('item', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||||
|
}
|
||||||
|
|
||||||
public function auditStore(UploadFileRequest $request, Asset $asset)
|
public function auditStore(UploadFileRequest $request, Asset $asset)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->authorize('audit', Asset::class);
|
$this->authorize('audit', Asset::class);
|
||||||
|
|
||||||
$rules = [
|
$originalValues = $asset->getRawOriginal();
|
||||||
'location_id' => 'exists:locations,id|nullable|numeric',
|
|
||||||
'next_audit_date' => 'date|nullable',
|
|
||||||
];
|
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), $rules);
|
$asset->next_audit_date = $request->input('next_audit_date');
|
||||||
|
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
if ($validator->fails()) {
|
// Check to see if they checked the box to update the physical location,
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
|
// not just note it in the audit notes
|
||||||
|
if ($request->input('update_location') == '1') {
|
||||||
|
$asset->location_id = $request->input('location_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update custom fields in the database
|
||||||
|
if (($asset->model) && ($asset->model->fieldset)) {
|
||||||
|
foreach ($asset->model->fieldset->fields as $field) {
|
||||||
|
if (($field->display_audit=='1') && ($request->has($field->db_column))) {
|
||||||
|
if ($field->field_encrypted == '1') {
|
||||||
|
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||||
|
if (is_array($request->input($field->db_column))) {
|
||||||
|
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||||
|
} else {
|
||||||
|
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_array($request->input($field->db_column))) {
|
||||||
|
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||||
|
} else {
|
||||||
|
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate custom fields
|
||||||
|
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
|
||||||
|
|
||||||
|
// Validate the rest of the data before we turn off the event dispatcher
|
||||||
|
if ($asset->isInvalid()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -917,18 +951,11 @@ class AssetsController extends Controller
|
||||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||||
*
|
*
|
||||||
* @see \App\Observers\AssetObserver::updating()
|
* @see \App\Observers\AssetObserver::updating()
|
||||||
|
* @see \App\Models\Asset::save()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$asset->unsetEventDispatcher();
|
$asset->unsetEventDispatcher();
|
||||||
|
|
||||||
$asset->next_audit_date = $request->input('next_audit_date');
|
|
||||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
// Check to see if they checked the box to update the physical location,
|
|
||||||
// not just note it in the audit notes
|
|
||||||
if ($request->input('update_location') == '1') {
|
|
||||||
$asset->location_id = $request->input('location_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
||||||
|
@ -942,7 +969,7 @@ class AssetsController extends Controller
|
||||||
$file_name = $request->handleFile('private_uploads/audits/', 'audit-'.$asset->id, $request->file('image'));
|
$file_name = $request->handleFile('private_uploads/audits/', 'audit-'.$asset->id, $request->file('image'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
|
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
|
||||||
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ class CustomFieldsController extends Controller
|
||||||
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
|
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
|
||||||
"display_checkin" => $request->get("display_checkin", 0),
|
"display_checkin" => $request->get("display_checkin", 0),
|
||||||
"display_checkout" => $request->get("display_checkout", 0),
|
"display_checkout" => $request->get("display_checkout", 0),
|
||||||
|
"display_audit" => $request->get("display_audit", 0),
|
||||||
"created_by" => auth()->id()
|
"created_by" => auth()->id()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -250,6 +251,7 @@ class CustomFieldsController extends Controller
|
||||||
$field->show_in_requestable_list = $request->get("show_in_requestable_list", 0);
|
$field->show_in_requestable_list = $request->get("show_in_requestable_list", 0);
|
||||||
$field->display_checkin = $request->get("display_checkin", 0);
|
$field->display_checkin = $request->get("display_checkin", 0);
|
||||||
$field->display_checkout = $request->get("display_checkout", 0);
|
$field->display_checkout = $request->get("display_checkout", 0);
|
||||||
|
$field->display_audit = $request->get("display_audit", 0);
|
||||||
|
|
||||||
if ($request->get('format') == 'CUSTOM REGEX') {
|
if ($request->get('format') == 'CUSTOM REGEX') {
|
||||||
$field->format = e($request->get('custom_format'));
|
$field->format = e($request->get('custom_format'));
|
||||||
|
|
|
@ -243,7 +243,7 @@ class ReportsController extends Controller
|
||||||
|
|
||||||
$header = [
|
$header = [
|
||||||
trans('general.date'),
|
trans('general.date'),
|
||||||
trans('general.admin'),
|
trans('general.created_by'),
|
||||||
trans('general.action'),
|
trans('general.action'),
|
||||||
trans('general.type'),
|
trans('general.type'),
|
||||||
trans('general.item'),
|
trans('general.item'),
|
||||||
|
|
|
@ -10,19 +10,36 @@ trait MayContainCustomFields
|
||||||
// this gets called automatically on a form request
|
// this gets called automatically on a form request
|
||||||
public function withValidator($validator)
|
public function withValidator($validator)
|
||||||
{
|
{
|
||||||
// find the model
|
|
||||||
if ($this->method() == 'POST') {
|
// In case the model is being changed via form
|
||||||
$asset_model = AssetModel::find($this->model_id);
|
if (request()->has('model_id')!='') {
|
||||||
}
|
|
||||||
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
|
$asset_model = AssetModel::find(request()->input('model_id'));
|
||||||
$asset_model = $this->asset->model;
|
|
||||||
|
// or if we have it available to route-model-binding
|
||||||
|
} elseif ((request()->route('asset') && (request()->route('asset')->model_id))) {
|
||||||
|
|
||||||
|
$asset_model = AssetModel::find(request()->route('asset')->model_id);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if ($this->method() == 'POST') {
|
||||||
|
$asset_model = AssetModel::find($this->model_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
|
||||||
|
$asset_model = $this->asset->model;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// collect the custom fields in the request
|
// collect the custom fields in the request
|
||||||
$validator->after(function ($validator) use ($asset_model) {
|
$validator->after(function ($validator) use ($asset_model) {
|
||||||
$request_fields = $this->collect()->keys()->filter(function ($attributes) {
|
$request_fields = $this->collect()->keys()->filter(function ($attributes) {
|
||||||
return str_starts_with($attributes, '_snipeit_');
|
return str_starts_with($attributes, '_snipeit_');
|
||||||
});
|
});
|
||||||
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
|
|
||||||
|
// if there are custom fields, find the ones that don't exist on the model's fieldset and add an error to the validator's error bag
|
||||||
if (count($request_fields) > 0 && $validator->errors()->isEmpty()) {
|
if (count($request_fields) > 0 && $validator->errors()->isEmpty()) {
|
||||||
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
|
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
|
||||||
->each(function ($request_field_name) use ($request_fields, $validator) {
|
->each(function ($request_field_name) use ($request_fields, $validator) {
|
||||||
|
|
|
@ -50,6 +50,9 @@ class CustomFieldsTransformer
|
||||||
'display_in_user_view' => ($field->display_in_user_view =='1') ? true : false,
|
'display_in_user_view' => ($field->display_in_user_view =='1') ? true : false,
|
||||||
'auto_add_to_fieldsets' => ($field->auto_add_to_fieldsets == '1') ? true : false,
|
'auto_add_to_fieldsets' => ($field->auto_add_to_fieldsets == '1') ? true : false,
|
||||||
'show_in_listview' => ($field->show_in_listview == '1') ? true : false,
|
'show_in_listview' => ($field->show_in_listview == '1') ? true : false,
|
||||||
|
'display_checkin' => ($field->display_checkin == '1') ? true : false,
|
||||||
|
'display_checkout' => ($field->display_checkout == '1') ? true : false,
|
||||||
|
'display_audit' => ($field->display_audit == '1') ? true : false,
|
||||||
'created_at' => Helper::getFormattedDateObject($field->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($field->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($field->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($field->updated_at, 'datetime'),
|
||||||
];
|
];
|
||||||
|
|
|
@ -57,7 +57,9 @@ class Actionlog extends SnipeModel
|
||||||
'user_agent',
|
'user_agent',
|
||||||
'item_type',
|
'item_type',
|
||||||
'target_type',
|
'target_type',
|
||||||
'action_source'
|
'action_source',
|
||||||
|
'created_at',
|
||||||
|
'action_date',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,7 +71,25 @@ class Actionlog extends SnipeModel
|
||||||
'company' => ['name'],
|
'company' => ['name'],
|
||||||
'adminuser' => ['first_name','last_name','username', 'email'],
|
'adminuser' => ['first_name','last_name','username', 'email'],
|
||||||
'user' => ['first_name','last_name','username', 'email'],
|
'user' => ['first_name','last_name','username', 'email'],
|
||||||
'assets' => ['asset_tag','name', 'serial'],
|
'assets' => ['asset_tag','name', 'serial', 'order_number', 'notes', 'purchase_date'],
|
||||||
|
'assets.model' => ['name', 'model_number', 'eol', 'notes'],
|
||||||
|
'assets.model.category' => ['name', 'notes'],
|
||||||
|
'assets.model.manufacturer' => ['name', 'notes'],
|
||||||
|
'licenses' => ['name', 'serial', 'notes', 'order_number', 'license_email', 'license_name', 'purchase_order', 'purchase_date'],
|
||||||
|
'licenses.category' => ['name', 'notes'],
|
||||||
|
'licenses.supplier' => ['name'],
|
||||||
|
'consumables' => ['name', 'notes', 'order_number', 'model_number', 'item_no', 'purchase_date'],
|
||||||
|
'consumables.category' => ['name', 'notes'],
|
||||||
|
'consumables.location' => ['name', 'notes'],
|
||||||
|
'consumables.supplier' => ['name', 'notes'],
|
||||||
|
'components' => ['name', 'notes', 'purchase_date'],
|
||||||
|
'components.category' => ['name', 'notes'],
|
||||||
|
'components.location' => ['name', 'notes'],
|
||||||
|
'components.supplier' => ['name', 'notes'],
|
||||||
|
'accessories' => ['name', 'purchase_date'],
|
||||||
|
'accessories.category' => ['name'],
|
||||||
|
'accessories.location' => ['name', 'notes'],
|
||||||
|
'accessories.supplier' => ['name', 'notes'],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,6 +154,54 @@ class Actionlog extends SnipeModel
|
||||||
return $this->hasMany(\App\Models\Asset::class, 'id', 'item_id');
|
return $this->hasMany(\App\Models\Asset::class, 'id', 'item_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the actionlog -> license relationship
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v3.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function licenses()
|
||||||
|
{
|
||||||
|
return $this->hasMany(\App\Models\License::class, 'id', 'item_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the actionlog -> consumable relationship
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v3.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function consumables()
|
||||||
|
{
|
||||||
|
return $this->hasMany(\App\Models\Consumable::class, 'id', 'item_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the actionlog -> consumable relationship
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v3.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function accessories()
|
||||||
|
{
|
||||||
|
return $this->hasMany(\App\Models\Accessory::class, 'id', 'item_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the actionlog -> components relationship
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v3.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function components()
|
||||||
|
{
|
||||||
|
return $this->hasMany(\App\Models\Component::class, 'id', 'item_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes the actionlog -> item type relationship
|
* Establishes the actionlog -> item type relationship
|
||||||
*
|
*
|
||||||
|
|
|
@ -213,6 +213,31 @@ class Asset extends Depreciable
|
||||||
$this->attributes['expected_checkin'] = $value;
|
$this->attributes['expected_checkin'] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function customFieldValidationRules()
|
||||||
|
{
|
||||||
|
|
||||||
|
$customFieldValidationRules = [];
|
||||||
|
|
||||||
|
if (($this->model) && ($this->model->fieldset)) {
|
||||||
|
|
||||||
|
foreach ($this->model->fieldset->fields as $field) {
|
||||||
|
|
||||||
|
if ($field->format == 'BOOLEAN'){
|
||||||
|
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$customFieldValidationRules += $this->model->fieldset->validation_rules();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $customFieldValidationRules;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handles the custom field validation for assets
|
* This handles the custom field validation for assets
|
||||||
*
|
*
|
||||||
|
@ -220,29 +245,7 @@ class Asset extends Depreciable
|
||||||
*/
|
*/
|
||||||
public function save(array $params = [])
|
public function save(array $params = [])
|
||||||
{
|
{
|
||||||
if ($this->model_id != '') {
|
$this->rules += $this->customFieldValidationRules();
|
||||||
$model = AssetModel::find($this->model_id);
|
|
||||||
|
|
||||||
if (($model) && ($model->fieldset)) {
|
|
||||||
|
|
||||||
foreach ($model->fieldset->fields as $field){
|
|
||||||
if($field->format == 'BOOLEAN'){
|
|
||||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->rules += $model->fieldset->validation_rules();
|
|
||||||
|
|
||||||
if ($this->model->fieldset){
|
|
||||||
foreach ($this->model->fieldset->fields as $field){
|
|
||||||
if($field->format == 'BOOLEAN'){
|
|
||||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::save($params);
|
return parent::save($params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +257,7 @@ class Asset extends Depreciable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the warranty expiration date as Carbon object
|
* Returns the warranty expiration date as Carbon object
|
||||||
* @return \Carbon|null
|
* @return \Carbon\Carbon|null
|
||||||
*/
|
*/
|
||||||
public function getWarrantyExpiresAttribute()
|
public function getWarrantyExpiresAttribute()
|
||||||
{
|
{
|
||||||
|
@ -687,6 +690,21 @@ class Asset extends Depreciable
|
||||||
->withTrashed();
|
->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of audits for this asset
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v2.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function audits()
|
||||||
|
{
|
||||||
|
return $this->assetlog()->where('action_type', '=', 'audit')
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of checkins for this asset
|
* Get the list of checkins for this asset
|
||||||
*
|
*
|
||||||
|
|
|
@ -220,9 +220,41 @@ trait Loggable
|
||||||
* @since [v4.0]
|
* @since [v4.0]
|
||||||
* @return \App\Models\Actionlog
|
* @return \App\Models\Actionlog
|
||||||
*/
|
*/
|
||||||
public function logAudit($note, $location_id, $filename = null)
|
public function logAudit($note, $location_id, $filename = null, $originalValues = [])
|
||||||
{
|
{
|
||||||
|
|
||||||
$log = new Actionlog;
|
$log = new Actionlog;
|
||||||
|
|
||||||
|
if (static::class == Asset::class) {
|
||||||
|
if ($asset = Asset::find($log->item_id)) {
|
||||||
|
// add the custom fields that were changed
|
||||||
|
if ($asset->model->fieldset) {
|
||||||
|
$fields_array = [];
|
||||||
|
foreach ($asset->model->fieldset->fields as $field) {
|
||||||
|
if ($field->display_audit == 1) {
|
||||||
|
$fields_array[$field->db_column] = $asset->{$field->db_column};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$changed = [];
|
||||||
|
|
||||||
|
unset($originalValues['updated_at'], $originalValues['last_audit_date']);
|
||||||
|
foreach ($originalValues as $key => $value) {
|
||||||
|
|
||||||
|
if ($value != $this->getAttributes()[$key]) {
|
||||||
|
$changed[$key]['old'] = $value;
|
||||||
|
$changed[$key]['new'] = $this->getAttributes()[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($changed)){
|
||||||
|
$log->log_meta = json_encode($changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$location = Location::find($location_id);
|
$location = Location::find($location_id);
|
||||||
if (static::class == LicenseSeat::class) {
|
if (static::class == LicenseSeat::class) {
|
||||||
$log->item_type = License::class;
|
$log->item_type = License::class;
|
||||||
|
@ -235,6 +267,7 @@ trait Loggable
|
||||||
$log->note = $note;
|
$log->note = $note;
|
||||||
$log->created_by = auth()->id();
|
$log->created_by = auth()->id();
|
||||||
$log->filename = $filename;
|
$log->filename = $filename;
|
||||||
|
$log->action_date = date('Y-m-d H:i:s');
|
||||||
$log->logaction('audit');
|
$log->logaction('audit');
|
||||||
|
|
||||||
$params = [
|
$params = [
|
||||||
|
@ -276,6 +309,7 @@ trait Loggable
|
||||||
$log->item_id = $this->id;
|
$log->item_id = $this->id;
|
||||||
}
|
}
|
||||||
$log->location_id = null;
|
$log->location_id = null;
|
||||||
|
$log->action_date = date('Y-m-d H:i:s');
|
||||||
$log->note = $note;
|
$log->note = $note;
|
||||||
$log->created_by = $created_by;
|
$log->created_by = $created_by;
|
||||||
$log->logaction('create');
|
$log->logaction('create');
|
||||||
|
@ -303,6 +337,7 @@ trait Loggable
|
||||||
$log->note = $note;
|
$log->note = $note;
|
||||||
$log->target_id = null;
|
$log->target_id = null;
|
||||||
$log->created_at = date('Y-m-d H:i:s');
|
$log->created_at = date('Y-m-d H:i:s');
|
||||||
|
$log->action_date = date('Y-m-d H:i:s');
|
||||||
$log->filename = $filename;
|
$log->filename = $filename;
|
||||||
$log->logaction('uploaded');
|
$log->logaction('uploaded');
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ class AccessoryPresenter extends Presenter
|
||||||
'field' => 'created_by',
|
'field' => 'created_by',
|
||||||
'searchable' => false,
|
'searchable' => false,
|
||||||
'sortable' => false,
|
'sortable' => false,
|
||||||
'title' => trans('general.admin'),
|
'title' => trans('general.created_by'),
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
'formatter' => 'usersLinkObjFormatter',
|
'formatter' => 'usersLinkObjFormatter',
|
||||||
],
|
],
|
||||||
|
|
|
@ -102,6 +102,10 @@ class ActionlogPresenter extends Presenter
|
||||||
return 'fas fa-sticky-note';
|
return 'fas fa-sticky-note';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->action_type == 'audit') {
|
||||||
|
return 'fas fa-clipboard-check';
|
||||||
|
}
|
||||||
|
|
||||||
return 'fa-solid fa-rotate-right';
|
return 'fa-solid fa-rotate-right';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,7 +262,7 @@ class LocationPresenter extends Presenter
|
||||||
'field' => 'created_by',
|
'field' => 'created_by',
|
||||||
'searchable' => false,
|
'searchable' => false,
|
||||||
'sortable' => false,
|
'sortable' => false,
|
||||||
'title' => trans('general.admin'),
|
'title' => trans('general.created_by'),
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
'formatter' => 'usersLinkObjFormatter',
|
'formatter' => 'usersLinkObjFormatter',
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
return array (
|
return array (
|
||||||
'app_version' => 'v8.0.4',
|
'app_version' => 'v8.0.4',
|
||||||
'full_app_version' => 'v8.0.4 - build 17333-gaf408bb45',
|
'full_app_version' => 'v8.0.4 - build 17400-gb0b5a9669',
|
||||||
'build_version' => '17333',
|
'build_version' => '17400',
|
||||||
'prerelease_version' => '',
|
'prerelease_version' => '',
|
||||||
'hash_version' => 'gaf408bb45',
|
'hash_version' => 'gb0b5a9669',
|
||||||
'full_hash' => 'v8.0.4-135-gaf408bb45',
|
'full_hash' => 'v8.0.4-202-gb0b5a9669',
|
||||||
'branch' => 'master',
|
'branch' => 'master',
|
||||||
);
|
);
|
|
@ -385,6 +385,12 @@ class UserFactory extends Factory
|
||||||
return $this->appendPermission(['suppliers.delete' => '1']);
|
return $this->appendPermission(['suppliers.delete' => '1']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function auditAssets()
|
||||||
|
{
|
||||||
|
return $this->appendPermission(['assets.audit' => '1']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function appendPermission(array $permission)
|
private function appendPermission(array $permission)
|
||||||
{
|
{
|
||||||
return $this->state(function ($currentState) use ($permission) {
|
return $this->state(function ($currentState) use ($permission) {
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('custom_fields', function (Blueprint $table) {
|
||||||
|
$table->boolean('display_audit')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('custom_fields', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('display_audit');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/css/dist/all.css
vendored
2
public/css/dist/all.css
vendored
File diff suppressed because one or more lines are too long
|
@ -2,8 +2,8 @@
|
||||||
"/js/build/app.js": "/js/build/app.js?id=970945c192cb3217d5f371a1931d7d77",
|
"/js/build/app.js": "/js/build/app.js?id=970945c192cb3217d5f371a1931d7d77",
|
||||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=d34ae2483cbe2c77478c45f4006eba55",
|
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=d34ae2483cbe2c77478c45f4006eba55",
|
||||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=6bf62cdec2477f3176df196fd0c99662",
|
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=6bf62cdec2477f3176df196fd0c99662",
|
||||||
"/css/build/overrides.css": "/css/build/overrides.css?id=3ca26335f461f9d198f3c9869dc0ce52",
|
"/css/build/overrides.css": "/css/build/overrides.css?id=12c469a76750af9fee1c92722c563e7e",
|
||||||
"/css/build/app.css": "/css/build/app.css?id=35c2aaddd23e600b56f77c95b04523ac",
|
"/css/build/app.css": "/css/build/app.css?id=1aed878e9211529befa4fdeed11ac52e",
|
||||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
|
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
|
||||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
|
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=53edc92eb2d272744bc7404ec259930e",
|
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=53edc92eb2d272744bc7404ec259930e",
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=091d9625203be910caca3e229afe438f",
|
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=091d9625203be910caca3e229afe438f",
|
||||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=18787b3f00a3be7be38ee4e26cbd2a07",
|
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=18787b3f00a3be7be38ee4e26cbd2a07",
|
||||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||||
"/css/dist/all.css": "/css/dist/all.css?id=119a35ca4c300a14baefbdcbb203d08e",
|
"/css/dist/all.css": "/css/dist/all.css?id=d092c5407b7e50f6a01e5e918ec737ee",
|
||||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||||
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
||||||
|
|
|
@ -886,7 +886,7 @@ th.css-history > .th-inner::before {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group.has-error label {
|
.form-group.has-error label, .form-group.has-error .help-block {
|
||||||
color: #a94442;
|
color: #a94442;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,5 +59,6 @@ return [
|
||||||
'encrypted_options' => 'This field is encrypted, so some display options will not be available.',
|
'encrypted_options' => 'This field is encrypted, so some display options will not be available.',
|
||||||
'display_checkin' => 'Display in checkin forms',
|
'display_checkin' => 'Display in checkin forms',
|
||||||
'display_checkout' => 'Display in checkout forms',
|
'display_checkout' => 'Display in checkout forms',
|
||||||
|
'display_audit' => 'Display in audit forms',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -31,6 +31,7 @@ return [
|
||||||
'accept_assets_menu' => 'Accept Assets',
|
'accept_assets_menu' => 'Accept Assets',
|
||||||
'accept_item' => 'Accept Item',
|
'accept_item' => 'Accept Item',
|
||||||
'audit' => 'Audit',
|
'audit' => 'Audit',
|
||||||
|
'audits' => 'Audits',
|
||||||
'audit_report' => 'Audit Log',
|
'audit_report' => 'Audit Log',
|
||||||
'assets' => 'Assets',
|
'assets' => 'Assets',
|
||||||
'assets_audited' => 'assets audited',
|
'assets_audited' => 'assets audited',
|
||||||
|
|
|
@ -125,7 +125,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-2" data-visible="false" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.record_created') }}</th>
|
<th class="col-sm-2" data-visible="false" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.record_created') }}</th>
|
||||||
<th class="col-sm-2"data-visible="true" data-sortable="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th class="col-sm-2"data-visible="true" data-sortable="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||||
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||||
|
|
|
@ -421,7 +421,7 @@
|
||||||
{{ trans('general.date') }}
|
{{ trans('general.date') }}
|
||||||
</th>
|
</th>
|
||||||
<th data-searchable="false" data-sortable="false" data-field="note">{{ trans('general.notes') }}</th>
|
<th data-searchable="false" data-sortable="false" data-field="note">{{ trans('general.notes') }}</th>
|
||||||
<th data-searchable="false" data-sortable="false" data-field="admin">{{ trans('general.admin') }}</th>
|
<th data-searchable="false" data-sortable="false" data-field="admin">{{ trans('general.created_by') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
|
@ -472,7 +472,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||||
<th data-visible="true" data-field="action_date" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
<th data-visible="true" data-field="action_date" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||||
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||||
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||||
|
|
|
@ -224,6 +224,14 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Show in Audit Form -->
|
||||||
|
<div class="col-md-9 col-md-offset-3" id="display_audit" style="padding-bottom: 10px;">
|
||||||
|
<label class="form-control">
|
||||||
|
<input type="checkbox" name="display_audit" aria-label="display_audit" value="1" {{ (old('display_audit') || $field->display_audit) ? ' checked="checked"' : '' }}>
|
||||||
|
{{ trans('admin/custom_fields/general.display_audit') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Show in View All Assets profile view -->
|
<!-- Show in View All Assets profile view -->
|
||||||
<div class="col-md-9 col-md-offset-3" id="display_in_user_view">
|
<div class="col-md-9 col-md-offset-3" id="display_in_user_view">
|
||||||
|
|
|
@ -196,6 +196,14 @@
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
|
<th data-sortable="true" data-visible="false" data-searchable="false" class="text-center"
|
||||||
|
data-tooltip="{{ trans('admin/custom_fields/general.display_audit') }}">
|
||||||
|
<x-icon type="due" />
|
||||||
|
<span class="sr-only">
|
||||||
|
{{ trans('admin/custom_fields/general.display_audit') }}
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
|
||||||
|
|
||||||
<th data-sortable="true" data-searchable="true" class="text-center">{{ trans('admin/custom_fields/general.field_element_short') }}</th>
|
<th data-sortable="true" data-searchable="true" class="text-center">{{ trans('admin/custom_fields/general.field_element_short') }}</th>
|
||||||
|
|
||||||
|
@ -227,6 +235,7 @@
|
||||||
<td class="text-center">{!! ($field->is_unique=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
<td class="text-center">{!! ($field->is_unique=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||||
<td class="text-center">{!! ($field->display_checkin=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
<td class="text-center">{!! ($field->display_checkin=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||||
<td class="text-center">{!! ($field->display_checkout=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
<td class="text-center">{!! ($field->display_checkout=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||||
|
<td class="text-center">{!! ($field->display_audit=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||||
<td>{{ $field->element }}</td>
|
<td>{{ $field->element }}</td>
|
||||||
<td>
|
<td>
|
||||||
@foreach($field->fieldset as $fieldset)
|
@foreach($field->fieldset as $fieldset)
|
||||||
|
|
|
@ -115,6 +115,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Custom fields -->
|
||||||
|
@include("models/custom_fields_form", [
|
||||||
|
'model' => $asset->model,
|
||||||
|
'show_display_checkout_fields' => 'true'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
<!-- Note -->
|
<!-- Note -->
|
||||||
<div class="form-group{{ $errors->has('note') ? ' has-error' : '' }}">
|
<div class="form-group{{ $errors->has('note') ? ' has-error' : '' }}">
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
var formData = $('#audit-form').serializeArray();
|
var formData = $('#audit-form').serializeArray();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "{{ route('api.asset.audit') }}",
|
url: "{{ route('api.asset.audit.legacy') }}",
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"X-Requested-With": 'XMLHttpRequest',
|
"X-Requested-With": 'XMLHttpRequest',
|
||||||
|
|
|
@ -111,6 +111,22 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
|
@if ($asset->audits->count() > 0)
|
||||||
|
<li>
|
||||||
|
<a href="#audits" data-toggle="tab" data-tooltip="true">
|
||||||
|
|
||||||
|
<span class="hidden-lg hidden-md">
|
||||||
|
<i class="fas fa-clipboard-check fa-2x"></i>
|
||||||
|
</span>
|
||||||
|
<span class="hidden-xs hidden-sm">
|
||||||
|
{{ trans('general.audits') }}
|
||||||
|
{!! ($asset->audits()->count() > 0 ) ? '<span class="badge badge-secondary">'.number_format($asset->audits()->count()).'</span>' : '' !!}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="#history" data-toggle="tab">
|
<a href="#history" data-toggle="tab">
|
||||||
<span class="hidden-lg hidden-md">
|
<span class="hidden-lg hidden-md">
|
||||||
|
@ -1390,7 +1406,53 @@
|
||||||
</div> <!-- /.row -->
|
</div> <!-- /.row -->
|
||||||
</div> <!-- /.tab-pane maintenances -->
|
</div> <!-- /.tab-pane maintenances -->
|
||||||
|
|
||||||
<div class="tab-pane fade" id="history">
|
|
||||||
|
<div class="tab-pane fade" id="audits">
|
||||||
|
<!-- checked out assets table -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<table
|
||||||
|
class="table table-striped snipe-table"
|
||||||
|
id="asseAuditHistory"
|
||||||
|
data-pagination="true"
|
||||||
|
data-id-table="asseAuditHistory"
|
||||||
|
data-search="true"
|
||||||
|
data-side-pagination="server"
|
||||||
|
data-show-columns="true"
|
||||||
|
data-show-fullscreen="true"
|
||||||
|
data-show-refresh="true"
|
||||||
|
data-sort-order="desc"
|
||||||
|
data-sort-name="created_at"
|
||||||
|
data-show-export="true"
|
||||||
|
data-export-options='{
|
||||||
|
"fileName": "export-asset-{{ $asset->id }}-audits",
|
||||||
|
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
data-url="{{ route('api.activity.index', ['item_id' => $asset->id, 'item_type' => 'asset', 'action_type' => 'audit']) }}"
|
||||||
|
data-cookie-id-table="assetHistory"
|
||||||
|
data-cookie="true">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||||
|
<th data-visible="true" data-field="created_at" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||||
|
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
|
<th class="col-sm-2" data-field="file" data-sortable="true" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||||
|
<th data-field="note">{{ trans('general.notes') }}</th>
|
||||||
|
<th data-visible="false" data-field="file" data-visible="false" data-formatter="fileUploadFormatter">{{ trans('general.download') }}</th>
|
||||||
|
<th data-field="log_meta" data-visible="true" data-formatter="changeLogFormatter">{{ trans('admin/hardware/table.changed')}}</th>
|
||||||
|
<th data-field="remote_ip" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_ip') }}</th>
|
||||||
|
<th data-field="user_agent" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_user_agent') }}</th>
|
||||||
|
<th data-field="action_source" data-visible="false" data-sortable="true">{{ trans('general.action_source') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /.row -->
|
||||||
|
</div> <!-- /.tab-pane history -->
|
||||||
|
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="history">
|
||||||
<!-- checked out assets table -->
|
<!-- checked out assets table -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
@ -1419,7 +1481,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||||
<th data-visible="true" data-field="action_date" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
<th data-visible="true" data-field="action_date" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||||
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||||
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||||
|
|
|
@ -526,7 +526,7 @@ dir="{{ Helper::determineLanguageDirection() }}">
|
||||||
@can('audit', \App\Models\Asset::class)
|
@can('audit', \App\Models\Asset::class)
|
||||||
<li{!! (Request::is('hardware/audit/due') ? ' class="active"' : '') !!}>
|
<li{!! (Request::is('hardware/audit/due') ? ' class="active"' : '') !!}>
|
||||||
<a href="{{ route('assets.audit.due') }}">
|
<a href="{{ route('assets.audit.due') }}">
|
||||||
<x-icon type="due" class="text-yellow fa-fw"/>
|
<x-icon type="audit" class="text-yellow fa-fw"/>
|
||||||
{{ trans('general.audit_due') }}
|
{{ trans('general.audit_due') }}
|
||||||
<span class="badge">{{ (isset($total_due_and_overdue_for_audit)) ? $total_due_and_overdue_for_audit : '' }}</span>
|
<span class="badge">{{ (isset($total_due_and_overdue_for_audit)) ? $total_due_and_overdue_for_audit : '' }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -498,7 +498,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-sm-2" data-visible="false" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.record_created') }}</th>
|
<th class="col-sm-2" data-visible="false" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.record_created') }}</th>
|
||||||
<th class="col-sm-2"data-visible="true" data-sortable="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th class="col-sm-2"data-visible="true" data-sortable="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||||
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||||
|
|
|
@ -434,7 +434,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||||
<th class="col-sm-2" data-visible="true" data-field="action_date" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
<th class="col-sm-2" data-visible="true" data-field="action_date" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||||
<th class="col-sm-1" data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th class="col-sm-1" data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th class="col-sm-1" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
<th class="col-sm-1" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||||
<th class="col-sm-2" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
<th class="col-sm-2" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||||
<th class="col-sm-2" data-visible="true" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
<th class="col-sm-2" data-visible="true" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||||
|
|
|
@ -101,10 +101,4 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
<script nonce="{{ csrf_token() }}">
|
|
||||||
// We have to re-call the tooltip since this is pulled in after the DOM has loaded
|
|
||||||
$('[data-tooltip="true"]').tooltip({
|
|
||||||
container: 'body',
|
|
||||||
animation: true,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -341,7 +341,7 @@
|
||||||
|
|
||||||
|
|
||||||
function hardwareAuditFormatter(value, row) {
|
function hardwareAuditFormatter(value, row) {
|
||||||
return '<a href="{{ config('app.url') }}/hardware/audit/' + row.id + '/" class="btn btn-sm bg-yellow" data-tooltip="true" title="Audit this item">{{ trans('general.audit') }}</a>';
|
return '<a href="{{ config('app.url') }}/hardware/' + row.id + '/audit" class="btn btn-sm bg-yellow" data-tooltip="true" title="Audit this item">{{ trans('general.audit') }}</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<th data-sortable="true" data-field="location" data-formatter="deployedLocationFormatter" data-visible="false">{{ trans('general.location') }}</th>
|
<th data-sortable="true" data-field="location" data-formatter="deployedLocationFormatter" data-visible="false">{{ trans('general.location') }}</th>
|
||||||
<th data-sortable="true" data-field="rtd_location" data-formatter="deployedLocationFormatter" data-visible="false">{{ trans('admin/hardware/form.default_location') }}</th>
|
<th data-sortable="true" data-field="rtd_location" data-formatter="deployedLocationFormatter" data-visible="false">{{ trans('admin/hardware/form.default_location') }}</th>
|
||||||
<th data-searchable="true" data-sortable="true" data-field="is_warranty" data-formatter="trueFalseFormatter">{{ trans('admin/asset_maintenances/table.is_warranty') }}</th>
|
<th data-searchable="true" data-sortable="true" data-field="is_warranty" data-formatter="trueFalseFormatter">{{ trans('admin/asset_maintenances/table.is_warranty') }}</th>
|
||||||
<th data-searchable="true" data-sortable="true" data-field="user_id" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th data-searchable="true" data-sortable="true" data-field="user_id" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th data-searchable="true" data-sortable="true" data-field="notes" data-visible="false">{{ trans('admin/asset_maintenances/form.notes') }}</th>
|
<th data-searchable="true" data-sortable="true" data-field="notes" data-visible="false">{{ trans('admin/asset_maintenances/form.notes') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -1003,7 +1003,7 @@
|
||||||
<th data-field="signature_file" data-visible="false" data-formatter="imageFormatter">{{ trans('general.signature') }}</th>
|
<th data-field="signature_file" data-visible="false" data-formatter="imageFormatter">{{ trans('general.signature') }}</th>
|
||||||
@endif
|
@endif
|
||||||
<th data-field="item.serial" data-visible="false">{{ trans('admin/hardware/table.serial') }}</th>
|
<th data-field="item.serial" data-visible="false">{{ trans('admin/hardware/table.serial') }}</th>
|
||||||
<th data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
<th data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||||
<th data-field="remote_ip" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_ip') }}</th>
|
<th data-field="remote_ip" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_ip') }}</th>
|
||||||
<th data-field="user_agent" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_user_agent') }}</th>
|
<th data-field="user_agent" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_user_agent') }}</th>
|
||||||
<th data-field="action_source" data-visible="false" data-sortable="true">{{ trans('general.action_source') }}</th>
|
<th data-field="action_source" data-visible="false" data-sortable="true">{{ trans('general.action_source') }}</th>
|
||||||
|
|
|
@ -511,8 +511,17 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
|
||||||
->where(['action' => 'audit|audits|checkins', 'upcoming_status' => 'due|overdue|due-or-overdue']);
|
->where(['action' => 'audit|audits|checkins', 'upcoming_status' => 'due|overdue|due-or-overdue']);
|
||||||
|
|
||||||
|
|
||||||
|
// Legacy URL for audit
|
||||||
|
Route::post('audit',
|
||||||
|
[
|
||||||
|
Api\AssetsController::class,
|
||||||
|
'audit'
|
||||||
|
]
|
||||||
|
)->name('api.asset.audit.legacy');
|
||||||
|
|
||||||
Route::post('audit',
|
|
||||||
|
// Newer url for audit
|
||||||
|
Route::post('{asset}/audit',
|
||||||
[
|
[
|
||||||
Api\AssetsController::class,
|
Api\AssetsController::class,
|
||||||
'audit'
|
'audit'
|
||||||
|
|
|
@ -63,14 +63,14 @@ Route::group(
|
||||||
->push(trans_choice('general.checkin_due_days', Setting::getSettings()->due_checkin_days, ['days' => Setting::getSettings()->due_checkin_days]), route('assets.audit.due'))
|
->push(trans_choice('general.checkin_due_days', Setting::getSettings()->due_checkin_days, ['days' => Setting::getSettings()->due_checkin_days]), route('assets.audit.due'))
|
||||||
);
|
);
|
||||||
|
|
||||||
Route::get('audit/{asset}', [AssetsController::class, 'audit'])
|
Route::get('{asset}/audit', [AssetsController::class, 'audit'])
|
||||||
->name('asset.audit.create')
|
->name('asset.audit.create')
|
||||||
->breadcrumbs(fn (Trail $trail, Asset $asset) =>
|
->breadcrumbs(fn (Trail $trail, Asset $asset) =>
|
||||||
$trail->parent('hardware.show', $asset)
|
$trail->parent('hardware.show', $asset)
|
||||||
->push(trans('general.audit'))
|
->push(trans('general.audit'))
|
||||||
);
|
);
|
||||||
|
|
||||||
Route::post('audit/{asset}',
|
Route::post('{asset}/audit',
|
||||||
[AssetsController::class, 'auditStore']
|
[AssetsController::class, 'auditStore']
|
||||||
)->name('asset.audit.store');
|
)->name('asset.audit.store');
|
||||||
|
|
||||||
|
|
78
tests/Feature/Assets/Api/AuditAssetTest.php
Normal file
78
tests/Feature/Assets/Api/AuditAssetTest.php
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Assets\Api;
|
||||||
|
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\AssetModel;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Location;
|
||||||
|
use App\Models\Statuslabel;
|
||||||
|
use App\Models\Supplier;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\CustomField;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AuditAssetTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testThatANonExistentAssetIdReturnsError()
|
||||||
|
{
|
||||||
|
$this->actingAsForApi(User::factory()->auditAssets()->create())
|
||||||
|
->postJson(route('api.asset.audit', 123456789))
|
||||||
|
->assertStatusMessageIs('error');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRequiresPermissionToAuditAsset()
|
||||||
|
{
|
||||||
|
$asset = Asset::factory()->create();
|
||||||
|
$this->actingAsForApi(User::factory()->create())
|
||||||
|
->postJson(route('api.asset.audit', $asset))
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLegacyAssetAuditIsSaved()
|
||||||
|
{
|
||||||
|
$asset = Asset::factory()->create();
|
||||||
|
$this->actingAsForApi(User::factory()->auditAssets()->create())
|
||||||
|
->postJson(route('api.asset.audit.legacy'), [
|
||||||
|
'asset_tag' => $asset->asset_tag,
|
||||||
|
'note' => 'test',
|
||||||
|
])
|
||||||
|
->assertStatusMessageIs('success')
|
||||||
|
->assertJson(
|
||||||
|
[
|
||||||
|
'messages' =>trans('admin/hardware/message.audit.success'),
|
||||||
|
'payload' => [
|
||||||
|
'id' => $asset->id,
|
||||||
|
'asset_tag' => $asset->asset_tag,
|
||||||
|
'note' => 'test'
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testAssetAuditIsSaved()
|
||||||
|
{
|
||||||
|
$asset = Asset::factory()->create();
|
||||||
|
$this->actingAsForApi(User::factory()->auditAssets()->create())
|
||||||
|
->postJson(route('api.asset.audit', $asset), [
|
||||||
|
'note' => 'test'
|
||||||
|
])
|
||||||
|
->assertStatusMessageIs('success')
|
||||||
|
->assertJson(
|
||||||
|
[
|
||||||
|
'messages' =>trans('admin/hardware/message.audit.success'),
|
||||||
|
'payload' => [
|
||||||
|
'id' => $asset->id,
|
||||||
|
'asset_tag' => $asset->asset_tag,
|
||||||
|
'note' => 'test'
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
34
tests/Feature/Assets/Ui/AuditAssetTest.php
Normal file
34
tests/Feature/Assets/Ui/AuditAssetTest.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Assets\Ui;
|
||||||
|
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AuditAssetTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testPermissionRequiredToCreateAssetModel()
|
||||||
|
{
|
||||||
|
$this->actingAs(User::factory()->create())
|
||||||
|
->get(route('clone/hardware', Asset::factory()->create()))
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPageCanBeAccessed(): void
|
||||||
|
{
|
||||||
|
$this->actingAs(User::factory()->auditAssets()->create())
|
||||||
|
->get(route('asset.audit.create', Asset::factory()->create()))
|
||||||
|
->assertStatus(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAssetCanBeAudited()
|
||||||
|
{
|
||||||
|
$response = $this->actingAs(User::factory()->auditAssets()->create())
|
||||||
|
->post(route('asset.audit.store', Asset::factory()->create()))
|
||||||
|
->assertStatus(302)
|
||||||
|
->assertRedirect(route('assets.audit.due'));
|
||||||
|
|
||||||
|
$this->followRedirects($response)->assertSee('success');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue