Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Requestable/Request Item API Endpoints #15922

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
48 changes: 48 additions & 0 deletions app/Actions/CheckoutRequests/CancelCheckoutRequestAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace App\Actions\CheckoutRequests;

use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetCancelation;
use Illuminate\Auth\Access\AuthorizationException;

class CancelCheckoutRequestAction
{
public static function run(Asset $asset, User $user)
{
if (!Company::isCurrentUserHasAccess($asset)) {
throw new AuthorizationException();
}

$asset->cancelRequest();

$asset->decrement('requests_counter', 1);

$data['item'] = $asset;
$data['target'] = $user;
$data['item_quantity'] = 1;
$settings = Setting::getSettings();

$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = $data['item_type'] = Asset::class;
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;
$logaction->location_id = $user->location_id ?? null;
$logaction->logaction('request canceled');

try {
$settings->notify(new RequestAssetCancelation($data));
} catch (\Exception $e) {
\Log::warning($e);
}

return true;
}

}
54 changes: 54 additions & 0 deletions app/Actions/CheckoutRequests/CreateCheckoutRequestAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace App\Actions\CheckoutRequests;

use App\Exceptions\AssetNotRequestable;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetNotification;
use Illuminate\Auth\Access\AuthorizationException;
use Log;

class CreateCheckoutRequestAction
{
/**
* @throws AssetNotRequestable
* @throws AuthorizationException
*/
public static function run(Asset $asset, User $user): string
{
if (is_null(Asset::RequestableAssets()->find($asset->id))) {
throw new AssetNotRequestable($asset);
}
if (!Company::isCurrentUserHasAccess($asset)) {
throw new AuthorizationException();
}

$data['item'] = $asset;
$data['target'] = $user;
$data['item_quantity'] = 1;
$settings = Setting::getSettings();

$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = $data['item_type'] = Asset::class;
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;
$logaction->location_id = $user->location_id ?? null;
$logaction->logaction('requested');

$asset->request();
$asset->increment('requests_counter', 1);
try {
$settings->notify(new RequestAssetNotification($data));
} catch (\Exception $e) {
Log::warning($e);
}

return true;
}
}
9 changes: 9 additions & 0 deletions app/Exceptions/AssetNotRequestable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

class AssetNotRequestable extends Exception
{
}
10 changes: 10 additions & 0 deletions app/Exceptions/UserDoestExistException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Exceptions;

use Exception;

class UserDoestExistException extends Exception
{

}
44 changes: 44 additions & 0 deletions app/Http/Controllers/Api/CheckoutRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace App\Http\Controllers\Api;

use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
use App\Exceptions\AssetNotRequestable;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Models\Asset;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\JsonResponse;
use Exception;

class CheckoutRequest extends Controller
{
public function store(Asset $asset): JsonResponse
{
try {
CreateCheckoutRequestAction::run($asset, auth()->user());
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success')));
} catch (AssetNotRequestable $e) {
return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable'));
} catch (AuthorizationException $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
} catch (Exception $e) {
report($e);
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
}
}

public function destroy(Asset $asset): JsonResponse
{
try {
CancelCheckoutRequestAction::run($asset, auth()->user());
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled')));
} catch (AuthorizationException $e) {
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
} catch (Exception $e) {
report($e);
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
}
}
}
79 changes: 26 additions & 53 deletions app/Http/Controllers/ViewAssetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

namespace App\Http\Controllers;

use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
use App\Exceptions\AssetNotRequestable;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\Setting;
use App\Models\User;
use App\Notifications\RequestAssetCancelation;
use App\Notifications\RequestAssetNotification;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
use Log;
use Exception;

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

