Checkout Improvements, and initial support for requesting an asset model (#2573)

* Create a new action_log table to replace asset_log.  Use Polymorphism to generalize class and targets.  Port everything I can find to use it.  Add a migration to port the asset_logs table to action_logs.

* Initial work on requestable asset models

* Backend work for polymorphic requests table to store checkout requests.

* Add missing files

* Add a record to the db when requesting items.  Build up a testing route for interfacing with this.

* Users can now toggle requests of items on the request page.  Reformat page to use the same tab layout we use elsewhere

* Polymorphic request function.  Implement requesting of asset models.  Need to port mail/slack to notifications still.

* Implement requesting of asset models.  Build up emails and notifications to support it.  Allow specifying a quantity of model to request.

* Add view to show currently requested assets.  Needs some work and cleanup, but it isn't accessible from anywhere yet.
This commit is contained in:
Daniel Meltzer 2016-09-15 21:58:27 -05:00 committed by snipe
parent b0a8a0427d
commit 2d8269ddcd
26 changed files with 631 additions and 103 deletions

View file

@ -99,12 +99,12 @@ class AssetModelsController extends Controller
$model->category_id = e(Input::get('category_id'));
$model->note = e(Input::get('note'));
$model->user_id = Auth::user()->id;
$model->requestable = Input::has('requestable');
if (Input::get('custom_fieldset')!='') {
$model->fieldset_id = e(Input::get('custom_fieldset'));
}
//$model->show_mac_address = e(Input::get('show_mac_address', '0'));
if (Input::file('image')) {
$image = Input::file('image');
@ -228,6 +228,9 @@ class AssetModelsController extends Controller
$model->manufacturer_id = e(Input::get('manufacturer_id'));
$model->category_id = e(Input::get('category_id'));
$model->note = e(Input::get('note'));
$model->requestable = Input::has('requestable');
if (Input::get('custom_fieldset')=='') {
$model->fieldset_id = null;
} else {

View file

@ -1670,10 +1670,6 @@ class AssetsController extends Controller
case 'Deployed':
$assets->Deployed();
break;
default:
$assets->NotArchived();
break;
}
if ($request->has('status_id')) {

View file

@ -336,7 +336,7 @@ class ConsumablesController extends Controller
'fields' => [
[
'title' => 'Checked Out:',
'value' => strtoupper($logaction->item_type).' <'.config('app.url').'/admin/consumables/'.$consumable->id.'/view'.'|'.$consumable->name.'> checked out to <'.config('app.url').'/admin/users/'.$user->id.'/view|'.$user->fullName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
'value' => 'Consumable <'.config('app.url').'/admin/consumables/'.$consumable->id.'/view'.'|'.$consumable->name.'> checked out to <'.config('app.url').'/admin/users/'.$user->id.'/view|'.$user->fullName().'> by <'.config('app.url').'/admin/users/'.$admin_user->id.'/view'.'|'.$admin_user->fullName().'>.'
],
[
'title' => 'Note:',

View file

@ -4,12 +4,14 @@ namespace App\Http\Controllers;
use App\Models\Accessory;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\CheckoutRequest;
use App\Models\Company;
use App\Models\Consumable;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\License;
use App\Models\Setting;
use App\Models\User;
use App\Models\License;
use Auth;
use Config;
use DB;
@ -39,9 +41,7 @@ class ViewAssetsController extends Controller
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find(Auth::user()->id);
$userlog = $user->userlog->load('item', 'item.model', 'user', 'target');
$userlog = $user->userlog->load('item', 'user', 'target');
if (isset($user->id)) {
return View::make('account/view-assets', compact('user', 'userlog'));
@ -60,11 +60,130 @@ class ViewAssetsController extends Controller
{
$assets = Asset::with('model', 'defaultLoc', 'assetloc', 'assigneduser')->Hardware()->RequestableAssets()->get();
$models = AssetModel::with('category')->RequestableModels()->get();
return View::make('account/requestable-assets', compact('user', 'assets'));
return View::make('account/requestable-assets', compact('user', 'assets', 'models'));
}
public function getRequestedIndex()
{
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->get();
return View::make('admin/requested-assets', compact('requestedItems'));
}
public function getRequestItem($itemType, $itemId = null)
{
$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['requested_by'] = $user->fullName();
$data['item_name'] = $item->name;
$data['item_type'] = $itemType;
if ($fullItemType == Asset::class) {
$data['item_url'] = route('view/hardware', $item->id);
$slackMessage = ' Asset <'.config('app.url').'/hardware/'.$item->id.'/view'.'|'.$item->showAssetName().'> requested by <'.config('app.url').'/users/'.$item->user_id.'/view'.'|'.$user->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 <'.config('app.url').'/user/'.$item->id.'/view'.'|'.$user->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 (($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->subject('Item Request Canceled');
});
}
if ($settings->slack_endpoint) {
try {
$slackClient->attach([
'color' => 'good',
'fields' => [
[
'title' => 'CANCELED:',
'value' => $slackMessage
]
]
])->send('Item Request Canceled');
} catch (Exception $e) {
}
}
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->subject('Item Requested');
});
}
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)
{
@ -76,6 +195,11 @@ class ViewAssetsController extends Controller
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'));
}
// If it's 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'));
} else {
$logaction = new Actionlog();
@ -102,6 +226,8 @@ class ViewAssetsController extends Controller
});
}
$asset->request();
if ($settings->slack_endpoint) {
@ -138,6 +264,13 @@ class ViewAssetsController extends Controller
}
public function getRequestedAssets()
{
$checkoutrequests = CheckoutRequest::all();
return View::make('account/requested-items', compact($checkoutrequests));
}
// Get the acceptance screen

