Skip to content

Commit c305284

Browse files
authored
Merge pull request #15922 from spencerrlongg/feature/sc-24347
Requestable/Request Item API Endpoints
2 parents d95549b + 6201e47 commit c305284

File tree

12 files changed

+234
-61
lines changed

12 files changed

+234
-61
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace App\Actions\CheckoutRequests;
4+
5+
use App\Models\Actionlog;
6+
use App\Models\Asset;
7+
use App\Models\Company;
8+
use App\Models\Setting;
9+
use App\Models\User;
10+
use App\Notifications\RequestAssetCancelation;
11+
use Illuminate\Auth\Access\AuthorizationException;
12+
13+
class CancelCheckoutRequestAction
14+
{
15+
public static function run(Asset $asset, User $user)
16+
{
17+
if (!Company::isCurrentUserHasAccess($asset)) {
18+
throw new AuthorizationException();
19+
}
20+
21+
$asset->cancelRequest();
22+
23+
$asset->decrement('requests_counter', 1);
24+
25+
$data['item'] = $asset;
26+
$data['target'] = $user;
27+
$data['item_quantity'] = 1;
28+
$settings = Setting::getSettings();
29+
30+
$logaction = new Actionlog();
31+
$logaction->item_id = $data['asset_id'] = $asset->id;
32+
$logaction->item_type = $data['item_type'] = Asset::class;
33+
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
34+
$logaction->target_id = $data['user_id'] = auth()->id();
35+
$logaction->target_type = User::class;
36+
$logaction->location_id = $user->location_id ?? null;
37+
$logaction->logaction('request canceled');
38+
39+
try {
40+
$settings->notify(new RequestAssetCancelation($data));
41+
} catch (\Exception $e) {
42+
\Log::warning($e);
43+
}
44+
45+
return true;
46+
}
47+
48+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace App\Actions\CheckoutRequests;
4+
5+
use App\Exceptions\AssetNotRequestable;
6+
use App\Models\Actionlog;
7+
use App\Models\Asset;
8+
use App\Models\Company;
9+
use App\Models\Setting;
10+
use App\Models\User;
11+
use App\Notifications\RequestAssetNotification;
12+
use Illuminate\Auth\Access\AuthorizationException;
13+
use Log;
14+
15+
class CreateCheckoutRequestAction
16+
{
17+
/**
18+
* @throws AssetNotRequestable
19+
* @throws AuthorizationException
20+
*/
21+
public static function run(Asset $asset, User $user): string
22+
{
23+
if (is_null(Asset::RequestableAssets()->find($asset->id))) {
24+
throw new AssetNotRequestable($asset);
25+
}
26+
if (!Company::isCurrentUserHasAccess($asset)) {
27+
throw new AuthorizationException();
28+
}
29+
30+
$data['item'] = $asset;
31+
$data['target'] = $user;
32+
$data['item_quantity'] = 1;
33+
$settings = Setting::getSettings();
34+
35+
$logaction = new Actionlog();
36+
$logaction->item_id = $data['asset_id'] = $asset->id;
37+
$logaction->item_type = $data['item_type'] = Asset::class;
38+
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
39+
$logaction->target_id = $data['user_id'] = auth()->id();
40+
$logaction->target_type = User::class;
41+
$logaction->location_id = $user->location_id ?? null;
42+
$logaction->logaction('requested');
43+
44+
$asset->request();
45+
$asset->increment('requests_counter', 1);
46+
try {
47+
$settings->notify(new RequestAssetNotification($data));
48+
} catch (\Exception $e) {
49+
Log::warning($e);
50+
}
51+
52+
return true;
53+
}
54+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace App\Exceptions;
4+
5+
use Exception;
6+
7+
class AssetNotRequestable extends Exception
8+
{
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace App\Exceptions;
4+
5+
use Exception;
6+
7+
class UserDoestExistException extends Exception
8+
{
9+
10+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Api;
4+
5+
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
6+
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
7+
use App\Exceptions\AssetNotRequestable;
8+
use App\Helpers\Helper;
9+
use App\Http\Controllers\Controller;
10+
use App\Models\Asset;
11+
use Illuminate\Auth\Access\AuthorizationException;
12+
use Illuminate\Http\JsonResponse;
13+
use Exception;
14+
15+
class CheckoutRequest extends Controller
16+
{
17+
public function store(Asset $asset): JsonResponse
18+
{
19+
try {
20+
CreateCheckoutRequestAction::run($asset, auth()->user());
21+
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success')));
22+
} catch (AssetNotRequestable $e) {
23+
return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable'));
24+
} catch (AuthorizationException $e) {
25+
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
26+
} catch (Exception $e) {
27+
report($e);
28+
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
29+
}
30+
}
31+
32+
public function destroy(Asset $asset): JsonResponse
33+
{
34+
try {
35+
CancelCheckoutRequestAction::run($asset, auth()->user());
36+
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled')));
37+
} catch (AuthorizationException $e) {
38+
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
39+
} catch (Exception $e) {
40+
report($e);
41+
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
42+
}
43+
}
44+
}

app/Http/Controllers/ViewAssetsController.php

Lines changed: 26 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22

33
namespace App\Http\Controllers;
44

5+
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
6+
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
7+
use App\Exceptions\AssetNotRequestable;
58
use App\Models\Actionlog;
69
use App\Models\Asset;
710
use App\Models\AssetModel;
8-
use App\Models\Company;
911
use App\Models\Setting;
1012
use App\Models\User;
1113
use App\Notifications\RequestAssetCancelation;
1214
use App\Notifications\RequestAssetNotification;
15+
use Illuminate\Auth\Access\AuthorizationException;
1316
use Illuminate\Http\Request;
1417
use Illuminate\Http\RedirectResponse;
1518
use \Illuminate\Contracts\View\View;
16-
use Log;
19+
use Exception;
1720

1821
/**
1922
* This controller handles all actions related to the ability for users
@@ -81,7 +84,7 @@ public function getRequestableIndex() : View
8184
return view('account/requestable-assets', compact('assets', 'models'));
8285
}
8386

84-
public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null) : RedirectResponse
87+
public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null): RedirectResponse
8588
{
8689
$item = null;
8790
$fullItemType = 'App\\Models\\'.studly_case($itemType);
@@ -144,63 +147,33 @@ public function getRequestItem(Request $request, $itemType, $itemId = null, $can
144147
* Process a specific requested asset
145148
* @param null $assetId
146149
*/
147-
public function getRequestAsset($assetId = null) : RedirectResponse
150+
public function store(Asset $asset): RedirectResponse
148151
{
149-
$user = auth()->user();
150-
151-
// Check if the asset exists and is requestable
152-
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
153-
return redirect()->route('requestable-assets')
154-
->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
155-
}
156-
if (! Company::isCurrentUserHasAccess($asset)) {
157-
return redirect()->route('requestable-assets')
158-
->with('error', trans('general.insufficient_permissions'));
159-
}
160-
161-
$data['item'] = $asset;
162-
$data['target'] = auth()->user();
163-
$data['item_quantity'] = 1;
164-
$settings = Setting::getSettings();
165-
166-
$logaction = new Actionlog();
167-
$logaction->item_id = $data['asset_id'] = $asset->id;
168-
$logaction->item_type = $data['item_type'] = Asset::class;
169-
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
170-
171-
if ($user->location_id) {
172-
$logaction->location_id = $user->location_id;
173-
}
174-
$logaction->target_id = $data['user_id'] = auth()->id();
175-
$logaction->target_type = User::class;
176-
177-
// If it's already requested, cancel the request.
178-
if ($asset->isRequestedBy(auth()->user())) {
179-
$asset->cancelRequest();
180-
$asset->decrement('requests_counter', 1);
181-
182-
$logaction->logaction('request canceled');
183-
try {
184-
$settings->notify(new RequestAssetCancelation($data));
185-
} catch (\Exception $e) {
186-
Log::warning($e);
187-
}
188-
return redirect()->route('requestable-assets')
189-
->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
152+
try {
153+
CreateCheckoutRequestAction::run($asset, auth()->user());
154+
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
155+
} catch (AssetNotRequestable $e) {
156+
return redirect()->back()->with('error', 'Asset is not requestable');
157+
} catch (AuthorizationException $e) {
158+
return redirect()->back()->with('error', trans('admin/hardware/message.requests.error'));
159+
} catch (Exception $e) {
160+
report($e);
161+
return redirect()->back()->with('error', trans('general.something_went_wrong'));
190162
}
163+
}
191164

192-
$logaction->logaction('requested');
193-
$asset->request();
194-
$asset->increment('requests_counter', 1);
165+
public function destroy(Asset $asset): RedirectResponse
166+
{
195167
try {
196-
$settings->notify(new RequestAssetNotification($data));
197-
} catch (\Exception $e) {
198-
Log::warning($e);
168+
CancelCheckoutRequestAction::run($asset, auth()->user());
169+
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
170+
} catch (Exception $e) {
171+
report($e);
172+
return redirect()->back()->with('error', trans('general.something_went_wrong'));
199173
}
200-
201-
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
202174
}
203175

176+
204177
public function getRequestedAssets() : View
205178
{
206179
return view('account/requested');

app/Models/Asset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1456,7 +1456,7 @@ public function scopeDeployed($query)
14561456
* @return \Illuminate\Database\Query\Builder Modified query builder
14571457
*/
14581458

1459-
public function scopeRequestableAssets($query)
1459+
public function scopeRequestableAssets($query): Builder
14601460
{
14611461
$table = $query->getModel()->getTable();
14621462

database/factories/AssetFactory.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,22 @@ public function deleted()
347347

348348
public function requestable()
349349
{
350-
return $this->state(['requestable' => true]);
350+
$id = Statuslabel::factory()->create([
351+
'archived' => false,
352+
'deployable' => true,
353+
'pending' => true,
354+
])->id;
355+
return $this->state(['status_id' => $id, 'requestable' => true]);
351356
}
352357

353358
public function nonrequestable()
354359
{
355-
return $this->state(['requestable' => false]);
360+
$id = Statuslabel::factory()->create([
361+
'archived' => true,
362+
'deployable' => false,
363+
'pending' => false,
364+
])->id;
365+
return $this->state(['status_id' => $id, 'requestable' => false]);
356366
}
357367

358368
public function noPurchaseOrEolDate()

resources/views/partials/bootstrap-table.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ function assetRequestActionsFormatter (row, value) {
542542
if (value.assigned_to_self == true){
543543
return '<button class="btn btn-danger btn-sm disabled" data-tooltip="true" title="Cancel this item request">{{ trans('button.cancel') }}</button>';
544544
} else if (value.available_actions.cancel == true) {
545-
return '<form action="{{ config('app.url') }}/account/request-asset/'+ value.id + '" method="POST">@csrf<button class="btn btn-danger btn-sm" data-tooltip="true" title="Cancel this item request">{{ trans('button.cancel') }}</button></form>';
545+
return '<form action="{{ config('app.url') }}/account/request-asset/' + value.id + '/cancel" method="POST">@csrf<button class="btn btn-danger btn-sm" data-tooltip="true" title="Cancel this item request">{{ trans('button.cancel') }}</button></form>';
546546
} else if (value.available_actions.request == true) {
547547
return '<form action="{{ config('app.url') }}/account/request-asset/'+ value.id + '" method="POST">@csrf<button class="btn btn-primary btn-sm" data-tooltip="true" title="{{ trans('general.request_item') }}">{{ trans('button.request') }}</button></form>';
548548
}

routes/api.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
]
4141
)->name('api.assets.requested');
4242

43+
Route::post('request/{asset}', [Api\CheckoutRequest::class, 'store'])->name('api.assets.requests.store');
44+
Route::post('request/{asset}/cancel', [Api\CheckoutRequest::class, 'destroy'])->name('api.assets.requests.destroy');
45+
4346
Route::get('requestable/hardware',
4447
[
4548
Api\AssetsController::class,

0 commit comments

Comments
 (0)