WIP - Improved requested assets (#5289)
* WIP - beginning of improved requested assets - Use Ajax tables for faster loading - Use new notifications for requesting an asset TODO: - Use ajax tables for requestable asset models - Use new notifications for canceling an asset request - Expire requests once the asset has been checked out to the requesting user * Only show asset name in email if it has one * Refactor requested method to only include non-canceled requests * Refactored requestable assets to log request and cancelation * Added softdeletes on checkout requests * Differentiate between canceling and deleting requests * Added asset request cancelation notification * Added timestamps and corrected unique key on requests table * Improved requests view * Re-use blade for cancel/request email * Refactored BS table formatter for requested assets * Location name min reduced to 2 * Added PAT test as maintenance option This needs to be refactored into database-driven options with a UI * Better slack message * Added getImageUrl method for assets * Include qty in request notifications TODO: - Try to pull requested info from original request for cancelation, otherwise it will default to 1 * Removed old asset request/cancel emails * Added user profile asset request routes * Added profile controller requested assets method * Added blade link to requested assets for profile view * Sort user history desc * Added requested assets blade * Added canceled at to checkoutRequest method * Include qty in request * Fixed comment, removed allowed_columns * Removed Queable methods, since we don’t use a queue * Fixed return type in method doc * Fixed version number * Changed id to user_id for clarity
This commit is contained in:
@@ -95,10 +95,6 @@ class AssetsController extends Controller
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// These are used by the API to query against specific ID numbers.
|
||||
// They are also used by the individual searches on detail pages like
|
||||
// locations, etc.
|
||||
@@ -106,6 +102,10 @@ class AssetsController extends Controller
|
||||
$assets->where('assets.status_id', '=', $request->input('status_id'));
|
||||
}
|
||||
|
||||
if ($request->input('requestable')=='true') {
|
||||
$assets->where('assets.requestable', '=', '1');
|
||||
}
|
||||
|
||||
if ($request->has('model_id')) {
|
||||
$assets->InModelList([$request->input('model_id')]);
|
||||
}
|
||||
@@ -736,5 +736,50 @@ class AssetsController extends Controller
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON listing of all requestable assets
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function requestable(Request $request)
|
||||
{
|
||||
|
||||
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
|
||||
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
|
||||
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->where('assets.requestable', '=', '1');
|
||||
|
||||
$offset = request('offset', 0);
|
||||
$limit = $request->input('limit', 50);
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
$assets->TextSearch($request->input('search'));
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'model':
|
||||
$assets->OrderModels($order);
|
||||
break;
|
||||
case 'model_number':
|
||||
$assets->OrderModelNumber($order);
|
||||
break;
|
||||
case 'category':
|
||||
$assets->OrderCategory($order);
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$assets->OrderManufacturer($order);
|
||||
break;
|
||||
default:
|
||||
$assets->orderBy('assets.created_at', $order);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Auth;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of requested assets.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.3.0]
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function requestedAssets()
|
||||
{
|
||||
$checkoutRequests = CheckoutRequest::where('user_id', '=', Auth::user()->id)->get();
|
||||
|
||||
$results = [];
|
||||
$results['total'] = $checkoutRequests->count();
|
||||
|
||||
|
||||
foreach ($checkoutRequests as $checkoutRequest) {
|
||||
$results['rows'][] = [
|
||||
'image' => $checkoutRequest->itemRequested()->present()->getImageUrl(),
|
||||
'name' => $checkoutRequest->itemRequested()->present()->name(),
|
||||
'type' => $checkoutRequest->itemType(),
|
||||
'qty' => $checkoutRequest->quantity,
|
||||
'location' => ($checkoutRequest->location()) ? $checkoutRequest->location()->name : null,
|
||||
'expected_checkin' => Helper::getFormattedDateObject($checkoutRequest->itemRequested()->expected_checkin, 'datetime'),
|
||||
'request_date' => Helper::getFormattedDateObject($checkoutRequest->created_at, 'datetime'),
|
||||
];
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -134,12 +134,6 @@ class SettingsController extends Controller
|
||||
if (!config('app.lock_passwords')) {
|
||||
try {
|
||||
Notification::send(Setting::first(), new MailTest());
|
||||
|
||||
/*Mail::send('emails.test', [], function ($m) {
|
||||
$m->to(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.test_email'));
|
||||
});*/
|
||||
return response()->json(['message' => 'Mail sent to '.config('mail.reply_to.address')], 200);
|
||||
} catch (Exception $e) {
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
|
||||
@@ -1305,12 +1305,16 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function getRequestedIndex($id = null)
|
||||
public function getRequestedIndex($user_id = null)
|
||||
{
|
||||
if ($id) {
|
||||
$requestedItems = CheckoutRequest::where('user_id', $id)->with('user', 'requestedItem')->get();
|
||||
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->whereNull('canceled_at')->with('user', 'requestedItem');
|
||||
|
||||
if ($user_id) {
|
||||
$requestedItems->where('user_id', $user_id)->get();
|
||||
}
|
||||
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->get();
|
||||
|
||||
$requestedItems = $requestedItems->orderBy('created_at', 'desc')->get();
|
||||
|
||||
return view('hardware/requested', compact('requestedItems'));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\RequestAssetNotification;
|
||||
use App\Notifications\RequestAssetCancelationNotification;
|
||||
use Auth;
|
||||
use Config;
|
||||
use DB;
|
||||
@@ -80,116 +82,71 @@ class ViewAssetsController extends Controller
|
||||
{
|
||||
$item = null;
|
||||
$fullItemType = 'App\\Models\\' . studly_case($itemType);
|
||||
|
||||
if ($itemType == "asset_model") {
|
||||
$itemType = "model";
|
||||
}
|
||||
$item = call_user_func(array($fullItemType, 'find'), $itemId);
|
||||
|
||||
$user = Auth::user();
|
||||
$quantity = $data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
|
||||
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $item->id;
|
||||
$logaction->item_type = $fullItemType;
|
||||
$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::user()->id;
|
||||
$logaction->target_type = User::class;
|
||||
|
||||
$data['item_quantity'] = Input::has('request-quantity') ? e(Input::get('request-quantity')) : 1;
|
||||
$data['requested_by'] = $user->present()->fullName();
|
||||
$data['item_name'] = $item->name;
|
||||
$data['item'] = $item;
|
||||
$data['item_type'] = $itemType;
|
||||
$data['target'] = Auth::user();
|
||||
|
||||
|
||||
if ($fullItemType == Asset::class) {
|
||||
$data['item_url'] = route('hardware.show', $item->id);
|
||||
$slackMessage = ' Asset <'.url('/').'/hardware/'.$item->id.'/view'.'|'.$item->present()->name().'> requested by <'.url('/').'/users/'.$item->user_id.'/view'.'|'.$user->present()->fullName().'>.';
|
||||
} else {
|
||||
$data['item_url'] = route("view/${itemType}", $item->id);
|
||||
$slackMessage = $quantity. ' ' . class_basename(strtoupper($logaction->item_type)).' <'.$data['item_url'].'|'.$item->name.'> requested by <'.url('/').'/user/'.$item->id.'/view'.'|'.$user->present()->fullName().'>.';
|
||||
|
||||
}
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$slackClient = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
}
|
||||
|
||||
if ($item->isRequestedBy($user)) {
|
||||
|
||||
$item->cancelRequest();
|
||||
$log = $logaction->logaction('request_canceled');
|
||||
if ($item_request = $item->isRequestedBy($user)) {
|
||||
$item->cancelRequest();
|
||||
$data['item_quantity'] = $item_request->qty;
|
||||
$logaction->logaction('request_canceled');
|
||||
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.asset-canceled', $data, function ($m) use ($user, $settings) {
|
||||
$m->to(explode(',', $settings->alert_email), $settings->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Item_Request_Canceled'));
|
||||
});
|
||||
}
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
try {
|
||||
$slackClient->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'CANCELED:',
|
||||
'value' => $slackMessage
|
||||
]
|
||||
|
||||
]
|
||||
])->send('Item Request Canceled');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
$settings->notify(new RequestAssetCancelationNotification($data));
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
||||
|
||||
} else {
|
||||
$item->request();
|
||||
|
||||
$log = $logaction->logaction('requested');
|
||||
|
||||
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
|
||||
$m->to(explode(',', $settings->alert_email), $settings->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.Item_Requested'));
|
||||
});
|
||||
$logaction->logaction('requested');
|
||||
$settings->notify(new RequestAssetNotification($data));
|
||||
}
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
try {
|
||||
$slackClient->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'REQUESTED:',
|
||||
'value' => $slackMessage
|
||||
]
|
||||
|
||||
]
|
||||
])->send('Item Requested');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function getRequestAsset($assetId = null)
|
||||
{
|
||||
|
||||
@@ -197,74 +154,45 @@ class ViewAssetsController extends Controller
|
||||
|
||||
// Check if the asset exists and is requestable
|
||||
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
|
||||
// Redirect to the asset management page
|
||||
return redirect()->route('requestable-assets')->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
|
||||
} elseif (!Company::isCurrentUserHasAccess($asset)) {
|
||||
return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions'));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
// If it's requested, cancel the request.
|
||||
|
||||
$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::user()->id;
|
||||
$logaction->target_type = User::class;
|
||||
|
||||
|
||||
// If it's already requested, cancel the request.
|
||||
if ($asset->isRequestedBy(Auth::user())) {
|
||||
$asset->cancelRequest();
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
$logaction->logaction('request canceled');
|
||||
$settings->notify(new RequestAssetCancelationNotification($data));
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('success')->with('success', trans('admin/hardware/message.requests.cancel-success'));
|
||||
} else {
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $asset->id;
|
||||
$logaction->item_type = Asset::class;
|
||||
$logaction->created_at = $data['requested_date'] = date("Y-m-d H:i:s");
|
||||
$data['asset_type'] = 'hardware';
|
||||
if ($user->location_id) {
|
||||
$logaction->location_id = $user->location_id;
|
||||
}
|
||||
$logaction->target_id = $data['user_id'] = Auth::user()->id;
|
||||
$logaction->target_type = User::class;
|
||||
$log = $logaction->logaction('requested');
|
||||
|
||||
$data['requested_by'] = $user->present()->fullName();
|
||||
$data['asset_name'] = $asset->present()->name();
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (($settings->alert_email!='') && ($settings->alerts_enabled=='1') && (!config('app.lock_passwords'))) {
|
||||
Mail::send('emails.asset-requested', $data, function ($m) use ($user, $settings) {
|
||||
$m->to(explode(',', $settings->alert_email), $settings->site_name);
|
||||
$m->replyTo(config('mail.reply_to.address'), config('mail.reply_to.name'));
|
||||
$m->subject(trans('mail.asset_requested'));
|
||||
});
|
||||
}
|
||||
$logaction->logaction('requested');
|
||||
|
||||
$asset->request();
|
||||
$settings->notify(new RequestAssetNotification($data));
|
||||
|
||||
|
||||
if ($settings->slack_endpoint) {
|
||||
|
||||
|
||||
$slack_settings = [
|
||||
'username' => $settings->botname,
|
||||
'channel' => $settings->slack_channel,
|
||||
'link_names' => true
|
||||
];
|
||||
|
||||
$client = new \Maknz\Slack\Client($settings->slack_endpoint, $slack_settings);
|
||||
|
||||
try {
|
||||
$client->attach([
|
||||
'color' => 'good',
|
||||
'fields' => [
|
||||
[
|
||||
'title' => 'REQUESTED:',
|
||||
'value' => class_basename(strtoupper($logaction->item_type)).' asset <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.$asset->present()->name().'> requested by <'.url('/').'/hardware/'.$asset->id.'/view'.'|'.Auth::user()->present()->fullName().'>.'
|
||||
]
|
||||
|
||||
]
|
||||
])->send('Asset Requested');
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
}
|
||||
|
||||
@@ -273,13 +201,10 @@ class ViewAssetsController extends Controller
|
||||
|
||||
public function getRequestedAssets()
|
||||
{
|
||||
$checkoutrequests = CheckoutRequest::all();
|
||||
|
||||
return view('account/requested-items', compact($checkoutrequests));
|
||||
return view('account/requested');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Get the acceptance screen
|
||||
public function getAcceptAsset($logID = null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user