View file

@ -1,6 +1,7 @@
<?php
use App\Models\Statuslabel;
use App\Models\CheckoutRequest;
use App\Models\Location;
use App\Models\Statuslabel;
/*
|--------------------------------------------------------------------------
@ -388,6 +389,14 @@ Route::group(
Route::group([ 'prefix' => 'admin','middleware' => ['web','auth']], function () {
Route::get('requests',
// foreach( CheckoutRequest::with('user')->get() as $requestedItem) {
// echo $requestedItem->user->username . ' requested ' . $requestedItem->requestedItem->name;
[
'as' => 'requests',
'middleware' => 'authorize:admin',
'uses' => 'ViewAssetsController@getRequestedIndex'
]);
# Licenses
Route::group([ 'prefix' => 'licenses', 'middleware'=>'authorize:licenses.view' ], function () {
@ -858,6 +867,11 @@ Route::group([ 'prefix' => 'account', 'middleware' => ['web', 'auth']], function
[ 'as' => 'account/request-asset', 'uses' => 'ViewAssetsController@getRequestAsset' ]
);
Route::post(
'request/{itemType}/{itemId}',
[ 'as' => 'account/request-item', 'uses' => 'ViewAssetsController@getRequestItem']
);
# Account Dashboard
Route::get('/', [ 'as' => 'account', 'uses' => 'ViewAssetsController@getIndex' ]);

View file

@ -37,6 +37,9 @@ class Actionlog extends Model implements ICompanyableChild
public function itemType()
{
// dd($this);
if($this->item_type == AssetModel::class) {
return "model";
}
return camel_case(class_basename($this->item_type));
}
@ -50,8 +53,6 @@ class Actionlog extends Model implements ICompanyableChild
public function userlog()
{
// return $this->belongsTo(User::class, 'target_id')
return $this->target();
}
@ -68,13 +69,11 @@ class Actionlog extends Model implements ICompanyableChild
public function childlogs()
{
return $this->hasMany('\App\Models\ActionLog', 'thread_id');
}
public function parentlog()
{
return $this->belongsTo('\App\Models\ActionLog', 'thread_id');
}

View file

@ -7,6 +7,7 @@ use App\Models\Actionlog;
use App\Models\Company;
use App\Models\Location;
use App\Models\Loggable;
use App\Models\Requestable;
use App\Models\Setting;
use Auth;
use Config;
@ -26,6 +27,7 @@ class Asset extends Depreciable
{
use Loggable;
use SoftDeletes;
use Requestable;
/**
* The database table used by the model.

View file

@ -1,6 +1,7 @@
<?php
namespace App\Models;
use App\Models\Requestable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
@ -14,6 +15,7 @@ use Watson\Validating\ValidatingTrait;
class AssetModel extends Model
{
use SoftDeletes;
use Requestable;
protected $dates = ['deleted_at'];
protected $table = 'models';
@ -131,6 +133,22 @@ class AssetModel extends Model
return $query->whereIn('category_id', $categoryIdListing);
}
/**
* scopeRequestable
* Get all models that are requestable by a user.
*
* @param $query
*
* @return $query
* @author Daniel Meltzer <parallelgrapefruit@gmail.com
* @version v3.5
*/
public function scopeRequestableModels($query)
{
return $query->where('requestable', '1');
}
/**
* Query builder scope to search on text
*

View file

@ -0,0 +1,59 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class CheckoutRequest extends Model
{
//
protected $fillable = ['user_id'];
protected $table = 'checkout_requests';
public function user()
{
return $this->belongsTo(User::class);
}
public function requestingUser()
{
return $this->user()->first();
}
public function requestedItem()
{
return $this->morphTo('requestable');
}
public function itemRequested() // Workaround for laravel polymorphic issue that's not being solved :(
{
return $this->requestedItem()->first();
}
public function itemType()
{
return snake_case(class_basename($this->requestable_type));
}
public function location()
{
if ($this->itemType() == "asset") {
$asset = $this->itemRequested();
if ($asset->assigneduser && $asset->assetloc) {
return $asset->assetloc;
} elseif ($asset->defaultLoc) {
return $asset->defaultLoc;
}
}
return $this->itemRequested()->location;
}
public function name()
{
if ($this->itemType() == "asset") {
return $this->itemRequested()->showAssetName();
}
return $this->itemRequested()->name;
}
}

View file

@ -80,5 +80,4 @@ trait Loggable
return $log;
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Models;
use App\Models\CheckoutRequest;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
// $asset->requests
// $asset->isRequestedBy($user)
// $asset->whereRequestedBy($user)
trait Requestable
{
public function requests()
{
return $this->morphMany(CheckoutRequest::class, 'requestable');
}
public function isRequestedBy(User $user)
{
return $this->requests()
->where('user_id', $user->id)
->exists();
}
public function scopeRequestedBy($query, User $user)
{
return $query->whereHas('requests', function ($query) use ($user) {
$query->where('user_id', $user->id);
});
}
public function request()
{
$this->requests()->save(
new CheckoutRequest(['user_id' => Auth::id()])
);
}
public function cancelRequest()
{
$this->requests()->where('user_id', Auth::id())->delete();
}
}

View file

@ -261,6 +261,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
->orderBy('created_at', 'desc');
}
/**
* Fetch Items User has requested
*/
public function checkoutRequests()
{
return $this->belongsToMany(Asset::class, 'checkout_requests');
}
public function throttle()
{
return $this->hasOne('\App\Models\Throttle');

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddRequestableToAssetModel extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
Schema::table('models', function ($table) {
$table->tinyInteger('requestable')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::table('models', function ($table) {
$table->dropColumn('requestable');
});
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCheckoutRequestsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('checkout_requests', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('requestable_id');
$table->string('requestable_type');
$table->integer('quantity')->default(1);
$table->timestamps();
$table->unique(['user_id', 'requestable_id', 'requestable_type']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('checkout_requests');
}
}

View file

@ -13,6 +13,7 @@ return array(
'filetype_info' => 'Allowed filetypes are png, gif, jpg, jpeg, doc, docx, pdf, txt, zip, and rar.',
'model_deleted' => 'This Assets model has been deleted. You must restore the model before you can restore the Asset.<br/> <a href="/hardware/models/:model_id/restore">Click here to restore the model</a>.',
'requestable' => 'Requestable',
'requested' => 'Requested',
'restore' => 'Restore Asset',
'pending' => 'Pending',
'undeployable' => 'Undeployable',

View file

@ -69,6 +69,7 @@ return array(
'requests' => array(
'error' => 'Asset was not requested, please try again',
'success' => 'Asset requested successfully.',
'canceled' => 'Checkout request successfully canceled'
)
);

View file

@ -4,6 +4,7 @@ return array(
'deleted' => 'This model has been deleted. <a href="/hardware/models/:model_id/restore">Click here to restore it</a>.',
'restore' => 'Restore Model',
'requestable' => 'Users may request this model',
'show_mac_address' => 'Show MAC address field in assets in this model',
'view_deleted' => 'View Deleted',
'view_models' => 'View Models',

View file

@ -127,6 +127,7 @@
'remove_company' => 'Remove Company Association',
'reports' => 'Reports',
'requested' => 'Requested',
'request_canceled' => 'Request Canceled',
'save' => 'Save',
'select' => 'Select',
'search' => 'Search',

View file

@ -13,18 +13,25 @@
{{-- Page content --}}
@section('content')
<div class="row">
<div class="col-md-12">
<div class="box box-default">
<div class="box-body">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active">
<a href="#assets" data-toggle="tab" title="{{ trans('general.assets') }}">{{ trans('general.assets') }}</a>
</li>
<li>
<a href="#models" data-toggle="tab" title="{{ trans('general.asset_models') }}">{{ trans('general.asset_models') }}</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="assets">
@if ($assets->count() > 0)
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr role="row">
<th class="col-md-3" bSortable="true">{{ trans('admin/hardware/table.asset_model') }}</th>
@ -36,9 +43,11 @@
</tr>
</thead>
<tbody>
@foreach ($assets as $asset)
<tr>
<form action="{{route('account/request-item', ['itemType' => 'asset', 'itemId' => $asset->id])}}" method="POST" accept-charset="utf-8">
{{ csrf_field() }}
<td>{{ $asset->model->name }}</td>
@if (\App\Models\Setting::getSettings()->display_asset_name)
@ -47,8 +56,6 @@
<td>{{ $asset->serial }}</td>
<td>
@if ($asset->assigneduser && $asset->assetloc)
{{ $asset->assetloc->name }}
@ -64,14 +71,70 @@
<td>{{ trans('admin/hardware/general.requestable') }}</td>
@endif
<td>{{ $asset->expected_checkin }}</td>
<td>
<a href="{{ route('account/request-asset', $asset->id) }}" class="btn btn-info btn-sm" title="{{ trans('button.request') }}">{{ trans('button.request') }}</a>
@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
</td>
</form>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="col-md-12">
<div class="alert alert-info alert-block">
<i class="fa fa-info-circle"></i>
{{ trans('general.no_results') }}
</div>
</div>
@endif
</div>
<div class="tab-pane fade" id="models">
@if ($models->count() > 0)
<h4>Requestable Models</h4>
<table class="table table-striped">
<thead>
<tr role="row">
<th class="col-md-6" bSortable="true">{{ trans('admin/hardware/table.asset_model') }}</th>
<th class="col-md-3" bSortable="true">{{ trans('admin/accessories/general.remaining') }}</th>
<th class="col-md-2" bSortable="true">{{ trans('general.quantity') }}</th>
<th class="col-md-1 actions" bSortable="false">{{ trans('table.actions') }}</th>
</tr>
</thead>
<tbody>
@foreach($models as $requestableModel)
<tr>
<form action="{{route('account/request-item', ['itemType' => 'asset_model', 'itemId' => $requestableModel->id])}}"
method="POST"
accept-charset="utf-8"
>
{{ csrf_field() }}
<td>{{$requestableModel->name}}</td>
<td>{{$requestableModel->assets()->where('requestable', '1')->count()}}</td>
<td><input type="text" name="request-quantity" value=""></td>
<td>
@if ($requestableModel->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
</td>
</form>
</tr>
@endforeach
</tbody>
@ -84,15 +147,26 @@
{{ trans('general.no_results') }}
</div>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
</div> <!-- .tab-content-->
</div> <!-- .nav-tabs-custom -->
</div> <!-- .col-md-12> -->
</div> <!-- .row -->
@stop
@section('moar_scripts')
<script>
$( "a[name='Request']").click(function(event) {
// event.preventDefault();
quantity = $(this).closest('td').siblings().find('input').val();
currentUrl = $(this).attr('href');
// $(this).attr('href', currentUrl + '?quantity=' + quantity);
// alert($(this).attr('href'));
});
</script>
@stop

View file

@ -231,7 +231,7 @@ View Assets for {{ $user->fullName() }}
@endif
<div class="box-body">
@if (count($user->userlog) > 0)
@if (count($userlog) > 0)
<div class="table-responsive">
<table class="table table-striped" id="example">
<thead>
@ -244,7 +244,7 @@ View Assets for {{ $user->fullName() }}
</tr>
</thead>
<tbody>
@foreach ($user->userlog as $log)
@foreach ($userlog as $log)
<tr>
<td class="text-center">
@if ($log->itemType()=="asset")
@ -260,14 +260,13 @@ View Assets for {{ $user->fullName() }}
@endif
</td>
<td>{{ $log->action_type }}</td>
<td>
{{ strtolower(trans('general.'.str_replace(' ','_',$log->action_type))) }}
</td>
<td>
@if ($log->itemType()=="asset")
@if ($log->item->deleted_at=='')
{{ $log->item->showAssetName() }}
@else
<del>{{ $log->item->showAssetName() }}</del> (deleted)
@endif

View file

@ -0,0 +1,84 @@
@extends('layouts/default')
@section('title0')
{{ trans('admin/hardware/general.requested') }}
{{ trans('general.assets') }}
@stop
{{-- Page title --}}
@section('title')
@yield('title0') @parent
@stop
{{-- Page content --}}
@section('content')
<div class="row">
<div class="col-md-12">
@if ($requestedItems->count() > 0)
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr role="row">
<th class="col-md-1">Item Type</th>
<th class="col-md-1">Item Name</th>
<th class="col-md-1" bSortable="true">{{ trans('admin/hardware/table.location') }}</th>
<th class="col-md-1" bSortable="true">{{ trans('admin/hardware/form.expected_checkin') }}</th>
<th class="col-md-1" bSortable="true">Requesting User</th>
<th class="col-md-1">Requested Date</th>
<th class="col-md-1 actions" bSortable="false">{{ trans('table.actions') }}</th>
</tr>
</thead>
<tbody>
@foreach ($requestedItems as $request)
<tr>
<form action="#" method="POST" accept-charset="utf-8">
{{ csrf_field() }}
<td>{{ $request->itemType() }}</td>
<td>{{ $request->name() }}</td>
@if ($request->location())
<td>{{ $request->location()->name }}</td>
@else
<td></td>
@endif
<td>
@if ($request->itemType() == "asset")
{{ $request->itemRequested()->expected_checkin }}
@else
"N/A"
@endif
</td>
<td>{{ $request->requestingUser()->fullName() }}</td>
<td>{{$request->created_at}}</td>
<td>
</td>
</form>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="col-md-12">
<div class="alert alert-info alert-block">
<i class="fa fa-info-circle"></i>
{{ trans('general.no_results') }}
</div>
</div>
@endif
</div>
</div> <!-- .col-md-12> -->
</div> <!-- .row -->
@stop

View file

@ -127,7 +127,7 @@
</td>
<td>{{ date("M d, Y g:iA", strtotime($activity->created_at)) }}</td>
<td>
@if ($activity->action_type!='requested')
@if (($activity->action_type!='requested') && ($activity->action_type!="request_canceled"))
@if ($activity->user)
<a href="{{ route('view/user', $activity->user_id) }}">{{ $activity->user->fullName() }}</a>
@else
@ -157,7 +157,6 @@
<a href="{{route('view/user', $activity->target_id) }}"> {{ $activity->target->fullName() }}</a>
@elseif ($activity->action_type=='requested') <!--The user is who requested the item, not the target-->
<a href="{{ route('view/user', $activity->user_id) }}">{{ $activity->user->fullName() }}</a>
@endif
</td>

View file

@ -0,0 +1,12 @@
@extends('emails/layouts/default')
@section('content')
<p>A user has canceled an item request on the <a href="{{ config('app.url') }}">{{ \App\Models\Setting::getSettings()->site_name }} website</a>. </p>
<p>User: <a href="{{ config('app.url') }}/admin/users/{{ $user_id }}/view">{{ $requested_by }}</a><br>
Item: <a href="{{ $item_url }}">{{ $item_name }}</a> ({{ $item_type }}) <br>
Canceled: {{ $requested_date }}
</p>
@stop

View file

@ -2,11 +2,14 @@
@section('content')
<p>A user has requested an asset on the <a href="{{ config('app.url') }}">{{ \App\Models\Setting::getSettings()->site_name }} website</a>. </p>
<p>A user has requested an item on the <a href="{{ config('app.url') }}">{{ \App\Models\Setting::getSettings()->site_name }} website</a>. </p>
<p>User: <a href="{{ config('app.url') }}/admin/users/{{ $user_id }}/view">{{ $requested_by }}</a><br>
Asset: <a href="{{ config('app.url') }}/hardware/{{ $asset_id }}/view">{{ $asset_name }}</a> ({{ $asset_type }}) <br>
Item: <a href="{{ $item_url }}">{{ $item_name }}</a> ({{ $item_type }}) <br>
Requested: {{ $requested_date }}
@if ($item_quantity > 1)
Quantity: {{ $item_quantity}}
@endif
</p>
@stop

View file

@ -523,7 +523,6 @@
</td>
<td>{{ $log->action_type }}</td>
<td>
@if ($log->action_type=='uploaded')
{{ $log->filename }}

View file

@ -118,6 +118,15 @@
</div>
</div>
<!-- Requestable -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-10">
<label>
<input type="checkbox" value="1" name="requestable" id="requestable" class="minimal" {{ Input::old('requestable', $model->requestable) == '1' ? ' checked="checked"' : '' }}> {{ trans('admin/models/general.requestable') }}
</label>
</div>
</div>
<!-- Image -->
@if ($model->image)
<div class="form-group {{ $errors->has('image_delete') ? 'has-error' : '' }}">