mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-23 12:44:12 -08:00
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:
parent
201efecafa
commit
8a6713d5c0
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
44
app/Http/Controllers/Api/ProfileController.php
Normal file
44
app/Http/Controllers/Api/ProfileController.php
Normal file
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -160,4 +160,39 @@ class AssetsTransformer
|
|||
'type' => $asset->assignedType()
|
||||
] : null;
|
||||
}
|
||||
|
||||
|
||||
public function transformRequestedAssets(Collection $assets, $total)
|
||||
{
|
||||
$array = array();
|
||||
foreach ($assets as $asset) {
|
||||
$array[] = self::transformRequestedAsset($asset);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformRequestedAsset(Asset $asset) {
|
||||
$array = [
|
||||
'id' => (int) $asset->id,
|
||||
'name' => e($asset->name),
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'serial' => e($asset->serial),
|
||||
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
|
||||
'model' => ($asset->model) ? e($asset->model->name) : null,
|
||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
||||
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
|
||||
'location' => ($asset->location) ? e($asset->location->name) : null,
|
||||
'status'=> ($asset->assetstatus) ? $asset->present()->statusMeta : null,
|
||||
];
|
||||
|
||||
$permissions_array['available_actions'] = [
|
||||
'cancel' => ($asset->isRequestedBy(\Auth::user())) ? true : false,
|
||||
'request' => ($asset->isRequestedBy(\Auth::user())) ? false : true,
|
||||
|
||||
];
|
||||
|
||||
$array += $permissions_array;
|
||||
return $array;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,8 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
|||
return [
|
||||
trans('admin/asset_maintenances/general.maintenance') => trans('admin/asset_maintenances/general.maintenance'),
|
||||
trans('admin/asset_maintenances/general.repair') => trans('admin/asset_maintenances/general.repair'),
|
||||
trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade')
|
||||
trans('admin/asset_maintenances/general.upgrade') => trans('admin/asset_maintenances/general.upgrade'),
|
||||
'PAT test' => 'PAT test',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,14 @@ class AssetModel extends SnipeModel
|
|||
return $this->belongsTo('\App\Models\CustomFieldset', 'fieldset_id');
|
||||
}
|
||||
|
||||
|
||||
public function getImageUrl() {
|
||||
if ($this->image) {
|
||||
return url('/').'/uploads/models/'.$this->image;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CheckoutRequest extends Model
|
||||
{
|
||||
//
|
||||
use SoftDeletes;
|
||||
protected $fillable = ['user_id'];
|
||||
protected $table = 'checkout_requests';
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ trait Requestable
|
|||
|
||||
public function isRequestedBy(User $user)
|
||||
{
|
||||
$requests = $this->requests->where('user_id', $user->id);
|
||||
|
||||
return $requests->count() > 0;
|
||||
return $this->requests->where('canceled_at', NULL)->where('user_id', $user->id)->first();
|
||||
}
|
||||
|
||||
public function scopeRequestedBy($query, User $user)
|
||||
|
@ -31,15 +29,20 @@ trait Requestable
|
|||
});
|
||||
}
|
||||
|
||||
public function request()
|
||||
public function request($qty = 1)
|
||||
{
|
||||
$this->requests()->save(
|
||||
new CheckoutRequest(['user_id' => Auth::id()])
|
||||
new CheckoutRequest(['user_id' => Auth::id(), 'qty' => $qty])
|
||||
);
|
||||
}
|
||||
|
||||
public function deleteRequest()
|
||||
{
|
||||
$this->requests()->where('user_id', Auth::id())->delete();
|
||||
}
|
||||
|
||||
public function cancelRequest()
|
||||
{
|
||||
$this->requests()->where('user_id', Auth::id())->delete();
|
||||
$this->requests()->where('user_id', Auth::id())->update(['canceled_at' => \Carbon\Carbon::now()]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,7 +292,7 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
|||
*/
|
||||
public function checkoutRequests()
|
||||
{
|
||||
return $this->belongsToMany(Asset::class, 'checkout_requests');
|
||||
return $this->belongsToMany(Asset::class, 'checkout_requests', 'user_id', 'requestable_id')->whereNull('canceled_at');
|
||||
}
|
||||
|
||||
public function throttle()
|
||||
|
|
137
app/Notifications/RequestAssetCancelation.php
Normal file
137
app/Notifications/RequestAssetCancelation.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class RequestAssetCancelationNotification extends Notification
|
||||
{
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @param $params
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->target = $params['target'];
|
||||
$this->item = $params['item'];
|
||||
$this->note = '';
|
||||
$this->last_checkout = '';
|
||||
$this->item_quantity = $params['item_quantity'];
|
||||
$this->expected_checkin = '';
|
||||
$this->requested_date = \App\Helpers\Helper::getFormattedDateObject($params['requested_date'], 'datetime',
|
||||
false);
|
||||
$this->settings = Setting::getSettings();
|
||||
|
||||
if (array_key_exists('note', $params)) {
|
||||
$this->note = $params['note'];
|
||||
}
|
||||
|
||||
if ($this->item->last_checkout) {
|
||||
$this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
|
||||
false);
|
||||
}
|
||||
|
||||
if ($this->item->expected_checkin) {
|
||||
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
|
||||
false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via()
|
||||
{
|
||||
|
||||
$notifyBy = [];
|
||||
|
||||
if (Setting::getSettings()->slack_endpoint!='') {
|
||||
\Log::debug('use slack');
|
||||
$notifyBy[] = 'slack';
|
||||
}
|
||||
|
||||
|
||||
$notifyBy[] = 'mail';
|
||||
|
||||
return $notifyBy;
|
||||
}
|
||||
|
||||
public function toSlack()
|
||||
{
|
||||
|
||||
|
||||
$target = $this->target;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
$qty = $this->item_quantity;
|
||||
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
|
||||
|
||||
$fields = [
|
||||
'QTY' => $qty,
|
||||
'Canceled By' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
|
||||
];
|
||||
|
||||
if (($this->expected_checkin) && ($this->expected_checkin!='')) {
|
||||
$fields['Expected Checkin'] = $this->expected_checkin;
|
||||
}
|
||||
|
||||
return (new SlackMessage)
|
||||
->content( trans('mail.a_user_canceled'))
|
||||
->from($botname)
|
||||
->attachment(function ($attachment) use ($item, $note, $fields) {
|
||||
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
|
||||
->fields($fields)
|
||||
->content($note);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail()
|
||||
{
|
||||
|
||||
$fields = [];
|
||||
|
||||
// Check if the item has custom fields associated with it
|
||||
if (($this->item->model) && ($this->item->model->fieldset)) {
|
||||
$fields = $this->item->model->fieldset->fields;
|
||||
}
|
||||
|
||||
$message = (new MailMessage)->markdown('notifications.markdown.asset-requested',
|
||||
[
|
||||
'item' => $this->item,
|
||||
'note' => $this->note,
|
||||
'requested_by' => $this->target,
|
||||
'requested_date' => $this->requested_date,
|
||||
'fields' => $fields,
|
||||
'qty' => $this->item_quantity,
|
||||
'last_checkout' => $this->last_checkout,
|
||||
'expected_checkin' => $this->expected_checkin,
|
||||
'intro_text' => trans('mail.a_user_canceled'),
|
||||
])
|
||||
->subject(trans('Item Request Canceled'));
|
||||
|
||||
|
||||
return $message;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
134
app/Notifications/RequestAssetNotification.php
Normal file
134
app/Notifications/RequestAssetNotification.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class RequestAssetNotification extends Notification
|
||||
{
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @param $params
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->target = $params['target'];
|
||||
$this->item = $params['item'];
|
||||
$this->item_type = $params['item_type'];
|
||||
$this->item_quantity = $params['item_quantity'];
|
||||
$this->note = '';
|
||||
$this->last_checkout = '';
|
||||
$this->expected_checkin = '';
|
||||
$this->requested_date = \App\Helpers\Helper::getFormattedDateObject($params['requested_date'], 'datetime',
|
||||
false);
|
||||
$this->settings = Setting::getSettings();
|
||||
|
||||
if (array_key_exists('note', $params)) {
|
||||
$this->note = $params['note'];
|
||||
}
|
||||
|
||||
if ($this->item->last_checkout) {
|
||||
$this->last_checkout = \App\Helpers\Helper::getFormattedDateObject($this->item->last_checkout, 'date',
|
||||
false);
|
||||
}
|
||||
|
||||
if ($this->item->expected_checkin) {
|
||||
$this->expected_checkin = \App\Helpers\Helper::getFormattedDateObject($this->item->expected_checkin, 'date',
|
||||
false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via()
|
||||
{
|
||||
|
||||
$notifyBy = [];
|
||||
|
||||
if (Setting::getSettings()->slack_endpoint!='') {
|
||||
\Log::debug('use slack');
|
||||
$notifyBy[] = 'slack';
|
||||
}
|
||||
|
||||
|
||||
$notifyBy[] = 'mail';
|
||||
|
||||
return $notifyBy;
|
||||
}
|
||||
|
||||
public function toSlack()
|
||||
{
|
||||
|
||||
|
||||
$target = $this->target;
|
||||
$qty = $this->item_quantity;
|
||||
$item = $this->item;
|
||||
$note = $this->note;
|
||||
$botname = ($this->settings->slack_botname) ? $this->settings->slack_botname : 'Snipe-Bot' ;
|
||||
|
||||
$fields = [
|
||||
'QTY' => $qty,
|
||||
'Requested By' => '<'.$target->present()->viewUrl().'|'.$target->present()->fullName().'>',
|
||||
];
|
||||
|
||||
return (new SlackMessage)
|
||||
->content(trans('mail.Item_Requested'))
|
||||
->from($botname)
|
||||
->attachment(function ($attachment) use ($item, $note, $fields) {
|
||||
$attachment->title(htmlspecialchars_decode($item->present()->name), $item->present()->viewUrl())
|
||||
->fields($fields)
|
||||
->content($note);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail()
|
||||
{
|
||||
|
||||
$fields = [];
|
||||
|
||||
// Check if the item has custom fields associated with it
|
||||
if (($this->item->model) && ($this->item->model->fieldset)) {
|
||||
$fields = $this->item->model->fieldset->fields;
|
||||
}
|
||||
|
||||
$message = (new MailMessage)->markdown('notifications.markdown.asset-requested',
|
||||
[
|
||||
'item' => $this->item,
|
||||
'note' => $this->note,
|
||||
'requested_by' => $this->target,
|
||||
'requested_date' => $this->requested_date,
|
||||
'fields' => $fields,
|
||||
'last_checkout' => $this->last_checkout,
|
||||
'expected_checkin' => $this->expected_checkin,
|
||||
'intro_text' => trans('mail.a_user_requested'),
|
||||
'qty' => $this->item_quantity,
|
||||
])
|
||||
->subject(trans('mail.Item_Requested'));
|
||||
|
||||
|
||||
return $message;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddCanceledAtAndFulfilledAtInRequests extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('checkout_requests', function (Blueprint $table) {
|
||||
$table->dateTime('canceled_at')->nullable()->default(null);
|
||||
$table->dateTime('fulfilled_at')->nullable()->default(null);
|
||||
$table->dateTime('deleted_at')->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('checkout_requests', function (Blueprint $table) {
|
||||
$table->dropColumn('canceled_at');
|
||||
$table->dropColumn('fulfilled_at');
|
||||
$table->dropColumn('deleted_at');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddDropUniqueRequests extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('checkout_requests', function (Blueprint $table) {
|
||||
$table->dropUnique('checkout_requests_user_id_requestable_id_requestable_type_unique');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('checkout_requests', function (Blueprint $table) {
|
||||
$table->index(['user_id','requestable_id','requestable_type'], 'checkout_requests_user_id_requestable_id_requestable_type_unique')->unique();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddNewIndexRequestable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('checkout_requests', function (Blueprint $table) {
|
||||
$table->index(['user_id','requestable_id','requestable_type'], 'checkout_requests_user_id_requestable_id_requestable_type');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('checkout_requests', function (Blueprint $table) {
|
||||
$table->dropIndex('checkout_requests_user_id_requestable_id_requestable_type');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -29,97 +29,40 @@
|
|||
<div class="tab-pane fade in active" id="assets">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
@if ($assets->count() > 0)
|
||||
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
name="requested-assets"
|
||||
data-toolbar="#toolbar"
|
||||
class="table table-striped snipe-table"
|
||||
id="table"
|
||||
data-advanced-search="true"
|
||||
data-id-table="advancedTable"
|
||||
data-cookie-id-table="requestableAssets">
|
||||
data-click-to-select="true"
|
||||
data-cookie-id-table="requestableAssetsListingTable"
|
||||
data-pagination="true"
|
||||
data-id-table="requestableAssetsListingTable"
|
||||
data-search="true"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
data-show-export="false"
|
||||
data-show-footer="false"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
data-sort-name="name"
|
||||
data-toolbar="#toolbar"
|
||||
id="assetsListingTable"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.assets.requestable', ['requestable' => true]) }}">
|
||||
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th class="col-md-1" data-sortable="true">{{ trans('general.image') }}</th>
|
||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/table.asset_model') }}</th>
|
||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/models/table.modelnumber') }}</th>
|
||||
@if ($snipeSettings->display_asset_name)
|
||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/form.name') }}</th>
|
||||
@endif
|
||||
<th class="col-md-3" data-sortable="true">{{ trans('admin/hardware/table.serial') }}</th>
|
||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/table.location') }}</th>
|
||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/table.status') }}</th>
|
||||
<th class="col-md-2" data-sortable="true">{{ trans('admin/hardware/form.expected_checkin') }}</th>
|
||||
<th class="col-md-1 actions" data-sortable="false">{{ trans('table.actions') }}</th>
|
||||
<tr>
|
||||
<th class="col-md-1" data-field="image" data-formatter="imageFormatter" data-sortable="true">{{ trans('general.image') }}</th>
|
||||
<th class="col-md-2" data-field="model" data-sortable="true">{{ trans('admin/hardware/table.asset_model') }}</th>
|
||||
<th class="col-md-2" data-field="model_number" data-sortable="true">{{ trans('admin/models/table.modelnumber') }}</th>
|
||||
<th class="col-md-2" data-field="name" data-sortable="true">{{ trans('admin/hardware/form.name') }}</th>
|
||||
<th class="col-md-3" data-field="serial" data-sortable="true">{{ trans('admin/hardware/table.serial') }}</th>
|
||||
<th class="col-md-2" data-field="location" data-sortable="true">{{ trans('admin/hardware/table.location') }}</th>
|
||||
<th class="col-md-2" data-field="status" data-sortable="true">{{ trans('admin/hardware/table.status') }}</th>
|
||||
<th class="col-md-2" data-field="expected_checkin" data-formatter="dateDisplayFormatter" data-sortable="true">{{ trans('admin/hardware/form.expected_checkin') }}</th>
|
||||
<th class="col-md-1" data-formatter="assetRequestActionsFormatter" data-field="actions" data-sortable="false">{{ trans('table.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($assets as $asset)
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
@if ($asset->getImageUrl())
|
||||
<a href="{{ $asset->getImageUrl() }}" data-toggle="lightbox" data-type="image">
|
||||
<img src="{{ $asset->getImageUrl() }}" style="max-height: {{ $snipeSettings->thumbnail_max_h }}px; width: auto;" class="img-responsive">
|
||||
</a>
|
||||
@endif
|
||||
|
||||
</td>
|
||||
<td>{{ $asset->model->name }}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{ $asset->model->model_number }}
|
||||
</td>
|
||||
|
||||
@if ($snipeSettings->display_asset_name)
|
||||
<td>{{ $asset->name }}</td>
|
||||
@endif
|
||||
|
||||
<td>{{ $asset->serial }}</td>
|
||||
|
||||
<td>
|
||||
@if ($asset->location)
|
||||
{{ $asset->location->name }}
|
||||
@endif
|
||||
</td>
|
||||
@if ($asset->assigned_to != '' && $asset->assigned_to > 0)
|
||||
<td>Checked out</td>
|
||||
@else
|
||||
<td>{{ trans('admin/hardware/general.requestable') }}</td>
|
||||
@endif
|
||||
|
||||
<td>{{ $asset->expected_checkin }}</td>
|
||||
<td>
|
||||
<form action="{{route('account/request-item', ['itemType' => 'asset', 'itemId' => $asset->id])}}" method="POST" accept-charset="utf-8">
|
||||
{{ csrf_field() }}
|
||||
@if ($asset->isRequestedBy(Auth::user()))
|
||||
{{Form::submit(trans('button.cancel'), ['class' => 'btn btn-danger btn-sm'])}}
|
||||
@else
|
||||
{{Form::submit(trans('button.request'), ['class' => 'btn btn-primary btn-sm'])}}
|
||||
@endif
|
||||
</form>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-info alert-block">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
{{ trans('general.no_results') }}
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
54
resources/views/account/requested.blade.php
Normal file
54
resources/views/account/requested.blade.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
@extends('layouts/default')
|
||||
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
Requested Assets
|
||||
@stop
|
||||
|
||||
{{-- Account page content --}}
|
||||
@section('content')
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="box box-default">
|
||||
<div class="box-body">
|
||||
|
||||
<table
|
||||
data-cookie-id-table="userRequests"
|
||||
data-pagination="true"
|
||||
data-id-table="userRequests"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="desc"
|
||||
id="userRequests"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.assets.requested') }}"
|
||||
data-export-options='{
|
||||
"fileName": "my-requested-assets-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-1" data-field="image" data-formatter="imageFormatter">Image</th>
|
||||
<th class="col-md-2" data-field="name">Item Name</th>
|
||||
<th class="col-md-2" data-field="type">Type</th>
|
||||
<th class="col-md-2" data-field="qty">{{ trans('general.qty') }}</th>
|
||||
<th class="col-md-2" data-field="location">{{ trans('admin/hardware/table.location') }}</th>
|
||||
<th class="col-md-2" data-field="expected_checkin" data-formatter="dateDisplayFormatter"> {{ trans('admin/hardware/form.expected_checkin') }}</th>
|
||||
<th class="col-md-2" data-field="request_date" data-formatter="dateDisplayFormatter">Requested Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
</div> <!-- .box-body -->
|
||||
</div> <!-- .box-default -->
|
||||
</div> <!-- .col-md-9 -->
|
||||
</div> <!-- .row-->
|
||||
|
||||
@stop
|
||||
@section('moar_scripts')
|
||||
@include ('partials.bootstrap-table')
|
||||
@stop
|
|
@ -266,7 +266,7 @@ View Assets for {{ $user->present()->fullName() }}
|
|||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
data-sort-order="desc"
|
||||
id="userActivityReport"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{route('api.activity.index', ['target_id' => $user->id, 'target_type' => 'User', 'order' => 'desc']) }}"
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
@extends('emails/layouts/default')
|
||||
|
||||
@section('content')
|
||||
|
||||
<p>{{ trans('mail.a_user_canceled') }} <a href="{{ url('/') }}"> {{ $snipeSettings->site_name }}</a>. </p>
|
||||
|
||||
<p>{{ trans('mail.user') }} <a href="{{ route('users.show', $user_id) }}">{{ $requested_by }}</a><br>
|
||||
{{ trans('mail.item') }} <a href="{{ $item_url }}">{{ $item_name }}</a> ({{ $item_type }}) <br>
|
||||
{{ trans('mail.canceled') }} {{ $requested_date }}
|
||||
</p>
|
||||
|
||||
@if ($snipeSettings->show_url_in_emails=='1')
|
||||
<p><a href="{{ url('/') }}">{{ $snipeSettings->site_name }}</a></p>
|
||||
@else
|
||||
<p>{{ $snipeSettings->site_name }}</p>
|
||||
@endif
|
||||
@stop
|
|
@ -1,20 +0,0 @@
|
|||
@extends('emails/layouts/default')
|
||||
|
||||
@section('content')
|
||||
|
||||
<p>{{ trans('mail.a_user_requested') }} <a href="{{ url('/') }}"> {{ $snipeSettings->site_name }}</a>. </p>
|
||||
|
||||
<p>{{ trans('mail.user') }} <a href="{{ route('users.show', $user_id) }}">{{ $requested_by }}</a><br>
|
||||
{{ trans('mail.item') }} <a href="{{ $item_url }}">{{ $item_name }}</a> ({{ $item_type }}) <br>
|
||||
{{ trans('general.requested') }} {{ $requested_date }}
|
||||
@if ($item_quantity > 1)
|
||||
<br> {{ trans('general.qty') }} {{ $item_quantity}}
|
||||
@endif
|
||||
|
||||
@if ($snipeSettings->show_url_in_emails=='1')
|
||||
<p><a href="{{ url('/') }}">{{ $snipeSettings->site_name }}</a></p>
|
||||
@else
|
||||
<p>{{ $snipeSettings->site_name }}</p>
|
||||
@endif
|
||||
|
||||
@stop
|
|
@ -28,13 +28,22 @@
|
|||
@if ($requestedItems->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
name="requested-assets"
|
||||
name="requestedAssets"
|
||||
data-toolbar="#toolbar"
|
||||
class="table table-striped snipe-table"
|
||||
id="table"
|
||||
id="requestedAssets"
|
||||
data-advanced-search="true"
|
||||
data-id-table="advancedTable"
|
||||
data-cookie-id-table="requestedAssets">
|
||||
data-search="true"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-pagination="true"
|
||||
data-id-table="requestedAssets"
|
||||
data-cookie-id-table="requestedAssets"
|
||||
data-url="{{ route('api.consumables.index') }}"
|
||||
data-export-options='{
|
||||
"fileName": "export-assetrequests-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'>
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th class="col-md-1">Image</th>
|
||||
|
@ -44,6 +53,7 @@
|
|||
<th class="col-md-3" data-sortable="true">Requesting User</th>
|
||||
<th class="col-md-2">Requested Date</th>
|
||||
<th class="col-md-1"></th>
|
||||
<th class="col-md-1"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -307,6 +307,16 @@
|
|||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ trans('general.viewassets') }}
|
||||
</a></li>
|
||||
|
||||
<li {!! (Request::is('account/requested') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ route('account.requested') }}">
|
||||
<i class="fa fa-check fa-disk"></i>
|
||||
Requested Assets
|
||||
</a></li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a href="{{ route('profile') }}">
|
||||
<i class="fa fa-user fa-fw"></i>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
@component('mail::message')
|
||||
# {{ trans('mail.hello') }},
|
||||
|
||||
{{ $intro_text }}.
|
||||
|
||||
@if (($snipeSettings->show_images_in_email =='1') && $item->getImageUrl())
|
||||
<center><img src="{{ $item->getImageUrl() }}" alt="Asset" style="max-width: 570px;"></center>
|
||||
@endif
|
||||
|
||||
@component('mail::table')
|
||||
| | |
|
||||
| ------------- | ------------- |
|
||||
@if (isset($qty))
|
||||
| **{{ trans('general.qty') }}** | {{ $qty }}
|
||||
@endif
|
||||
| **{{ trans('mail.user') }}** | [{{ $requested_by->present()->fullName() }}]({{ route('users.show', $requested_by->id) }}) |
|
||||
| **{{ trans('general.requested') }}** | {{ $requested_date }} |
|
||||
@if ((isset($item->asset_tag)) && ($item->asset_tag!=''))
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} |
|
||||
@endif
|
||||
@if ((isset($item->name)) && ($item->name!=''))
|
||||
| **{{ trans('mail.asset_name') }}** | {{ $item->name }} |
|
||||
@endif
|
||||
@if (isset($item->assetstatus))
|
||||
| **{{ trans('general.status') }}** | {{ $item->assetstatus->name }}
|
||||
@endif
|
||||
@if ($item->assignedTo)
|
||||
| **Checked out to** | {!! $item->assignedTo->present()->nameUrl() !!} ({{ $item->present()->statusMeta }})
|
||||
@endif
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
@endif
|
||||
@if (isset($item->model))
|
||||
| **{{ trans('general.asset_model') }}** | {{ $item->model->name }} |
|
||||
@endif
|
||||
@if (isset($item->model_no))
|
||||
| **{{ trans('general.model_no') }}** | {{ $item->model_no }} |
|
||||
@endif
|
||||
@if (isset($item->serial))
|
||||
| **{{ trans('mail.serial') }}** | {{ $item->serial }} |
|
||||
@endif
|
||||
@if ((isset($last_checkout)) && ($last_checkout!=''))
|
||||
| **Last Checkout** | {{ $last_checkout }} |
|
||||
@endif
|
||||
@if ((isset($expected_checkin)) && ($expected_checkin!=''))
|
||||
| **{{ trans('mail.expecting_checkin_date') }}** | {{ $expected_checkin }} |
|
||||
@endif
|
||||
@foreach($fields as $field)
|
||||
@if (($item->{ $field->db_column_name() }!='') && ($field->show_in_email) && ($field->field_encrypted=='0'))
|
||||
| **{{ $field->name }}** | {{ $item->{ $field->db_column_name() } }} |
|
||||
@endif
|
||||
@endforeach
|
||||
@if ($note)
|
||||
| **{{ trans('mail.additional_notes') }}** | {{ $note }} |
|
||||
@endif
|
||||
@endcomponent
|
||||
|
||||
Thanks,
|
||||
|
||||
{{ $snipeSettings->site_name }}
|
||||
|
||||
@endcomponent
|
|
@ -10,7 +10,9 @@
|
|||
@component('mail::table')
|
||||
| | |
|
||||
| ------------- | ------------- |
|
||||
@if ((isset($item->name)) && ($item->name!=''))
|
||||
| **{{ trans('mail.asset_name') }}** | {{ $item->name }} |
|
||||
@endif
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} |
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
@component('mail::table')
|
||||
| | |
|
||||
| ------------- | ------------- |
|
||||
@if ((isset($item->name)) && ($item->name!=''))
|
||||
| **{{ trans('mail.asset_name') }}** | {{ $item->name }} |
|
||||
@endif
|
||||
| **{{ trans('mail.asset_tag') }}** | {{ $item->asset_tag }} |
|
||||
@if (isset($item->manufacturer))
|
||||
| **{{ trans('general.manufacturer') }}** | {{ $item->manufacturer->name }} |
|
||||
|
|
|
@ -301,6 +301,17 @@
|
|||
}
|
||||
|
||||
|
||||
// This is only used by the requestable assets section
|
||||
function assetRequestActionsFormatter (row, value) {
|
||||
if (value.available_actions.cancel == true) {
|
||||
return '<form action="{{ url('/') }}/account/request-asset/'+ value.id + '" method="GET"><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="{{ url('/') }}/account/request-asset/'+ value.id + '" method="GET"><button class="btn btn-primary btn-sm" data-tooltip="true" title="Request this item">{{ trans('button.request') }}</button></form>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var formatters = [
|
||||
'hardware',
|
||||
|
|
|
@ -16,8 +16,24 @@ use Illuminate\Http\Request;
|
|||
|
||||
Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
|
||||
|
||||
/*--- Accessories API ---*/
|
||||
Route::group(['prefix' => 'account'], function () {
|
||||
Route::get('requestable/hardware',
|
||||
[
|
||||
'as' => 'api.assets.requestable',
|
||||
'uses' => 'AssetsController@requestable'
|
||||
]
|
||||
);
|
||||
|
||||
Route::get('requests',
|
||||
[
|
||||
'as' => 'api.assets.requested',
|
||||
'uses' => 'ProfileController@requestedAssets'
|
||||
]
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
/*--- Accessories API ---*/
|
||||
Route::resource('accessories', 'AccessoriesController',
|
||||
['names' =>
|
||||
[
|
||||
|
@ -716,4 +732,5 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
|
|||
);
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
|
|
@ -249,6 +249,8 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['auth']], function () {
|
|||
# View Assets
|
||||
Route::get('view-assets', [ 'as' => 'view-assets', 'uses' => 'ViewAssetsController@getIndex' ]);
|
||||
|
||||
Route::get('requested', [ 'as' => 'account.requested', 'uses' => 'ViewAssetsController@getRequestedAssets' ]);
|
||||
|
||||
# Accept Asset
|
||||
Route::get(
|
||||
'accept-asset/{logID}',
|
||||
|
|
Loading…
Reference in a new issue