mirror of
https://github.com/snipe/snipe-it.git
synced 2025-03-05 20:52:15 -08:00
Merge branch 'develop' into snipeit_v7
Had to do a lot of conflict work here, so this could get ugly :(
This commit is contained in:
commit
8f2a17585e
|
@ -75,7 +75,12 @@ class Handler extends ExceptionHandler
|
||||||
|
|
||||||
// Handle SCIM exceptions
|
// Handle SCIM exceptions
|
||||||
if ($e instanceof SCIMException) {
|
if ($e instanceof SCIMException) {
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Invalid SCIM Request'), 400);
|
try {
|
||||||
|
$e->report(); // logs as 'debug', so shouldn't get too noisy
|
||||||
|
} catch(\Exception $reportException) {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
return $e->render($request); // ALL SCIMExceptions have the 'render()' method
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle standard requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date)
|
// Handle standard requests that fail because Carbon cannot parse the date on validation (when a submitted date value is definitely not a date)
|
||||||
|
|
|
@ -115,7 +115,7 @@ class AssetsController extends Controller
|
||||||
$allowed_columns[] = $field->db_column_name();
|
$allowed_columns[] = $field->db_column_name();
|
||||||
}
|
}
|
||||||
|
|
||||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'), 'company_id', 'assets')
|
$assets = Asset::select('assets.*')
|
||||||
->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo',
|
->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo',
|
||||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
|
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
|
||||||
|
|
||||||
|
@ -125,6 +125,8 @@ class AssetsController extends Controller
|
||||||
$assets->InModelList($non_deprecable_models->toArray());
|
$assets->InModelList($non_deprecable_models->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// These are used by the API to query against specific ID numbers.
|
// These are used by the API to query against specific ID numbers.
|
||||||
// They are also used by the individual searches on detail pages like
|
// They are also used by the individual searches on detail pages like
|
||||||
// locations, etc.
|
// locations, etc.
|
||||||
|
@ -136,12 +138,11 @@ class AssetsController extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
$assets->ByFilter($filter);
|
||||||
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : abs($request->input('offset'));
|
} elseif ($request->filled('search')) {
|
||||||
$limit = app('api_limit_value');
|
$assets->TextSearch($request->input('search'));
|
||||||
|
}
|
||||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
// This is used by the audit reporting routes
|
// This is used by the audit reporting routes
|
||||||
if (Gate::allows('audit', Asset::class)) {
|
if (Gate::allows('audit', Asset::class)) {
|
||||||
|
@ -156,7 +157,6 @@ class AssetsController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This is used by the sidenav, mostly
|
// This is used by the sidenav, mostly
|
||||||
|
|
||||||
// We switched from using query scopes here because of a Laravel bug
|
// We switched from using query scopes here because of a Laravel bug
|
||||||
|
@ -206,7 +206,7 @@ class AssetsController extends Controller
|
||||||
break;
|
break;
|
||||||
case 'Deployed':
|
case 'Deployed':
|
||||||
// more sad, horrible workarounds for laravel bugs when doing full text searches
|
// more sad, horrible workarounds for laravel bugs when doing full text searches
|
||||||
$assets->where('assets.assigned_to', '>', '0');
|
$assets->whereNotNull('assets.assigned_to');
|
||||||
break;
|
break;
|
||||||
case 'byod':
|
case 'byod':
|
||||||
// This is kind of redundant, since we already check for byod=1 above, but this keeps the
|
// This is kind of redundant, since we already check for byod=1 above, but this keeps the
|
||||||
|
@ -232,12 +232,6 @@ class AssetsController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
|
||||||
$assets->ByFilter($filter);
|
|
||||||
} elseif ($request->filled('search')) {
|
|
||||||
$assets->TextSearch($request->input('search'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leave these under the TextSearch scope, else the fuzziness will override the specific ID (status ID, etc) requested
|
// Leave these under the TextSearch scope, else the fuzziness will override the specific ID (status ID, etc) requested
|
||||||
if ($request->filled('status_id')) {
|
if ($request->filled('status_id')) {
|
||||||
$assets->where('assets.status_id', '=', $request->input('status_id'));
|
$assets->where('assets.status_id', '=', $request->input('status_id'));
|
||||||
|
@ -313,6 +307,7 @@ class AssetsController extends Controller
|
||||||
// in the allowed_columns array)
|
// in the allowed_columns array)
|
||||||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
||||||
|
|
||||||
|
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||||
|
|
||||||
switch ($sort_override) {
|
switch ($sort_override) {
|
||||||
case 'model':
|
case 'model':
|
||||||
|
@ -350,6 +345,10 @@ class AssetsController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||||
|
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : abs($request->input('offset'));
|
||||||
|
$limit = app('api_limit_value');
|
||||||
|
|
||||||
$total = $assets->count();
|
$total = $assets->count();
|
||||||
$assets = $assets->skip($offset)->take($limit)->get();
|
$assets = $assets->skip($offset)->take($limit)->get();
|
||||||
|
|
||||||
|
@ -480,7 +479,7 @@ class AssetsController extends Controller
|
||||||
public function selectlist(Request $request)
|
public function selectlist(Request $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$assets = Company::scopeCompanyables(Asset::select([
|
$assets = Asset::select([
|
||||||
'assets.id',
|
'assets.id',
|
||||||
'assets.name',
|
'assets.name',
|
||||||
'assets.asset_tag',
|
'assets.asset_tag',
|
||||||
|
@ -488,7 +487,7 @@ class AssetsController extends Controller
|
||||||
'assets.assigned_to',
|
'assets.assigned_to',
|
||||||
'assets.assigned_type',
|
'assets.assigned_type',
|
||||||
'assets.status_id',
|
'assets.status_id',
|
||||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived(), 'company_id', 'assets');
|
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
|
||||||
|
|
||||||
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||||
$assets = $assets->RTD();
|
$assets = $assets->RTD();
|
||||||
|
@ -1033,9 +1032,10 @@ class AssetsController extends Controller
|
||||||
{
|
{
|
||||||
$this->authorize('viewRequestable', Asset::class);
|
$this->authorize('viewRequestable', Asset::class);
|
||||||
|
|
||||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'), 'company_id', 'assets')
|
$assets = Asset::select('assets.*')
|
||||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier')->requestableAssets();
|
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier')
|
||||||
|
->requestableAssets();
|
||||||
|
|
||||||
$offset = request('offset', 0);
|
$offset = request('offset', 0);
|
||||||
$limit = $request->input('limit', 50);
|
$limit = $request->input('limit', 50);
|
||||||
|
|
|
@ -44,9 +44,8 @@ class ComponentsController extends Controller
|
||||||
'notes',
|
'notes',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$components = Component::select('components.*')
|
||||||
$components = Company::scopeCompanyables(Component::select('components.*')
|
->with('company', 'location', 'category', 'assets', 'supplier');
|
||||||
->with('company', 'location', 'category', 'assets', 'supplier'));
|
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$components = $components->TextSearch($request->input('search'));
|
$components = $components->TextSearch($request->input('search'));
|
||||||
|
|
|
@ -45,11 +45,8 @@ class ConsumablesController extends Controller
|
||||||
'notes',
|
'notes',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$consumables = Consumable::select('consumables.*')
|
||||||
$consumables = Company::scopeCompanyables(
|
->with('company', 'location', 'category', 'users', 'manufacturer');
|
||||||
Consumable::select('consumables.*')
|
|
||||||
->with('company', 'location', 'category', 'users', 'manufacturer')
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$consumables = $consumables->TextSearch(e($request->input('search')));
|
$consumables = $consumables->TextSearch(e($request->input('search')));
|
||||||
|
|
|
@ -26,8 +26,8 @@ class LicensesController extends Controller
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$this->authorize('view', License::class);
|
$this->authorize('view', License::class);
|
||||||
$licenses = Company::scopeCompanyables(License::with('company', 'manufacturer', 'supplier','category')->withCount('freeSeats as free_seats_count'));
|
|
||||||
|
|
||||||
|
$licenses = License::with('company', 'manufacturer', 'supplier','category')->withCount('freeSeats as free_seats_count');
|
||||||
|
|
||||||
if ($request->filled('company_id')) {
|
if ($request->filled('company_id')) {
|
||||||
$licenses->where('company_id', '=', $request->input('company_id'));
|
$licenses->where('company_id', '=', $request->input('company_id'));
|
||||||
|
|
|
@ -30,15 +30,17 @@ class LicenseCheckoutController extends Controller
|
||||||
// Check that the license is valid
|
// Check that the license is valid
|
||||||
if ($license = License::find($licenseId)) {
|
if ($license = License::find($licenseId)) {
|
||||||
|
|
||||||
|
$this->authorize('checkout', $license);
|
||||||
// If the license is valid, check that there is an available seat
|
// If the license is valid, check that there is an available seat
|
||||||
if ($license->avail_seats_count < 1) {
|
if ($license->avail_seats_count < 1) {
|
||||||
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
return redirect()->route('licenses.index')->with('error', 'There are no available seats for this license');
|
||||||
}
|
}
|
||||||
|
return view('licenses/checkout', compact('license'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->authorize('checkout', $license);
|
return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found'));
|
||||||
|
|
||||||
|
|
||||||
return view('licenses/checkout', compact('license'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1136,7 +1136,7 @@ class SettingsController extends Controller
|
||||||
public function postBackups()
|
public function postBackups()
|
||||||
{
|
{
|
||||||
if (! config('app.lock_passwords')) {
|
if (! config('app.lock_passwords')) {
|
||||||
Artisan::call('snipeit:backup', ['--filename' => 'manual-backup-'.date('Y-m-d-H:i:s')]);
|
Artisan::call('snipeit:backup', ['--filename' => 'manual-backup-'.date('Y-m-d-H-i-s')]);
|
||||||
$output = Artisan::output();
|
$output = Artisan::output();
|
||||||
|
|
||||||
// Backup completed
|
// Backup completed
|
||||||
|
|
|
@ -82,7 +82,7 @@ class ViewAssetsController extends Controller
|
||||||
return view('account/requestable-assets', compact('assets', 'models'));
|
return view('account/requestable-assets', compact('assets', 'models'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequestItem(Request $request, $itemType, $itemId = null)
|
public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null)
|
||||||
{
|
{
|
||||||
$item = null;
|
$item = null;
|
||||||
$fullItemType = 'App\\Models\\'.studly_case($itemType);
|
$fullItemType = 'App\\Models\\'.studly_case($itemType);
|
||||||
|
@ -119,16 +119,16 @@ class ViewAssetsController extends Controller
|
||||||
|
|
||||||
$settings = Setting::getSettings();
|
$settings = Setting::getSettings();
|
||||||
|
|
||||||
if ($item_request = $item->isRequestedBy($user)) {
|
if (($item_request = $item->isRequestedBy($user)) || $cancel_by_admin) {
|
||||||
$item->cancelRequest();
|
$item->cancelRequest($requestingUser);
|
||||||
$data['item_quantity'] = $item_request->qty;
|
$data['item_quantity'] = ($item_request) ? $item_request->qty : 1;
|
||||||
$logaction->logaction('request_canceled');
|
$logaction->logaction('request_canceled');
|
||||||
|
|
||||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
||||||
$settings->notify(new RequestAssetCancelation($data));
|
$settings->notify(new RequestAssetCancelation($data));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
return redirect()->back()->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
||||||
} else {
|
} else {
|
||||||
$item->request();
|
$item->request();
|
||||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
||||||
|
|
|
@ -214,7 +214,6 @@ class Importer extends Component
|
||||||
'model_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]),
|
'model_notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]),
|
||||||
'manufacturer' => trans('general.manufacturer'),
|
'manufacturer' => trans('general.manufacturer'),
|
||||||
'order_number' => trans('general.order_number'),
|
'order_number' => trans('general.order_number'),
|
||||||
'notes' => trans('general.notes'),
|
|
||||||
'image' => trans('general.importer.image_filename'),
|
'image' => trans('general.importer.image_filename'),
|
||||||
/**
|
/**
|
||||||
* Checkout fields:
|
* Checkout fields:
|
||||||
|
|
|
@ -32,7 +32,7 @@ class AccessoriesTransformer
|
||||||
'model_number' => ($accessory->model_number) ? e($accessory->model_number) : null,
|
'model_number' => ($accessory->model_number) ? e($accessory->model_number) : null,
|
||||||
'category' => ($accessory->category) ? ['id' => $accessory->category->id, 'name'=> e($accessory->category->name)] : null,
|
'category' => ($accessory->category) ? ['id' => $accessory->category->id, 'name'=> e($accessory->category->name)] : null,
|
||||||
'location' => ($accessory->location) ? ['id' => $accessory->location->id, 'name'=> e($accessory->location->name)] : null,
|
'location' => ($accessory->location) ? ['id' => $accessory->location->id, 'name'=> e($accessory->location->name)] : null,
|
||||||
'notes' => ($accessory->notes) ? e($accessory->notes) : null,
|
'notes' => ($accessory->notes) ? Helper::parseEscapedMarkedown($accessory->notes) : null,
|
||||||
'qty' => ($accessory->qty) ? (int) $accessory->qty : null,
|
'qty' => ($accessory->qty) ? (int) $accessory->qty : null,
|
||||||
'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null,
|
'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null,
|
||||||
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
|
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
|
||||||
|
|
|
@ -110,7 +110,7 @@ class ActionlogsTransformer
|
||||||
'type' => e($actionlog->targetType()),
|
'type' => e($actionlog->targetType()),
|
||||||
] : null,
|
] : null,
|
||||||
|
|
||||||
'note' => ($actionlog->note) ? e($actionlog->note): null,
|
'note' => ($actionlog->note) ? Helper::parseEscapedMarkedown($actionlog->note): null,
|
||||||
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
'signature_file' => ($actionlog->accept_signature) ? route('log.signature.view', ['filename' => $actionlog->accept_signature ]) : null,
|
||||||
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
'log_meta' => ((isset($clean_meta)) && (is_array($clean_meta))) ? $clean_meta: null,
|
||||||
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
'action_date' => ($actionlog->action_date) ? Helper::getFormattedDateObject($actionlog->action_date, 'datetime'): Helper::getFormattedDateObject($actionlog->created_at, 'datetime'),
|
||||||
|
|
|
@ -49,7 +49,7 @@ class AssetMaintenancesTransformer
|
||||||
'id' => (int) $assetmaintenance->asset->defaultLoc->id,
|
'id' => (int) $assetmaintenance->asset->defaultLoc->id,
|
||||||
'name'=> e($assetmaintenance->asset->defaultLoc->name),
|
'name'=> e($assetmaintenance->asset->defaultLoc->name),
|
||||||
] : null,
|
] : null,
|
||||||
'notes' => ($assetmaintenance->notes) ? e($assetmaintenance->notes) : null,
|
'notes' => ($assetmaintenance->notes) ? Helper::parseEscapedMarkedown($assetmaintenance->notes) : null,
|
||||||
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null,
|
'supplier' => ($assetmaintenance->supplier) ? ['id' => $assetmaintenance->supplier->id, 'name'=> e($assetmaintenance->supplier->name)] : null,
|
||||||
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
|
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
|
||||||
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
|
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
|
||||||
|
|
|
@ -63,7 +63,7 @@ class AssetModelsTransformer
|
||||||
'default_fieldset_values' => $default_field_values,
|
'default_fieldset_values' => $default_field_values,
|
||||||
'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None',
|
'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None',
|
||||||
'requestable' => ($assetmodel->requestable == '1') ? true : false,
|
'requestable' => ($assetmodel->requestable == '1') ? true : false,
|
||||||
'notes' => e($assetmodel->notes),
|
'notes' => Helper::parseEscapedMarkedown($assetmodel->notes),
|
||||||
'created_at' => Helper::getFormattedDateObject($assetmodel->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($assetmodel->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($assetmodel->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($assetmodel->updated_at, 'datetime'),
|
||||||
'deleted_at' => Helper::getFormattedDateObject($assetmodel->deleted_at, 'datetime'),
|
'deleted_at' => Helper::getFormattedDateObject($assetmodel->deleted_at, 'datetime'),
|
||||||
|
|
|
@ -58,7 +58,7 @@ class AssetsTransformer
|
||||||
'id' => (int) $asset->supplier->id,
|
'id' => (int) $asset->supplier->id,
|
||||||
'name'=> e($asset->supplier->name),
|
'name'=> e($asset->supplier->name),
|
||||||
] : null,
|
] : null,
|
||||||
'notes' => ($asset->notes) ? e($asset->notes) : null,
|
'notes' => ($asset->notes) ? Helper::parseEscapedMarkedown($asset->notes) : null,
|
||||||
'order_number' => ($asset->order_number) ? e($asset->order_number) : null,
|
'order_number' => ($asset->order_number) ? e($asset->order_number) : null,
|
||||||
'company' => ($asset->company) ? [
|
'company' => ($asset->company) ? [
|
||||||
'id' => (int) $asset->company->id,
|
'id' => (int) $asset->company->id,
|
||||||
|
|
|
@ -46,7 +46,7 @@ class ComponentsTransformer
|
||||||
'id' => (int) $component->company->id,
|
'id' => (int) $component->company->id,
|
||||||
'name' => e($component->company->name),
|
'name' => e($component->company->name),
|
||||||
] : null,
|
] : null,
|
||||||
'notes' => ($component->notes) ? e($component->notes) : null,
|
'notes' => ($component->notes) ? Helper::parseEscapedMarkedown($component->notes) : null,
|
||||||
'created_at' => Helper::getFormattedDateObject($component->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($component->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($component->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($component->updated_at, 'datetime'),
|
||||||
'user_can_checkout' => ($component->numRemaining() > 0) ? 1 : 0,
|
'user_can_checkout' => ($component->numRemaining() > 0) ? 1 : 0,
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ConsumablesTransformer
|
||||||
'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost),
|
'purchase_cost' => Helper::formatCurrencyOutput($consumable->purchase_cost),
|
||||||
'purchase_date' => Helper::getFormattedDateObject($consumable->purchase_date, 'date'),
|
'purchase_date' => Helper::getFormattedDateObject($consumable->purchase_date, 'date'),
|
||||||
'qty' => (int) $consumable->qty,
|
'qty' => (int) $consumable->qty,
|
||||||
'notes' => ($consumable->notes) ? e($consumable->notes) : null,
|
'notes' => ($consumable->notes) ? Helper::parseEscapedMarkedown($consumable->notes) : null,
|
||||||
'created_at' => Helper::getFormattedDateObject($consumable->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($consumable->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($consumable->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($consumable->updated_at, 'datetime'),
|
||||||
];
|
];
|
||||||
|
|
|
@ -34,7 +34,7 @@ class LicensesTransformer
|
||||||
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
|
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
|
||||||
'purchase_cost' => Helper::formatCurrencyOutput($license->purchase_cost),
|
'purchase_cost' => Helper::formatCurrencyOutput($license->purchase_cost),
|
||||||
'purchase_cost_numeric' => $license->purchase_cost,
|
'purchase_cost_numeric' => $license->purchase_cost,
|
||||||
'notes' => e($license->notes),
|
'notes' => Helper::parseEscapedMarkedown($license->notes),
|
||||||
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
||||||
'seats' => (int) $license->seats,
|
'seats' => (int) $license->seats,
|
||||||
'free_seats_count' => (int) $license->free_seats_count,
|
'free_seats_count' => (int) $license->free_seats_count,
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SuppliersTransformer
|
||||||
'licenses_count' => (int) $supplier->licenses_count,
|
'licenses_count' => (int) $supplier->licenses_count,
|
||||||
'consumables_count' => (int) $supplier->consumables_count,
|
'consumables_count' => (int) $supplier->consumables_count,
|
||||||
'components_count' => (int) $supplier->components_count,
|
'components_count' => (int) $supplier->components_count,
|
||||||
'notes' => ($supplier->notes) ? e($supplier->notes) : null,
|
'notes' => ($supplier->notes) ? Helper::parseEscapedMarkedown($supplier->notes) : null,
|
||||||
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($supplier->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($supplier->updated_at, 'datetime'),
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ class UsersTransformer
|
||||||
'id' => (int) $user->userloc->id,
|
'id' => (int) $user->userloc->id,
|
||||||
'name'=> e($user->userloc->name),
|
'name'=> e($user->userloc->name),
|
||||||
] : null,
|
] : null,
|
||||||
'notes'=> e($user->notes),
|
'notes'=> Helper::parseEscapedMarkedown($user->notes),
|
||||||
'permissions' => $user->decodePermissions(),
|
'permissions' => $user->decodePermissions(),
|
||||||
'activated' => ($user->activated == '1') ? true : false,
|
'activated' => ($user->activated == '1') ? true : false,
|
||||||
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,
|
'autoassign_licenses' => ($user->autoassign_licenses == '1') ? true : false,
|
||||||
|
|
|
@ -27,15 +27,24 @@ class LicenseImporter extends ItemImporter
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
* @param array $row
|
* @param array $row
|
||||||
* @return License|mixed|null
|
* @return License|mixed|null
|
||||||
|
* updated @author Jes Vinsmoke
|
||||||
|
* @since 6.1
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public function createLicenseIfNotExists(array $row)
|
public function createLicenseIfNotExists(array $row)
|
||||||
{
|
{
|
||||||
$editingLicense = false;
|
$editingLicense = false;
|
||||||
$license = License::where('name', $this->item['name'])
|
$license = License::where('serial', $this->item['serial'])->where('name', $this->item['name'])
|
||||||
->first();
|
->first();
|
||||||
if ($license) {
|
if ($license) {
|
||||||
if (! $this->updating) {
|
if (! $this->updating) {
|
||||||
|
|
||||||
|
if($this->item['serial'] != "") {
|
||||||
$this->log('A matching License ' . $this->item['name'] . ' with serial ' . $this->item['serial'] . ' already exists');
|
$this->log('A matching License ' . $this->item['name'] . ' with serial ' . $this->item['serial'] . ' already exists');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->log('A matching License ' . $this->item['name'] . ' with no serial number already exists');
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +66,10 @@ class LicenseImporter extends ItemImporter
|
||||||
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
|
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
|
||||||
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
|
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
|
||||||
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
|
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
|
||||||
|
if($this->item['reassignable'] == "")
|
||||||
|
{
|
||||||
|
$this->item['reassignable'] = 1;
|
||||||
|
}
|
||||||
$this->item['seats'] = $this->findCsvMatch($row, 'seats');
|
$this->item['seats'] = $this->findCsvMatch($row, 'seats');
|
||||||
|
|
||||||
$this->item["termination_date"] = null;
|
$this->item["termination_date"] = null;
|
||||||
|
|
|
@ -1566,7 +1566,7 @@ class Asset extends Depreciable
|
||||||
*/
|
*/
|
||||||
public function scopeOrderModelNumber($query, $order)
|
public function scopeOrderModelNumber($query, $order)
|
||||||
{
|
{
|
||||||
return $query->leftJoin('models as model_number_sort', 'assets.model_id', '=', 'models.id')->orderBy('models.model_number', $order);
|
return $query->leftJoin('models as model_number_sort', 'assets.model_id', '=', 'model_number_sort.id')->orderBy('model_number_sort.model_number', $order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ class Depreciable extends SnipeModel
|
||||||
$yearsPast = 0;
|
$yearsPast = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return round($yearsPast / $deprecationYears * $this->purchase_cost, 2);
|
return $this->purchase_cost - round($yearsPast / $deprecationYears * $this->purchase_cost, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,8 +38,12 @@ trait Requestable
|
||||||
$this->requests()->where('user_id', Auth::id())->delete();
|
$this->requests()->where('user_id', Auth::id())->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancelRequest()
|
public function cancelRequest($user_id = null)
|
||||||
{
|
{
|
||||||
$this->requests()->where('user_id', Auth::id())->update(['canceled_at' => \Carbon\Carbon::now()]);
|
if (!$user_id){
|
||||||
|
$user_id = Auth::id();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->requests()->where('user_id', $user_id)->update(['canceled_at' => \Carbon\Carbon::now()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
"watson/validating": "^7.0"
|
"watson/validating": "^7.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"brianium/paratest": "^v6.4.4",
|
||||||
"fakerphp/faker": "^1.16",
|
"fakerphp/faker": "^1.16",
|
||||||
"laravel/dusk": "^6.25",
|
"laravel/dusk": "^6.25",
|
||||||
"mockery/mockery": "^1.4",
|
"mockery/mockery": "^1.4",
|
||||||
|
|
597
composer.lock
generated
597
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
return array (
|
return array (
|
||||||
'app_version' => 'v6.1.1',
|
'app_version' => 'v6.1.2',
|
||||||
'full_app_version' => 'v6.1.1 - build 10847-g2ac4449ea',
|
'full_app_version' => 'v6.1.2 - build 10938-g32747cafd',
|
||||||
'build_version' => '10847',
|
'build_version' => '10938',
|
||||||
'prerelease_version' => '',
|
'prerelease_version' => '',
|
||||||
'hash_version' => 'g2ac4449ea',
|
'hash_version' => 'g32747cafd',
|
||||||
'full_hash' => 'v6.1.1-605-g2ac4449ea',
|
'full_hash' => 'v6.1.2-89-g32747cafd',
|
||||||
'branch' => 'develop',
|
'branch' => 'develop',
|
||||||
);
|
);
|
|
@ -328,4 +328,14 @@ class AssetFactory extends Factory
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requestable()
|
||||||
|
{
|
||||||
|
return $this->state(['requestable' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nonrequestable()
|
||||||
|
{
|
||||||
|
return $this->state(['requestable' => false]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,6 +271,15 @@ class UserFactory extends Factory
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function viewDepartments()
|
||||||
|
{
|
||||||
|
return $this->state(function () {
|
||||||
|
return [
|
||||||
|
'permissions' => '{"departments.view":"1"}',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function viewLicenses()
|
public function viewLicenses()
|
||||||
{
|
{
|
||||||
return $this->state(function () {
|
return $this->state(function () {
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class ChangeSettingsTableIncreaseSamlIdpMetadataSize extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* This migration changes the format of the saml_idp_metadata field to MEDIUMTEXT
|
||||||
|
* to avoid truncating
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('settings', function (Blueprint $table) {
|
||||||
|
$table->mediumText('saml_idp_metadata')->nullable()->default(null)->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('settings', function (Blueprint $table) {
|
||||||
|
$table->text('saml_idp_metadata')->nullable()->default(null)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
BIN
public/css/dist/skins/skin-yellow-dark.css
vendored
BIN
public/css/dist/skins/skin-yellow-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-yellow-dark.min.css
vendored
BIN
public/css/dist/skins/skin-yellow-dark.min.css
vendored
Binary file not shown.
|
@ -7,7 +7,7 @@
|
||||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
|
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
|
||||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
|
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=d409d9b1a3b69247df8b98941ba06e33",
|
||||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
||||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=3cb840e047cd0c40484a08c7a8e7cdea",
|
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=21fef066e0bb1b02fd83fcb6694fad5f",
|
||||||
"/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-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=9f944e8021781af1ce45d27765d1c0c2",
|
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=9f944e8021781af1ce45d27765d1c0c2",
|
||||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
|
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=4a9e8c5e7b09506fa3e3a3f42849e07f",
|
||||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
|
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=3cb840e047cd0c40484a08c7a8e7cdea",
|
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=21fef066e0bb1b02fd83fcb6694fad5f",
|
||||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
|
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=7f0eb9e355b36b41c61c3af3b4d41143",
|
||||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||||
|
|
|
@ -78,7 +78,9 @@
|
||||||
a.actions {
|
a.actions {
|
||||||
color:#fff !important;
|
color:#fff !important;
|
||||||
}
|
}
|
||||||
|
a:visited.label-default, a:link.label-default{
|
||||||
|
color:#444;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
The dropdown is white, so use a darker color
|
The dropdown is white, so use a darker color
|
||||||
*/
|
*/
|
||||||
|
@ -159,9 +161,6 @@ h2.task_menu{
|
||||||
background: linear-gradient(to bottom, var(--header) 0%,var(--header) 100%);
|
background: linear-gradient(to bottom, var(--header) 0%,var(--header) 100%);
|
||||||
border-color: var(--header);
|
border-color: var(--header);
|
||||||
}
|
}
|
||||||
.label-default{
|
|
||||||
background-color:var(--back-sub);
|
|
||||||
}
|
|
||||||
a.btn.btn-default{
|
a.btn.btn-default{
|
||||||
color:var(--nav-link);
|
color:var(--nav-link);
|
||||||
}
|
}
|
||||||
|
|
|
@ -436,6 +436,7 @@ return [
|
||||||
'errors_importing' => 'Some Errors occurred while importing: ',
|
'errors_importing' => 'Some Errors occurred while importing: ',
|
||||||
'warning' => 'WARNING: :warning',
|
'warning' => 'WARNING: :warning',
|
||||||
'success_redirecting' => '"Success... Redirecting.',
|
'success_redirecting' => '"Success... Redirecting.',
|
||||||
|
'cancel_request' => 'Cancel this item request',
|
||||||
'setup_successful_migrations' => 'Your database tables have been created',
|
'setup_successful_migrations' => 'Your database tables have been created',
|
||||||
'setup_migration_output' => 'Migration output:',
|
'setup_migration_output' => 'Migration output:',
|
||||||
'setup_migration_create_user' => 'Next: Create User',
|
'setup_migration_create_user' => 'Next: Create User',
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
@if ($snipeSettings->default_eula_text!='')
|
@if ($snipeSettings->default_eula_text!='')
|
||||||
<label class="form-control">
|
<label class="form-control">
|
||||||
{{ Form::checkbox('use_default_eula', '1', old('use_default_eula', $item->use_default_eula), ['aria-label'=>'use_default_eula']) }}
|
{{ Form::checkbox('use_default_eula', '1', old('use_default_eula', $item->use_default_eula), ['aria-label'=>'use_default_eula']) }}
|
||||||
{!! trans('admin/categories/general.use_default_eula') !!}
|
<span>{!! trans('admin/categories/general.use_default_eula') !!}</span>
|
||||||
</label>
|
</label>
|
||||||
@else
|
@else
|
||||||
<label class="form-control form-control--disabled">
|
<label class="form-control form-control--disabled">
|
||||||
|
|
|
@ -52,7 +52,7 @@ $qr_size = ($settings->alt_barcode_enabled=='1') && ($settings->alt_barcode!='')
|
||||||
}
|
}
|
||||||
img.barcode {
|
img.barcode {
|
||||||
display:block;
|
display:block;
|
||||||
margin-top:-7px;
|
margin-top:-14px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
div.label-logo {
|
div.label-logo {
|
||||||
|
|
|
@ -17,11 +17,6 @@
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
{{ Form::open([
|
|
||||||
'method' => 'POST',
|
|
||||||
'route' => ['hardware/bulkedit'],
|
|
||||||
'class' => 'form-inline',
|
|
||||||
'id' => 'bulkForm']) }}
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
|
||||||
|
@ -51,7 +46,7 @@
|
||||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/form.expected_checkin') }}</th>
|
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/form.expected_checkin') }}</th>
|
||||||
<th class="col-md-3" data-sortable="true">{{ trans('admin/hardware/table.requesting_user') }}</th>
|
<th class="col-md-3" data-sortable="true">{{ trans('admin/hardware/table.requesting_user') }}</th>
|
||||||
<th class="col-md-2">{{ trans('admin/hardware/table.requested_date') }}</th>
|
<th class="col-md-2">{{ trans('admin/hardware/table.requested_date') }}</th>
|
||||||
<th class="col-md-1">{{ trans('general.checkin').'/'.trans('general.checkout') }}</th>
|
<th class="col-md-1">{{ trans('button.actions') }}</th> <th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -103,6 +98,14 @@
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>{{ App\Helpers\Helper::getFormattedDateObject($request->created_at, 'datetime', false) }}</td>
|
<td>{{ App\Helpers\Helper::getFormattedDateObject($request->created_at, 'datetime', false) }}</td>
|
||||||
|
<td>
|
||||||
|
{{ Form::open([
|
||||||
|
'method' => 'POST',
|
||||||
|
'route' => ['account/request-item', $request->itemType(), $request->requestable->id, true, $request->requestingUser()->id],
|
||||||
|
]) }}
|
||||||
|
<button class="btn btn-warning btn-sm" data-tooltip="true" title="{{ trans('general.cancel_request') }}">{{ trans('button.cancel') }}</button>
|
||||||
|
{{ Form::close() }}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if ($request->itemType() == "asset")
|
@if ($request->itemType() == "asset")
|
||||||
@if ($request->requestable->assigned_to=='')
|
@if ($request->requestable->assigned_to=='')
|
||||||
|
|
|
@ -934,6 +934,14 @@
|
||||||
{{ $asset->location->state }} {{ $asset->location->zip }}
|
{{ $asset->location->state }} {{ $asset->location->zip }}
|
||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
<li>
|
||||||
|
<i class="fas fa-calendar"></i> {{ trans('admin/hardware/form.checkout_date') }}: {{ Helper::getFormattedDateObject($asset->last_checkout, 'date', false) }}
|
||||||
|
</li>
|
||||||
|
@if (isset($asset->expected_checkin))
|
||||||
|
<li>
|
||||||
|
<i class="fas fa-calendar"></i> {{ trans('admin/hardware/form.expected_checkin') }}: {{ Helper::getFormattedDateObject($asset->expected_checkin, 'date', false) }}
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
|
|
|
@ -281,7 +281,7 @@ Route::group(['prefix' => 'account', 'middleware' => ['auth']], function () {
|
||||||
)->name('account/request-asset');
|
)->name('account/request-asset');
|
||||||
|
|
||||||
Route::post(
|
Route::post(
|
||||||
'request/{itemType}/{itemId}',
|
'request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}',
|
||||||
[ViewAssetsController::class, 'getRequestItem']
|
[ViewAssetsController::class, 'getRequestItem']
|
||||||
)->name('account/request-item');
|
)->name('account/request-item');
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
namespace Tests\Feature\Api\Assets;
|
namespace Tests\Feature\Api\Assets;
|
||||||
|
|
||||||
use App\Models\Asset;
|
use App\Models\Asset;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Testing\Fluent\AssertableJson;
|
use Illuminate\Testing\Fluent\AssertableJson;
|
||||||
use Laravel\Passport\Passport;
|
|
||||||
use Tests\Support\InteractsWithSettings;
|
use Tests\Support\InteractsWithSettings;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ class AssetIndexTest extends TestCase
|
||||||
{
|
{
|
||||||
Asset::factory()->count(3)->create();
|
Asset::factory()->count(3)->create();
|
||||||
|
|
||||||
Passport::actingAs(User::factory()->superuser()->create());
|
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||||
$this->getJson(
|
->getJson(
|
||||||
route('api.assets.index', [
|
route('api.assets.index', [
|
||||||
'sort' => 'name',
|
'sort' => 'name',
|
||||||
'order' => 'asc',
|
'order' => 'asc',
|
||||||
|
@ -32,4 +32,50 @@ class AssetIndexTest extends TestCase
|
||||||
])
|
])
|
||||||
->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc());
|
->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAssetIndexAdheresToCompanyScoping()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$assetA = Asset::factory()->for($companyA)->create();
|
||||||
|
$assetB = Asset::factory()->for($companyB)->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->viewAssets()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->viewAssets()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.assets.index'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.assets.index'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.assets.index'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.assets.index'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.assets.index'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseDoesNotContainInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.assets.index'))
|
||||||
|
->assertResponseDoesNotContainInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
76
tests/Feature/Api/Assets/AssetsForSelectListTest.php
Normal file
76
tests/Feature/Api/Assets/AssetsForSelectListTest.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Api\Assets;
|
||||||
|
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AssetsForSelectListTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function testAssetsCanBeSearchedForByAssetTag()
|
||||||
|
{
|
||||||
|
Asset::factory()->create(['asset_tag' => '0001']);
|
||||||
|
Asset::factory()->create(['asset_tag' => '0002']);
|
||||||
|
|
||||||
|
$response = $this->actingAsForApi(User::factory()->create())
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertOk();
|
||||||
|
|
||||||
|
$results = collect($response->json('results'));
|
||||||
|
|
||||||
|
$this->assertEquals(2, $results->count());
|
||||||
|
$this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, '0001')));
|
||||||
|
$this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, '0002')));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAssetsAreScopedToCompanyWhenMultipleCompanySupportEnabled()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$assetA = Asset::factory()->for($companyA)->create(['asset_tag' => '0001']);
|
||||||
|
$assetB = Asset::factory()->for($companyB)->create(['asset_tag' => '0002']);
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->viewAssets()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->viewAssets()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertResponseContainsInResults($assetA)
|
||||||
|
->assertResponseContainsInResults($assetB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertResponseContainsInResults($assetA)
|
||||||
|
->assertResponseContainsInResults($assetB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertResponseContainsInResults($assetA)
|
||||||
|
->assertResponseContainsInResults($assetB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertResponseContainsInResults($assetA)
|
||||||
|
->assertResponseContainsInResults($assetB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertResponseContainsInResults($assetA)
|
||||||
|
->assertResponseDoesNotContainInResults($assetB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('assets.selectlist', ['search' => '000']))
|
||||||
|
->assertResponseDoesNotContainInResults($assetA)
|
||||||
|
->assertResponseContainsInResults($assetB);
|
||||||
|
}
|
||||||
|
}
|
79
tests/Feature/Api/Assets/RequestableAssetsTest.php
Normal file
79
tests/Feature/Api/Assets/RequestableAssetsTest.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Api\Assets;
|
||||||
|
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class RequestableAssetsTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function testViewingRequestableAssetsRequiresCorrectPermission()
|
||||||
|
{
|
||||||
|
$this->actingAsForApi(User::factory()->create())
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsRequestableAssets()
|
||||||
|
{
|
||||||
|
$requestableAsset = Asset::factory()->requestable()->create(['asset_tag' => 'requestable']);
|
||||||
|
$nonRequestableAsset = Asset::factory()->nonrequestable()->create(['asset_tag' => 'non-requestable']);
|
||||||
|
|
||||||
|
$this->actingAsForApi(User::factory()->viewRequestableAssets()->create())
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertOk()
|
||||||
|
->assertResponseContainsInRows($requestableAsset, 'asset_tag')
|
||||||
|
->assertResponseDoesNotContainInRows($nonRequestableAsset, 'asset_tag');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRequestableAssetsAreScopedToCompanyWhenMultipleCompanySupportEnabled()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$assetA = Asset::factory()->requestable()->for($companyA)->create(['asset_tag' => '0001']);
|
||||||
|
$assetB = Asset::factory()->requestable()->for($companyB)->create(['asset_tag' => '0002']);
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->viewRequestableAssets()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->viewRequestableAssets()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertResponseContainsInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseDoesNotContainInRows($assetB, 'asset_tag');
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.assets.requestable'))
|
||||||
|
->assertResponseDoesNotContainInRows($assetA, 'asset_tag')
|
||||||
|
->assertResponseContainsInRows($assetB, 'asset_tag');
|
||||||
|
}
|
||||||
|
}
|
60
tests/Feature/Api/Components/ComponentIndexTest.php
Normal file
60
tests/Feature/Api/Components/ComponentIndexTest.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Api\Components;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Component;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ComponentIndexTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function testComponentIndexAdheresToCompanyScoping()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$componentA = Component::factory()->for($companyA)->create();
|
||||||
|
$componentB = Component::factory()->for($companyB)->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->viewComponents()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->viewComponents()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.components.index'))
|
||||||
|
->assertResponseContainsInRows($componentA)
|
||||||
|
->assertResponseContainsInRows($componentB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.components.index'))
|
||||||
|
->assertResponseContainsInRows($componentA)
|
||||||
|
->assertResponseContainsInRows($componentB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.components.index'))
|
||||||
|
->assertResponseContainsInRows($componentA)
|
||||||
|
->assertResponseContainsInRows($componentB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.components.index'))
|
||||||
|
->assertResponseContainsInRows($componentA)
|
||||||
|
->assertResponseContainsInRows($componentB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.components.index'))
|
||||||
|
->assertResponseContainsInRows($componentA)
|
||||||
|
->assertResponseDoesNotContainInRows($componentB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.components.index'))
|
||||||
|
->assertResponseDoesNotContainInRows($componentA)
|
||||||
|
->assertResponseContainsInRows($componentB);
|
||||||
|
}
|
||||||
|
}
|
60
tests/Feature/Api/Consumables/ConsumablesIndexTest.php
Normal file
60
tests/Feature/Api/Consumables/ConsumablesIndexTest.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Api\Consumables;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Consumable;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ConsumablesIndexTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function testConsumableIndexAdheresToCompanyScoping()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$consumableA = Consumable::factory()->for($companyA)->create();
|
||||||
|
$consumableB = Consumable::factory()->for($companyB)->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->viewConsumables()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->viewConsumables()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.consumables.index'))
|
||||||
|
->assertResponseContainsInRows($consumableA)
|
||||||
|
->assertResponseContainsInRows($consumableB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.consumables.index'))
|
||||||
|
->assertResponseContainsInRows($consumableA)
|
||||||
|
->assertResponseContainsInRows($consumableB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.consumables.index'))
|
||||||
|
->assertResponseContainsInRows($consumableA)
|
||||||
|
->assertResponseContainsInRows($consumableB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.consumables.index'))
|
||||||
|
->assertResponseContainsInRows($consumableA)
|
||||||
|
->assertResponseContainsInRows($consumableB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.consumables.index'))
|
||||||
|
->assertResponseContainsInRows($consumableA)
|
||||||
|
->assertResponseDoesNotContainInRows($consumableB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.consumables.index'))
|
||||||
|
->assertResponseDoesNotContainInRows($consumableA)
|
||||||
|
->assertResponseContainsInRows($consumableB);
|
||||||
|
}
|
||||||
|
}
|
60
tests/Feature/Api/Licenses/LicensesIndexTest.php
Normal file
60
tests/Feature/Api/Licenses/LicensesIndexTest.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Api\Licenses;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\License;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class LicensesIndexTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function testLicensesIndexAdheresToCompanyScoping()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$licenseA = License::factory()->for($companyA)->create();
|
||||||
|
$licenseB = License::factory()->for($companyB)->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->viewLicenses()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->viewLicenses()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.licenses.index'))
|
||||||
|
->assertResponseContainsInRows($licenseA)
|
||||||
|
->assertResponseContainsInRows($licenseB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.licenses.index'))
|
||||||
|
->assertResponseContainsInRows($licenseA)
|
||||||
|
->assertResponseContainsInRows($licenseB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.licenses.index'))
|
||||||
|
->assertResponseContainsInRows($licenseA)
|
||||||
|
->assertResponseContainsInRows($licenseB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAsForApi($superUser)
|
||||||
|
->getJson(route('api.licenses.index'))
|
||||||
|
->assertResponseContainsInRows($licenseA)
|
||||||
|
->assertResponseContainsInRows($licenseB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.licenses.index'))
|
||||||
|
->assertResponseContainsInRows($licenseA)
|
||||||
|
->assertResponseDoesNotContainInRows($licenseB);
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(route('api.licenses.index'))
|
||||||
|
->assertResponseDoesNotContainInRows($licenseA)
|
||||||
|
->assertResponseContainsInRows($licenseB);
|
||||||
|
}
|
||||||
|
}
|
19
tests/Feature/DashboardTest.php
Normal file
19
tests/Feature/DashboardTest.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class DashboardTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function testUsersWithoutAdminAccessAreRedirected()
|
||||||
|
{
|
||||||
|
$this->actingAs(User::factory()->create())
|
||||||
|
->get(route('home'))
|
||||||
|
->assertRedirect(route('view-assets'));
|
||||||
|
}
|
||||||
|
}
|
66
tests/Support/CustomTestMacros.php
Normal file
66
tests/Support/CustomTestMacros.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Support;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Testing\TestResponse;
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
trait CustomTestMacros
|
||||||
|
{
|
||||||
|
protected function registerCustomMacros()
|
||||||
|
{
|
||||||
|
$guardAgainstNullProperty = function (Model $model, string $property) {
|
||||||
|
if (is_null($model->{$property})) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"The property ({$property}) either does not exist or is null on the model which isn't helpful for comparison."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestResponse::macro(
|
||||||
|
'assertResponseContainsInRows',
|
||||||
|
function (Model $model, string $property = 'name') use ($guardAgainstNullProperty) {
|
||||||
|
$guardAgainstNullProperty($model, $property);
|
||||||
|
|
||||||
|
Assert::assertTrue(collect($this['rows'])->pluck($property)->contains($model->{$property}));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
TestResponse::macro(
|
||||||
|
'assertResponseDoesNotContainInRows',
|
||||||
|
function (Model $model, string $property = 'name') use ($guardAgainstNullProperty) {
|
||||||
|
$guardAgainstNullProperty($model, $property);
|
||||||
|
|
||||||
|
Assert::assertFalse(collect($this['rows'])->pluck($property)->contains($model->{$property}));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
TestResponse::macro(
|
||||||
|
'assertResponseContainsInResults',
|
||||||
|
function (Model $model, string $property = 'id') use ($guardAgainstNullProperty) {
|
||||||
|
$guardAgainstNullProperty($model, $property);
|
||||||
|
|
||||||
|
Assert::assertTrue(collect($this->json('results'))->pluck('id')->contains($model->{$property}));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
TestResponse::macro(
|
||||||
|
'assertResponseDoesNotContainInResults',
|
||||||
|
function (Model $model, string $property = 'id') use ($guardAgainstNullProperty) {
|
||||||
|
$guardAgainstNullProperty($model, $property);
|
||||||
|
|
||||||
|
Assert::assertFalse(collect($this->json('results'))->pluck('id')->contains($model->{$property}));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
16
tests/Support/InteractsWithAuthentication.php
Normal file
16
tests/Support/InteractsWithAuthentication.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Support;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Auth\Authenticatable;
|
||||||
|
use Laravel\Passport\Passport;
|
||||||
|
|
||||||
|
trait InteractsWithAuthentication
|
||||||
|
{
|
||||||
|
protected function actingAsForApi(Authenticatable $user)
|
||||||
|
{
|
||||||
|
Passport::actingAs($user);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,11 @@ class Settings
|
||||||
return $this->update(['full_multiple_companies_support' => 1]);
|
return $this->update(['full_multiple_companies_support' => 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function disableMultipleFullCompanySupport(): Settings
|
||||||
|
{
|
||||||
|
return $this->update(['full_multiple_companies_support' => 0]);
|
||||||
|
}
|
||||||
|
|
||||||
public function enableWebhook(): Settings
|
public function enableWebhook(): Settings
|
||||||
{
|
{
|
||||||
return $this->update([
|
return $this->update([
|
||||||
|
|
|
@ -5,11 +5,15 @@ namespace Tests;
|
||||||
use App\Http\Middleware\SecurityHeaders;
|
use App\Http\Middleware\SecurityHeaders;
|
||||||
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
use Tests\Support\CustomTestMacros;
|
||||||
|
use Tests\Support\InteractsWithAuthentication;
|
||||||
use Tests\Support\InteractsWithSettings;
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
abstract class TestCase extends BaseTestCase
|
||||||
{
|
{
|
||||||
use CreatesApplication;
|
use CreatesApplication;
|
||||||
|
use CustomTestMacros;
|
||||||
|
use InteractsWithAuthentication;
|
||||||
use LazilyRefreshDatabase;
|
use LazilyRefreshDatabase;
|
||||||
|
|
||||||
private array $globallyDisabledMiddleware = [
|
private array $globallyDisabledMiddleware = [
|
||||||
|
@ -25,5 +29,7 @@ abstract class TestCase extends BaseTestCase
|
||||||
if (collect(class_uses_recursive($this))->contains(InteractsWithSettings::class)) {
|
if (collect(class_uses_recursive($this))->contains(InteractsWithSettings::class)) {
|
||||||
$this->initializeSettings();
|
$this->initializeSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->registerCustomMacros();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
169
tests/Unit/CompanyScopingTest.php
Normal file
169
tests/Unit/CompanyScopingTest.php
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use App\Models\Accessory;
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\AssetMaintenance;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Component;
|
||||||
|
use App\Models\Consumable;
|
||||||
|
use App\Models\License;
|
||||||
|
use App\Models\LicenseSeat;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Tests\Support\InteractsWithSettings;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class CompanyScopingTest extends TestCase
|
||||||
|
{
|
||||||
|
use InteractsWithSettings;
|
||||||
|
|
||||||
|
public function models(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Accessories' => [Accessory::class],
|
||||||
|
'Assets' => [Asset::class],
|
||||||
|
'Components' => [Component::class],
|
||||||
|
'Consumables' => [Consumable::class],
|
||||||
|
'Licenses' => [License::class],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider models */
|
||||||
|
public function testCompanyScoping($model)
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$modelA = $model::factory()->for($companyA)->create();
|
||||||
|
$modelB = $model::factory()->for($companyB)->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAs($superUser);
|
||||||
|
$this->assertCanSee($modelA);
|
||||||
|
$this->assertCanSee($modelB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyA);
|
||||||
|
$this->assertCanSee($modelA);
|
||||||
|
$this->assertCanSee($modelB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyB);
|
||||||
|
$this->assertCanSee($modelA);
|
||||||
|
$this->assertCanSee($modelB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAs($superUser);
|
||||||
|
$this->assertCanSee($modelA);
|
||||||
|
$this->assertCanSee($modelB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyA);
|
||||||
|
$this->assertCanSee($modelA);
|
||||||
|
$this->assertCannotSee($modelB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyB);
|
||||||
|
$this->assertCannotSee($modelA);
|
||||||
|
$this->assertCanSee($modelB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAssetMaintenanceCompanyScoping()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$assetMaintenanceForCompanyA = AssetMaintenance::factory()->for(Asset::factory()->for($companyA))->create();
|
||||||
|
$assetMaintenanceForCompanyB = AssetMaintenance::factory()->for(Asset::factory()->for($companyB))->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAs($superUser);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyB);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAs($superUser);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyA);
|
||||||
|
$this->assertCannotSee($assetMaintenanceForCompanyB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyB);
|
||||||
|
$this->assertCannotSee($assetMaintenanceForCompanyA);
|
||||||
|
$this->assertCanSee($assetMaintenanceForCompanyB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLicenseSeatCompanyScoping()
|
||||||
|
{
|
||||||
|
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$licenseSeatA = LicenseSeat::factory()->for(Asset::factory()->for($companyA))->create();
|
||||||
|
$licenseSeatB = LicenseSeat::factory()->for(Asset::factory()->for($companyB))->create();
|
||||||
|
|
||||||
|
$superUser = $companyA->users()->save(User::factory()->superuser()->make());
|
||||||
|
$userInCompanyA = $companyA->users()->save(User::factory()->make());
|
||||||
|
$userInCompanyB = $companyB->users()->save(User::factory()->make());
|
||||||
|
|
||||||
|
$this->settings->disableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAs($superUser);
|
||||||
|
$this->assertCanSee($licenseSeatA);
|
||||||
|
$this->assertCanSee($licenseSeatB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyA);
|
||||||
|
$this->assertCanSee($licenseSeatA);
|
||||||
|
$this->assertCanSee($licenseSeatB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyB);
|
||||||
|
$this->assertCanSee($licenseSeatA);
|
||||||
|
$this->assertCanSee($licenseSeatB);
|
||||||
|
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$this->actingAs($superUser);
|
||||||
|
$this->assertCanSee($licenseSeatA);
|
||||||
|
$this->assertCanSee($licenseSeatB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyA);
|
||||||
|
$this->assertCanSee($licenseSeatA);
|
||||||
|
$this->assertCannotSee($licenseSeatB);
|
||||||
|
|
||||||
|
$this->actingAs($userInCompanyB);
|
||||||
|
$this->assertCannotSee($licenseSeatA);
|
||||||
|
$this->assertCanSee($licenseSeatB);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertCanSee(Model $model)
|
||||||
|
{
|
||||||
|
$this->assertTrue(
|
||||||
|
get_class($model)::all()->contains($model),
|
||||||
|
'User was not able to see expected model'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertCannotSee(Model $model)
|
||||||
|
{
|
||||||
|
$this->assertFalse(
|
||||||
|
get_class($model)::all()->contains($model),
|
||||||
|
'User was able to see model from a different company'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
29
upgrade.php
29
upgrade.php
|
@ -1,7 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('Access denied.');
|
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('Access denied.');
|
||||||
|
|
||||||
$required_php_min = '7.4.0';
|
$php_min_works = '7.4.0';
|
||||||
|
$php_max_wontwork = '8.2.0';
|
||||||
|
|
||||||
|
|
||||||
if ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') || (!function_exists('posix_getpwuid'))) {
|
if ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') || (!function_exists('posix_getpwuid'))) {
|
||||||
echo "Skipping user check as it is not supported on Windows or Posix is not installed on this server. \n";
|
echo "Skipping user check as it is not supported on Windows or Posix is not installed on this server. \n";
|
||||||
|
@ -123,12 +125,12 @@ echo $env_good;
|
||||||
|
|
||||||
if ($env_bad !='') {
|
if ($env_bad !='') {
|
||||||
|
|
||||||
echo "\n--------------------- !! ERROR !! ----------------------\n";
|
echo "!!!!!!!!!!!!!!!!!!!!!!!!!! .ENV FILE ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!\n";
|
||||||
echo "Your .env file is misconfigured. Upgrade cannot continue.\n";
|
echo "Your .env file is misconfigured. Upgrade cannot continue.\n";
|
||||||
echo "--------------------------------------------------------\n\n";
|
echo "--------------------------------------------------------\n\n";
|
||||||
echo $env_bad;
|
echo $env_bad;
|
||||||
echo "\n\n--------------------------------------------------------\n";
|
echo "\n\n--------------------------------------------------------\n";
|
||||||
echo "ABORTING THE INSTALLER \n";
|
echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
|
||||||
echo "Please correct the issues above in ".getcwd()."/.env and try again.\n";
|
echo "Please correct the issues above in ".getcwd()."/.env and try again.\n";
|
||||||
echo "--------------------------------------------------------\n";
|
echo "--------------------------------------------------------\n";
|
||||||
exit;
|
exit;
|
||||||
|
@ -136,22 +138,23 @@ if ($env_bad !='') {
|
||||||
|
|
||||||
|
|
||||||
echo "\n--------------------------------------------------------\n";
|
echo "\n--------------------------------------------------------\n";
|
||||||
echo "STEP 2: Checking PHP requirements: \n";
|
echo "STEP 2: Checking PHP requirements: (Required PHP >=". $php_min_works. " - <".$php_max_wontwork.") \n";
|
||||||
echo "--------------------------------------------------------\n\n";
|
echo "--------------------------------------------------------\n\n";
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, $required_php_min, '<')) {
|
if ((version_compare(phpversion(), $php_min_works, '>=')) && (version_compare(phpversion(), $php_max_wontwork, '<'))) {
|
||||||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ERROR !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";
|
|
||||||
echo "This version of PHP (".PHP_VERSION.") is not compatible with Snipe-IT.\n";
|
echo "√ Current PHP version: (" . phpversion() . ") is at least " . $php_min_works . " and less than ".$php_max_wontwork."! Continuing... \n";
|
||||||
echo "Snipe-IT requires PHP version ".$required_php_min." or greater. Please upgrade \n";
|
echo sprintf("FYI: The php.ini used by this PHP is: %s\n\n", get_cfg_var('cfg_file_path'));
|
||||||
echo "your version of PHP (web/php-fcgi and cli) and try running this script again.\n\n\n";
|
|
||||||
exit;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
echo "Current PHP version: (" . PHP_VERSION . ") is at least ".$required_php_min." - continuing... \n";
|
echo "!!!!!!!!!!!!!!!!!!!!!!!!! PHP VERSION ERROR !!!!!!!!!!!!!!!!!!!!!!!!!\n";
|
||||||
echo sprintf("FYI: The php.ini used by this PHP is: %s\n\n", get_cfg_var('cfg_file_path'));
|
echo "This version of PHP (".phpversion().") is NOT compatible with Snipe-IT.\n";
|
||||||
|
echo "Snipe-IT requires PHP versions between ".$php_min_works." and ".$php_max_wontwork.".\n";
|
||||||
|
echo "Please install a compatible version of PHP and re-run this script again. \n";
|
||||||
|
echo "!!!!!!!!!!!!!!!!!!!!!!!!! ABORTING THE UPGRADER !!!!!!!!!!!!!!!!!!!!!!\n";
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
echo "Checking Required PHP extensions... \n\n";
|
echo "Checking Required PHP extensions... \n\n";
|
||||||
|
|
||||||
// Get the list of installed extensions
|
// Get the list of installed extensions
|
||||||
|
|
Loading…
Reference in a new issue