public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null) : RedirectResponse
public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null): RedirectResponse
{
$item = null;
$fullItemType = 'App\\Models\\'.studly_case($itemType);
Expand Down Expand Up @@ -144,63 +147,33 @@ public function getRequestItem(Request $request, $itemType, $itemId = null, $can
* Process a specific requested asset
* @param null $assetId
*/
public function getRequestAsset($assetId = null) : RedirectResponse
public function store(Asset $asset): RedirectResponse
{
$user = auth()->user();

// Check if the asset exists and is requestable
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
return redirect()->route('requestable-assets')
->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
}
if (! Company::isCurrentUserHasAccess($asset)) {
return redirect()->route('requestable-assets')
->with('error', trans('general.insufficient_permissions'));
}

$data['item'] = $asset;
$data['target'] = auth()->user();
$data['item_quantity'] = 1;
$settings = Setting::getSettings();

$logaction = new Actionlog();
$logaction->item_id = $data['asset_id'] = $asset->id;
$logaction->item_type = $data['item_type'] = Asset::class;
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');

if ($user->location_id) {
$logaction->location_id = $user->location_id;
}
$logaction->target_id = $data['user_id'] = auth()->id();
$logaction->target_type = User::class;

// If it's already requested, cancel the request.
if ($asset->isRequestedBy(auth()->user())) {
$asset->cancelRequest();
$asset->decrement('requests_counter', 1);

$logaction->logaction('request canceled');
try {
$settings->notify(new RequestAssetCancelation($data));
} catch (\Exception $e) {
Log::warning($e);
}
return redirect()->route('requestable-assets')
->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
try {
CreateCheckoutRequestAction::run($asset, auth()->user());
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
} catch (AssetNotRequestable $e) {
return redirect()->back()->with('error', 'Asset is not requestable');
} catch (AuthorizationException $e) {
return redirect()->back()->with('error', trans('admin/hardware/message.requests.error'));
} catch (Exception $e) {
report($e);
return redirect()->back()->with('error', trans('general.something_went_wrong'));
}
}

$logaction->logaction('requested');
$asset->request();
$asset->increment('requests_counter', 1);
public function destroy(Asset $asset): RedirectResponse
{
try {
$settings->notify(new RequestAssetNotification($data));
} catch (\Exception $e) {
Log::warning($e);
CancelCheckoutRequestAction::run($asset, auth()->user());
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
} catch (Exception $e) {
report($e);
return redirect()->back()->with('error', trans('general.something_went_wrong'));
}

return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
}


public function getRequestedAssets() : View
{
return view('account/requested');
Expand Down
2 changes: 1 addition & 1 deletion app/Models/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,7 @@ public function scopeDeployed($query)
* @return \Illuminate\Database\Query\Builder Modified query builder
*/

public function scopeRequestableAssets($query)
public function scopeRequestableAssets($query): Builder
{
$table = $query->getModel()->getTable();

Expand Down
14 changes: 12 additions & 2 deletions database/factories/AssetFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,22 @@ public function deleted()

public function requestable()
{
return $this->state(['requestable' => true]);
$id = Statuslabel::factory()->create([
'archived' => false,
'deployable' => true,
'pending' => true,
])->id;
return $this->state(['status_id' => $id, 'requestable' => true]);
}

public function nonrequestable()
{
return $this->state(['requestable' => false]);
$id = Statuslabel::factory()->create([
'archived' => true,
'deployable' => false,
'pending' => false,
])->id;
return $this->state(['status_id' => $id, 'requestable' => false]);
}

public function noPurchaseOrEolDate()
Expand Down
2 changes: 1 addition & 1 deletion resources/views/partials/bootstrap-table.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ function assetRequestActionsFormatter (row, value) {
if (value.assigned_to_self == true){
return '<button class="btn btn-danger btn-sm disabled" data-tooltip="true" title="Cancel this item request">{{ trans('button.cancel') }}</button>';
} else if (value.available_actions.cancel == true) {
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>';
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>';
} else if (value.available_actions.request == true) {
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>';
}
Expand Down
3 changes: 3 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
]
)->name('api.assets.requested');

Route::post('request/{asset}', [Api\CheckoutRequest::class, 'store'])->name('api.assets.requests.store');
Route::post('request/{asset}/cancel', [Api\CheckoutRequest::class, 'destroy'])->name('api.assets.requests.destroy');

Route::get('requestable/hardware',
[
Api\AssetsController::class,
Expand Down
21 changes: 9 additions & 12 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,19 +297,16 @@
Route::get('requested', [ViewAssetsController::class, 'getRequestedAssets'])->name('account.requested');

// Profile
Route::get(
'requestable-assets',
[ViewAssetsController::class, 'getRequestableIndex']
)->name('requestable-assets');
Route::post(
'request-asset/{assetId}',
[ViewAssetsController::class, 'getRequestAsset']
)->name('account/request-asset');
Route::get('requestable-assets', [ViewAssetsController::class, 'getRequestableIndex'])->name('requestable-assets');

Route::post(
'request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}',
[ViewAssetsController::class, 'getRequestItem']
)->name('account/request-item');
Route::post('request-asset/{asset}', [ViewAssetsController::class, 'store'])
->name('account.request-asset');

Route::post('request-asset/{asset}/cancel', [ViewAssetsController::class, 'destroy'])
->name('account.request-asset.cancel');

Route::post('request/{itemType}/{itemId}/{cancel_by_admin?}/{requestingUser?}', [ViewAssetsController::class, 'getRequestItem'])
->name('account/request-item');

// Account Dashboard
Route::get('/', [ViewAssetsController::class, 'getIndex'])->name('account');
Expand Down
Loading
Loading