mirror of
https://github.com/snipe/snipe-it.git
synced 2025-01-03 18:07:41 -08:00
Merge remote-tracking branch 'origin/develop'
This commit is contained in:
commit
b99b56b32b
|
@ -17,3 +17,5 @@ DB_PORT=3306
|
|||
DB_DATABASE=null
|
||||
DB_USERNAME=null
|
||||
DB_PASSWORD=null
|
||||
|
||||
MAIL_FROM_ADDR=you@example.com
|
||||
|
|
5
.github/workflows/tests-sqlite.yml
vendored
5
.github/workflows/tests-sqlite.yml
vendored
|
@ -43,6 +43,9 @@ jobs:
|
|||
cp -v .env.testing.example .env
|
||||
cp -v .env.testing.example .env.testing
|
||||
|
||||
- name: Create database file
|
||||
run: touch database/database.sqlite
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
|
||||
|
||||
|
@ -57,5 +60,5 @@ jobs:
|
|||
|
||||
- name: Execute tests (Unit and Feature tests) via PHPUnit
|
||||
env:
|
||||
DB_CONNECTION: sqlite_testing
|
||||
DB_CONNECTION: sqlite
|
||||
run: php artisan test
|
||||
|
|
|
@ -33,6 +33,8 @@ use Illuminate\Http\Request;
|
|||
use App\Http\Requests\ImageUploadRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\View\Label;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -80,10 +82,10 @@ class AssetsController extends Controller
|
|||
$this->authorize('reports.view');
|
||||
} else {
|
||||
$transformer = 'App\Http\Transformers\AssetsTransformer';
|
||||
$this->authorize('index', Asset::class);
|
||||
$this->authorize('index', Asset::class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$allowed_columns = [
|
||||
|
@ -126,8 +128,19 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->with('location', 'assetstatus', 'company', 'defaultLoc','assignedTo', 'adminuser','model.depreciation',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier'); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
|
||||
->with(
|
||||
'location',
|
||||
'assetstatus',
|
||||
'company',
|
||||
'defaultLoc',
|
||||
'assignedTo',
|
||||
'adminuser',
|
||||
'model.depreciation',
|
||||
'model.category',
|
||||
'model.manufacturer',
|
||||
'model.fieldset',
|
||||
'supplier'
|
||||
); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
|
||||
|
||||
|
||||
if ($filter_non_deprecable_assets) {
|
||||
|
@ -159,8 +172,8 @@ class AssetsController extends Controller
|
|||
* Handle due and overdue audits and checkin dates
|
||||
*/
|
||||
switch ($action) {
|
||||
// Audit (singular) is left over from earlier legacy APIs
|
||||
case 'audits' :
|
||||
// Audit (singular) is left over from earlier legacy APIs
|
||||
case 'audits':
|
||||
switch ($upcoming_status) {
|
||||
case 'due':
|
||||
$assets->DueForAudit($settings);
|
||||
|
@ -187,7 +200,7 @@ class AssetsController extends Controller
|
|||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End handling due and overdue audits and checkin dates
|
||||
|
@ -265,7 +278,6 @@ class AssetsController extends Controller
|
|||
$join->on('status_alias.id', '=', 'assets.status_id');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -345,7 +357,7 @@ class AssetsController extends Controller
|
|||
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
switch ($sort_override) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
|
@ -399,7 +411,6 @@ class AssetsController extends Controller
|
|||
} else {
|
||||
$assets->orderBy($sort_override, $order);
|
||||
}
|
||||
|
||||
} else {
|
||||
$assets->orderBy($column_sort, $order);
|
||||
}
|
||||
|
@ -413,11 +424,11 @@ class AssetsController extends Controller
|
|||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Include additional associated relationships
|
||||
*/
|
||||
*/
|
||||
if ($request->input('components')) {
|
||||
$assets->loadMissing(['components' => function ($query) {
|
||||
$query->orderBy('created_at', 'desc');
|
||||
|
@ -441,7 +452,7 @@ class AssetsController extends Controller
|
|||
* @since [v4.2.1]
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function showByTag(Request $request, $tag) : JsonResponse | array
|
||||
public function showByTag(Request $request, $tag): JsonResponse | array
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
$assets = Asset::where('asset_tag', $tag)->with('assetstatus')->with('assignedTo');
|
||||
|
@ -463,12 +474,10 @@ class AssetsController extends Controller
|
|||
} else {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If there are 0 results, return the "no such asset" response
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -479,7 +488,7 @@ class AssetsController extends Controller
|
|||
* @since [v4.2.1]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function showBySerial(Request $request, $serial) : JsonResponse | array
|
||||
public function showBySerial(Request $request, $serial): JsonResponse | array
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
$assets = Asset::where('serial', $serial)->with('assetstatus')->with('assignedTo');
|
||||
|
@ -488,14 +497,13 @@ class AssetsController extends Controller
|
|||
if ($request->input('deleted', 'false') == 'true') {
|
||||
$assets = $assets->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
if (($assets = $assets->get()) && ($assets->count()) > 0) {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
}
|
||||
|
||||
// If there are 0 results, return the "no such asset" response
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -506,20 +514,20 @@ class AssetsController extends Controller
|
|||
* @since [v4.0]
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, $id) : JsonResponse | array
|
||||
public function show(Request $request, $id): JsonResponse | array
|
||||
{
|
||||
if ($asset = Asset::with('assetstatus')
|
||||
->with('assignedTo')->withTrashed()
|
||||
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)) {
|
||||
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->find($id)
|
||||
) {
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
return (new AssetsTransformer)->transformAsset($asset, $request->input('components') );
|
||||
return (new AssetsTransformer)->transformAsset($asset, $request->input('components'));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
public function licenses(Request $request, $id) : array
|
||||
public function licenses(Request $request, $id): array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
$this->authorize('view', License::class);
|
||||
|
@ -527,7 +535,7 @@ class AssetsController extends Controller
|
|||
$licenses = $asset->licenses()->get();
|
||||
|
||||
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -537,7 +545,7 @@ class AssetsController extends Controller
|
|||
* @since [v4.0.16]
|
||||
* @see \App\Http\Transformers\SelectlistTransformer
|
||||
*/
|
||||
public function selectlist(Request $request) : array
|
||||
public function selectlist(Request $request): array
|
||||
{
|
||||
|
||||
$assets = Asset::select([
|
||||
|
@ -548,7 +556,7 @@ class AssetsController extends Controller
|
|||
'assets.assigned_to',
|
||||
'assets.assigned_type',
|
||||
'assets.status_id',
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
|
||||
])->with('model', 'assetstatus', 'assignedTo')->NotArchived();
|
||||
|
||||
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
|
||||
$assets = $assets->RTD();
|
||||
|
@ -570,12 +578,12 @@ class AssetsController extends Controller
|
|||
$asset->use_text = $asset->present()->fullName;
|
||||
|
||||
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
|
||||
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
|
||||
$asset->use_text .= ' → ' . $asset->assigned->getFullNameAttribute();
|
||||
}
|
||||
|
||||
|
||||
if ($asset->assetstatus->getStatuslabelType() == 'pending') {
|
||||
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
|
||||
$asset->use_text .= '(' . $asset->assetstatus->getStatuslabelType() . ')';
|
||||
}
|
||||
|
||||
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
|
||||
|
@ -601,12 +609,12 @@ class AssetsController extends Controller
|
|||
$asset->created_by = auth()->id();
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
|
||||
|
@ -623,9 +631,9 @@ class AssetsController extends Controller
|
|||
|
||||
// If input value is null, use custom field's default value
|
||||
if ($field_val == null) {
|
||||
Log::debug('Field value for '.$field->db_column.' is null');
|
||||
Log::debug('Field value for ' . $field->db_column . ' is null');
|
||||
$field_val = $field->defaultValue($request->get('model_id'));
|
||||
Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
|
||||
Log::debug('Use the default fieldset value of ' . $field->defaultValue($request->get('model_id')));
|
||||
}
|
||||
|
||||
// if the field is set to encrypted, make sure we encrypt the value
|
||||
|
@ -643,7 +651,7 @@ class AssetsController extends Controller
|
|||
}
|
||||
}
|
||||
if ($field->element == 'checkbox') {
|
||||
if(is_array($field_val)) {
|
||||
if (is_array($field_val)) {
|
||||
$field_val = implode(',', $field_val);
|
||||
}
|
||||
}
|
||||
|
@ -702,59 +710,59 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
* this is here just legacy reasons. Api\AssetController
|
||||
* used image_source once to allow encoded image uploads.
|
||||
*/
|
||||
if ($request->has('image_source')) {
|
||||
$request->offsetSet('image', $request->offsetGet('image_source'));
|
||||
}
|
||||
|
||||
$asset = $request->handleImages($asset);
|
||||
$model = $asset->model;
|
||||
|
||||
// Update custom fields
|
||||
$problems_updating_encrypted_custom_fields = false;
|
||||
if (($model) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
$field_val = $request->input($field->db_column, null);
|
||||
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->element == 'checkbox') {
|
||||
if(is_array($field_val)) {
|
||||
$field_val = implode(',', $field_val);
|
||||
}
|
||||
// Update custom fields
|
||||
$problems_updating_encrypted_custom_fields = false;
|
||||
if (($model) && (isset($model->fieldset))) {
|
||||
foreach ($model->fieldset->fields as $field) {
|
||||
$field_val = $request->input($field->db_column, null);
|
||||
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->element == 'checkbox') {
|
||||
if (is_array($field_val)) {
|
||||
$field_val = implode(',', $field_val);
|
||||
}
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
$field_val = Crypt::encrypt($field_val);
|
||||
} else {
|
||||
$problems_updating_encrypted_custom_fields = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$asset->{$field->db_column} = $field_val;
|
||||
}
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
$field_val = Crypt::encrypt($field_val);
|
||||
} else {
|
||||
$problems_updating_encrypted_custom_fields = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$asset->{$field->db_column} = $field_val;
|
||||
}
|
||||
}
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
}
|
||||
if ($asset->save()) {
|
||||
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
|
||||
$location = $target->location_id;
|
||||
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
|
||||
$location = $target->location_id;
|
||||
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
}
|
||||
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $asset->id)
|
||||
->update(['location_id' => $target->location_id]);
|
||||
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
|
||||
$location = $target->id;
|
||||
}
|
||||
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
|
||||
}
|
||||
if (isset($target)) {
|
||||
$asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
|
||||
}
|
||||
|
||||
if ($asset->image) {
|
||||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
if ($asset->image) {
|
||||
$asset->image = $asset->getImageUrl();
|
||||
}
|
||||
|
||||
if ($problems_updating_encrypted_custom_fields) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.encrypted_warning')));
|
||||
|
@ -773,7 +781,7 @@ class AssetsController extends Controller
|
|||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function destroy($id) : JsonResponse
|
||||
public function destroy($id): JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Asset::class);
|
||||
|
||||
|
@ -799,7 +807,7 @@ class AssetsController extends Controller
|
|||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Restore a soft-deleted asset.
|
||||
|
@ -808,7 +816,7 @@ class AssetsController extends Controller
|
|||
* @param int $assetId
|
||||
* @since [v5.1.18]
|
||||
*/
|
||||
public function restore(Request $request, $assetId = null) : JsonResponse
|
||||
public function restore(Request $request, $assetId = null): JsonResponse
|
||||
{
|
||||
|
||||
if ($asset = Asset::withTrashed()->find($assetId)) {
|
||||
|
@ -827,7 +835,6 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -837,7 +844,7 @@ class AssetsController extends Controller
|
|||
* @param string $tag
|
||||
* @since [v6.0.5]
|
||||
*/
|
||||
public function checkoutByTag(AssetCheckoutRequest $request, $tag) : JsonResponse
|
||||
public function checkoutByTag(AssetCheckoutRequest $request, $tag): JsonResponse
|
||||
{
|
||||
if ($asset = Asset::where('asset_tag', $tag)->first()) {
|
||||
return $this->checkout($request, $asset->id);
|
||||
|
@ -852,13 +859,13 @@ class AssetsController extends Controller
|
|||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id) : JsonResponse
|
||||
public function checkout(AssetCheckoutRequest $request, $asset_id): JsonResponse
|
||||
{
|
||||
$this->authorize('checkout', Asset::class);
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
|
||||
if (! $asset->availableForCheckout()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
|
||||
}
|
||||
|
||||
$this->authorize('checkout', $asset);
|
||||
|
@ -875,14 +882,12 @@ class AssetsController extends Controller
|
|||
$asset->location_id = ($target) ? $target->id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_location');
|
||||
$error_payload['target_type'] = 'location';
|
||||
|
||||
} elseif (request('checkout_to_type') == 'asset') {
|
||||
$target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset'));
|
||||
// Override with the asset's location_id if it has one
|
||||
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
|
||||
$error_payload['target_id'] = $request->input('assigned_asset');
|
||||
$error_payload['target_type'] = 'asset';
|
||||
|
||||
} elseif (request('checkout_to_type') == 'user') {
|
||||
// Fetch the target and set the asset's new location_id
|
||||
$target = User::find(request('assigned_user'));
|
||||
|
@ -896,7 +901,7 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
if (! isset($target)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset ' . e($asset->asset_tag) . ' is invalid - ' . $error_payload['target_type'] . ' does not exist.'));
|
||||
}
|
||||
|
||||
$checkout_at = request('checkout_at', date('Y-m-d H:i:s'));
|
||||
|
@ -910,15 +915,15 @@ class AssetsController extends Controller
|
|||
// TODO: Follow up here. WTF. Commented out for now.
|
||||
|
||||
|
||||
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
|
||||
// $asset->location_id = $target->rtd_location_id;
|
||||
// }
|
||||
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
|
||||
// $asset->location_id = $target->rtd_location_id;
|
||||
// }
|
||||
|
||||
if ($asset->checkOut($target, auth()->user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
|
||||
}
|
||||
|
||||
|
||||
|
@ -929,7 +934,7 @@ class AssetsController extends Controller
|
|||
* @param int $assetId
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function checkin(Request $request, $asset_id) : JsonResponse
|
||||
public function checkin(Request $request, $asset_id): JsonResponse
|
||||
{
|
||||
$asset = Asset::with('model')->findOrFail($asset_id);
|
||||
$this->authorize('checkin', $asset);
|
||||
|
@ -937,7 +942,7 @@ class AssetsController extends Controller
|
|||
$target = $asset->assignedTo;
|
||||
if (is_null($target)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.already_checked_in')));
|
||||
|
@ -960,7 +965,7 @@ class AssetsController extends Controller
|
|||
if ($request->filled('location_id')) {
|
||||
$asset->location_id = $request->input('location_id');
|
||||
|
||||
if ($request->input('update_default_location')){
|
||||
if ($request->input('update_default_location')) {
|
||||
$asset->rtd_location_id = $request->input('location_id');
|
||||
}
|
||||
}
|
||||
|
@ -968,8 +973,8 @@ class AssetsController extends Controller
|
|||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->input('status_id');
|
||||
}
|
||||
|
||||
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at').' '. date('H:i:s') : date('Y-m-d H:i:s');
|
||||
|
||||
$checkin_at = $request->filled('checkin_at') ? $request->input('checkin_at') . ' ' . date('H:i:s') : date('Y-m-d H:i:s');
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) {
|
||||
|
@ -987,7 +992,8 @@ class AssetsController extends Controller
|
|||
[Asset::class],
|
||||
function (Builder $query) use ($asset) {
|
||||
$query->where('id', $asset->id);
|
||||
})
|
||||
}
|
||||
)
|
||||
->get()
|
||||
->map(function ($acceptance) {
|
||||
$acceptance->delete();
|
||||
|
@ -997,13 +1003,13 @@ class AssetsController extends Controller
|
|||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), $request->input('note'), $checkin_at, $originalValues));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'model' => e($asset->model->name),
|
||||
'model_number' => e($asset->model->model_number)
|
||||
], trans('admin/hardware/message.checkin.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', ['asset' => e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1012,7 +1018,7 @@ class AssetsController extends Controller
|
|||
* @author [A. Janes] [<ajanes@adagiohealth.org>]
|
||||
* @since [v6.0]
|
||||
*/
|
||||
public function checkinByTag(Request $request, $tag = null) : JsonResponse
|
||||
public function checkinByTag(Request $request, $tag = null): JsonResponse
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
if (null == $tag && null !== ($request->input('asset_tag'))) {
|
||||
|
@ -1025,8 +1031,8 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset'=> e($tag)
|
||||
], 'Asset with tag '.e($tag).' not found'));
|
||||
'asset' => e($tag)
|
||||
], 'Asset with tag ' . e($tag) . ' not found'));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1037,7 +1043,7 @@ class AssetsController extends Controller
|
|||
* @param int $id
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function audit(Request $request) : JsonResponse
|
||||
public function audit(Request $request): JsonResponse
|
||||
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
@ -1048,8 +1054,8 @@ class AssetsController extends Controller
|
|||
// No tag passed - return an error
|
||||
if (!$request->filled('asset_tag')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> '',
|
||||
'error'=> trans('admin/hardware/message.no_tag'),
|
||||
'asset_tag' => '',
|
||||
'error' => trans('admin/hardware/message.no_tag'),
|
||||
], trans('admin/hardware/message.no_tag')), 200);
|
||||
}
|
||||
|
||||
|
@ -1097,28 +1103,25 @@ class AssetsController extends Controller
|
|||
$asset->logAudit(request('note'), request('location_id'));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'asset_tag'=> e($asset->asset_tag),
|
||||
'note'=> e($request->input('note')),
|
||||
'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(),
|
||||
'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.
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag'=> e($request->input('asset_tag')),
|
||||
'error'=> trans('admin/hardware/message.audit.error'),
|
||||
'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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1129,7 +1132,7 @@ class AssetsController extends Controller
|
|||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function requestable(Request $request) : JsonResponse | array
|
||||
public function requestable(Request $request): JsonResponse | array
|
||||
{
|
||||
$this->authorize('viewRequestable', Asset::class);
|
||||
|
||||
|
@ -1150,8 +1153,18 @@ class AssetsController extends Controller
|
|||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
->with('location', 'assetstatus', 'assetlog', 'company','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier', 'requests');
|
||||
->with(
|
||||
'location',
|
||||
'assetstatus',
|
||||
'assetlog',
|
||||
'company',
|
||||
'assignedTo',
|
||||
'model.category',
|
||||
'model.manufacturer',
|
||||
'model.fieldset',
|
||||
'supplier',
|
||||
'requests'
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
@ -1159,7 +1172,7 @@ class AssetsController extends Controller
|
|||
if ($request->filled('search')) {
|
||||
$assets->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
// Search custom fields by column name
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if ($request->filled($field->db_column_name())) {
|
||||
|
@ -1200,4 +1213,89 @@ class AssetsController extends Controller
|
|||
|
||||
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate asset labels by tag
|
||||
*
|
||||
* @author [Nebelkreis] [https://github.com/NebelKreis]
|
||||
*
|
||||
* @param Request $request Contains asset_tags array of asset tags to generate labels for
|
||||
* @return JsonResponse Returns base64 encoded PDF on success, error message on failure
|
||||
*/
|
||||
public function getLabels(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
// Validate that asset tags were provided in the request
|
||||
if (!$request->filled('asset_tags')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null,
|
||||
trans('admin/hardware/message.no_assets_selected')), 400);
|
||||
}
|
||||
|
||||
// Convert asset tags from request into collection and fetch matching assets
|
||||
$asset_tags = collect($request->input('asset_tags'));
|
||||
$assets = Asset::whereIn('asset_tag', $asset_tags)->get();
|
||||
|
||||
// Return error if no assets were found for the provided tags
|
||||
if ($assets->isEmpty()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null,
|
||||
trans('admin/hardware/message.does_not_exist')), 404);
|
||||
}
|
||||
|
||||
try {
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
// Check if logo file exists in storage and disable logo if not found
|
||||
// This prevents errors when trying to include a non-existent logo in the PDF
|
||||
$settings->label_logo = ($original_logo = $settings->label_logo) && !Storage::disk('public')->exists('/' . $original_logo) ? null : $settings->label_logo;
|
||||
|
||||
|
||||
$label = new Label();
|
||||
|
||||
if (!$label) {
|
||||
throw new \Exception('Label object could not be created');
|
||||
}
|
||||
|
||||
// Configure label with assets and settings
|
||||
// bulkedit=false and count=0 are default values for label generation
|
||||
$label = $label->with('assets', $assets)
|
||||
->with('settings', $settings)
|
||||
->with('bulkedit', false)
|
||||
->with('count', 0);
|
||||
|
||||
// Generate PDF using callback function
|
||||
// The callback captures the PDF content in $pdf_content variable
|
||||
$pdf_content = '';
|
||||
$label->render(function($pdf) use (&$pdf_content) {
|
||||
$pdf_content = $pdf->Output('', 'S');
|
||||
return $pdf;
|
||||
});
|
||||
|
||||
// Verify PDF was generated successfully
|
||||
if (empty($pdf_content)) {
|
||||
throw new \Exception('PDF content is empty');
|
||||
}
|
||||
|
||||
$encoded_content = base64_encode($pdf_content);
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', [
|
||||
'pdf' => $encoded_content
|
||||
], trans('admin/hardware/message.labels_generated')));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'error_message' => $e->getMessage(),
|
||||
'error_line' => $e->getLine(),
|
||||
'error_file' => $e->getFile()
|
||||
], trans('admin/hardware/message.error_generating_labels')), 500);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'error_message' => $e->getMessage(),
|
||||
'error_line' => $e->getLine(),
|
||||
'error_file' => $e->getFile()
|
||||
], $e->getMessage()), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ class ComponentsController extends Controller
|
|||
$this->authorize('delete', $component);
|
||||
|
||||
// Remove the image if one exists
|
||||
if (Storage::disk('public')->exists('components/'.$component->image)) {
|
||||
if ($component->image && Storage::disk('public')->exists('components/' . $component->image)) {
|
||||
try {
|
||||
Storage::disk('public')->delete('components/'.$component->image);
|
||||
} catch (\Exception $e) {
|
||||
|
|
|
@ -307,7 +307,7 @@ class CheckoutableListener
|
|||
return $event->checkedOutTo->manager?->email ?? '';
|
||||
}
|
||||
else{
|
||||
return $event->checkedOutTo->email;
|
||||
return $event->checkedOutTo?->email ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class CheckinAccessoryMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -43,7 +43,7 @@ class CheckinAssetMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -34,7 +34,7 @@ class CheckinLicenseMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -37,7 +37,7 @@ class CheckoutAccessoryMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -52,7 +52,7 @@ class CheckoutAssetMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR', 'service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -38,7 +38,7 @@ class CheckoutConsumableMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -36,7 +36,7 @@ class CheckoutLicenseMail extends Mailable
|
|||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io'));
|
||||
$from = new Address(config('mail.from.address'));
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
|
|
|
@ -2,13 +2,16 @@
|
|||
|
||||
return [
|
||||
|
||||
'undeployable' => '<strong>Warning: </strong> This asset has been marked as currently undeployable. If this status has changed, please update the asset status.',
|
||||
'does_not_exist' => 'Asset does not exist.',
|
||||
'does_not_exist_var'=> 'Asset with tag :asset_tag not found.',
|
||||
'no_tag' => 'No asset tag provided.',
|
||||
'undeployable' => '<strong>Warning: </strong> This asset has been marked as currently undeployable. If this status has changed, please update the asset status.',
|
||||
'does_not_exist' => 'Asset does not exist.',
|
||||
'does_not_exist_var' => 'Asset with tag :asset_tag not found.',
|
||||
'no_tag' => 'No asset tag provided.',
|
||||
'does_not_exist_or_not_requestable' => 'That asset does not exist or is not requestable.',
|
||||
'assoc_users' => 'This asset is currently checked out to a user and cannot be deleted. Please check the asset in first, and then try deleting again. ',
|
||||
'assoc_users' => 'This asset is currently checked out to a user and cannot be deleted. Please check the asset in first, and then try deleting again. ',
|
||||
'warning_audit_date_mismatch' => 'This asset\'s next audit date (:next_audit_date) is before the last audit date (:last_audit_date). Please update the next audit date.',
|
||||
'labels_generated' => 'Labels were successfully generated.',
|
||||
'error_generating_labels' => 'Error while generating labels.',
|
||||
'no_assets_selected' => 'No assets selected.',
|
||||
|
||||
'create' => [
|
||||
'error' => 'Asset was not created, please try again. :(',
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
@if (($item->name!=$item->asset_tag))
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} |
|
||||
@endif
|
||||
@if (isset($item->model->category))
|
||||
| **{{ trans('general.category') }}** | {{ $item->model->category->name }} |
|
||||
@endif
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
@endif
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
@if (($item->name!=$item->asset_tag))
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} |
|
||||
@endif
|
||||
@if (isset($item->model->category))
|
||||
| **{{ trans('general.category') }}** | {{ $item->model->category->name }} |
|
||||
@endif
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
@endif
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
@if ((isset($item_tag)) && ($item_tag!=''))
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item_tag }} |
|
||||
@endif
|
||||
@if (isset($item->model->category))
|
||||
| **{{ trans('general.category') }}** | {{ $item->model->category->name }} |
|
||||
@endif
|
||||
@if ((isset($item_model)) && ($item_model!=''))
|
||||
| **{{ trans('mail.asset_name') }}** | {{ $item_model }} |
|
||||
@endif
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
@if ((isset($item->asset_tag)) && ($item->asset_tag!=''))
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} |
|
||||
@endif
|
||||
@if (isset($item->model->category))
|
||||
| **{{ trans('general.category') }}** | {{ $item->model->category->name }} |
|
||||
@endif
|
||||
@if ((isset($item->name)) && ($item->name!=''))
|
||||
| **{{ trans('mail.asset_name') }}** | {{ $item->name }} |
|
||||
@endif
|
||||
|
|
|
@ -1282,4 +1282,14 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
|
|||
], 404);
|
||||
}); // end fallback routes
|
||||
|
||||
/**
|
||||
* Generate label routes
|
||||
*/
|
||||
Route::post('hardware/labels', [
|
||||
Api\AssetsController::class,
|
||||
'getLabels'
|
||||
])->name('api.assets.labels');
|
||||
// end generate label routes
|
||||
|
||||
|
||||
}); // end API routes
|
||||
|
|
140
tests/Feature/Checkouts/Api/ComponentCheckoutTest.php
Normal file
140
tests/Feature/Checkouts/Api/ComponentCheckoutTest.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Checkouts\Api;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ComponentCheckoutTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->create())
|
||||
->postJson(route('api.components.checkout', $component->id))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testCannotCheckoutNonExistentComponent()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->checkoutComponents()->create())
|
||||
->postJson(route('api.components.checkout', 1000))
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->assertMessagesAre('Component does not exist.');
|
||||
}
|
||||
|
||||
public function testCheckingOutComponentRequiresValidFields()
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->checkoutComponents()->create())
|
||||
->postJson(route('api.components.checkout', $component->id), [
|
||||
//
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->assertPayloadContains('assigned_to')
|
||||
->assertPayloadContains('assigned_qty');
|
||||
}
|
||||
|
||||
public function testCannotCheckoutComponentIfRequestedAmountIsMoreThanComponentQuantity()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$component = Component::factory()->create(['qty' => 2]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->checkoutComponents()->create())
|
||||
->postJson(route('api.components.checkout', $component->id), [
|
||||
'assigned_to' => $asset->id,
|
||||
'assigned_qty' => 3,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error')
|
||||
->assertMessagesAre(trans('admin/components/message.checkout.unavailable', ['remaining' => 2, 'requested' => 3]));
|
||||
}
|
||||
|
||||
public function testCannotCheckoutComponentIfRequestedAmountIsMoreThanWhatIsRemaining()
|
||||
{
|
||||
$asset = Asset::factory()->create();
|
||||
$component = Component::factory()->create(['qty' => 2]);
|
||||
$component->assets()->attach($component->id, [
|
||||
'component_id' => $component->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'assigned_qty' => 1,
|
||||
'asset_id' => $asset->id,
|
||||
]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->checkoutComponents()->create())
|
||||
->postJson(route('api.components.checkout', $component->id), [
|
||||
'assigned_to' => $asset->id,
|
||||
'assigned_qty' => 3,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
||||
public function testCanCheckoutComponent()
|
||||
{
|
||||
$user = User::factory()->checkoutComponents()->create();
|
||||
$asset = Asset::factory()->create();
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->postJson(route('api.components.checkout', $component->id), [
|
||||
'assigned_to' => $asset->id,
|
||||
'assigned_qty' => 1,
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success');
|
||||
|
||||
$this->assertTrue($component->assets->first()->is($asset));
|
||||
}
|
||||
|
||||
public function testComponentCheckoutIsLogged()
|
||||
{
|
||||
$user = User::factory()->checkoutComponents()->create();
|
||||
$location = Location::factory()->create();
|
||||
$asset = Asset::factory()->create(['location_id' => $location->id]);
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->postJson(route('api.components.checkout', $component->id), [
|
||||
'assigned_to' => $asset->id,
|
||||
'assigned_qty' => 1,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'created_by' => $user->id,
|
||||
'action_type' => 'checkout',
|
||||
'target_id' => $asset->id,
|
||||
'target_type' => Asset::class,
|
||||
'location_id' => $location->id,
|
||||
'item_type' => Component::class,
|
||||
'item_id' => $component->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$userForCompanyA = User::factory()->for($companyA)->create();
|
||||
$assetForCompanyB = Asset::factory()->for($companyB)->create();
|
||||
$componentForCompanyB = Component::factory()->for($companyB)->create();
|
||||
|
||||
$this->actingAsForApi($userForCompanyA)
|
||||
->postJson(route('api.components.checkout', $componentForCompanyB->id), [
|
||||
'assigned_to' => $assetForCompanyB->id,
|
||||
'assigned_qty' => 1,
|
||||
])
|
||||
->assertForbidden();
|
||||
}
|
||||
}
|
88
tests/Feature/Components/Ui/DeleteComponentTest.php
Normal file
88
tests/Feature/Components/Ui/DeleteComponentTest.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Components\Ui;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
|
||||
class DeleteComponentTest extends TestCase implements TestsFullMultipleCompaniesSupport, TestsPermissionsRequirement
|
||||
{
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAs(User::factory()->create())
|
||||
->delete(route('components.destroy', $component->id))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testHandlesNonExistentComponent()
|
||||
{
|
||||
$this->actingAs(User::factory()->deleteComponents()->create())
|
||||
->delete(route('components.destroy', 10000))
|
||||
->assertSessionHas('error');
|
||||
}
|
||||
|
||||
public function testCanDeleteComponent()
|
||||
{
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAs(User::factory()->deleteComponents()->create())
|
||||
->delete(route('components.destroy', $component->id))
|
||||
->assertSessionHas('success')
|
||||
->assertRedirect(route('components.index'));
|
||||
|
||||
$this->assertSoftDeleted($component);
|
||||
}
|
||||
|
||||
public function testDeletingComponentRemovesComponentImage()
|
||||
{
|
||||
Storage::fake('public');
|
||||
|
||||
$component = Component::factory()->create(['image' => 'component-image.jpg']);
|
||||
|
||||
Storage::disk('public')->put('components/component-image.jpg', 'content');
|
||||
|
||||
Storage::disk('public')->assertExists('components/component-image.jpg');
|
||||
|
||||
$this->actingAs(User::factory()->deleteComponents()->create())->delete(route('components.destroy', $component->id));
|
||||
|
||||
Storage::disk('public')->assertMissing('components/component-image.jpg');
|
||||
}
|
||||
|
||||
public function testDeletingComponentIsLogged()
|
||||
{
|
||||
$user = User::factory()->deleteComponents()->create();
|
||||
$component = Component::factory()->create();
|
||||
|
||||
$this->actingAs($user)->delete(route('components.destroy', $component->id));
|
||||
|
||||
$this->assertDatabaseHas('action_logs', [
|
||||
'created_by' => $user->id,
|
||||
'action_type' => 'delete',
|
||||
'item_type' => Component::class,
|
||||
'item_id' => $component->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAdheresToFullMultipleCompaniesSupportScoping()
|
||||
{
|
||||
$this->settings->enableMultipleFullCompanySupport();
|
||||
|
||||
[$companyA, $companyB] = Company::factory()->count(2)->create();
|
||||
|
||||
$userInCompanyA = User::factory()->for($companyA)->create();
|
||||
$componentForCompanyB = Component::factory()->for($companyB)->create();
|
||||
|
||||
$this->actingAs($userInCompanyA)
|
||||
->delete(route('components.destroy', $componentForCompanyB->id))
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($componentForCompanyB);
|
||||
}
|
||||
}
|
|
@ -121,5 +121,26 @@ trait CustomTestMacros
|
|||
return $this;
|
||||
}
|
||||
);
|
||||
|
||||
TestResponse::macro(
|
||||
'assertPayloadContains',
|
||||
function (array|string $keys) {
|
||||
Assert::assertArrayHasKey('payload', $this, 'Response did not contain a payload');
|
||||
|
||||
if (is_string($keys)) {
|
||||
$keys = [$keys];
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
Assert::assertArrayHasKey(
|
||||
$key,
|
||||
$this['payload'],
|
||||
"Response messages did not contain the key: {$key}"
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue