mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-12 16:44:08 -08:00
Merge pull request #15185 from arne-kroeger/feat/accesspories-checkout-to-location-or-asset
Added #14979: add checkout to location and assets functionality to accessories
This commit is contained in:
commit
4eccb5ffc6
|
@ -92,7 +92,7 @@ class SQLStreamer {
|
||||||
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
|
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
|
||||||
|
|
||||||
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; //TODO - move to statics?
|
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; //TODO - move to statics?
|
||||||
//can't use 'users' because the 'accessories_users' table?
|
//can't use 'users' because the 'accessories_checkout' table?
|
||||||
// can't use 'assets' because 'ver1_components_assets'
|
// can't use 'assets' because 'ver1_components_assets'
|
||||||
foreach($check_tables as $check_table => $_ignore) {
|
foreach($check_tables as $check_table => $_ignore) {
|
||||||
foreach ($parser->tablenames as $tablename => $_count) {
|
foreach ($parser->tablenames as $tablename => $_count) {
|
||||||
|
|
|
@ -721,7 +721,7 @@ class Helper
|
||||||
{
|
{
|
||||||
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
||||||
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
||||||
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
|
$accessories = Accessory::withCount('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
|
||||||
$components = Component::whereNotNull('min_amt')->get();
|
$components = Component::whereNotNull('min_amt')->get();
|
||||||
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
||||||
$licenses = License::where('min_amt', '>', 0)->get();
|
$licenses = License::where('min_amt', '>', 0)->get();
|
||||||
|
@ -749,7 +749,7 @@ class Helper
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($accessories as $accessory) {
|
foreach ($accessories as $accessory) {
|
||||||
$avail = $accessory->qty - $accessory->users_count;
|
$avail = $accessory->qty - $accessory->checkouts_count;
|
||||||
if ($avail < ($accessory->min_amt) + $alert_threshold) {
|
if ($avail < ($accessory->min_amt) + $alert_threshold) {
|
||||||
if ($accessory->qty > 0) {
|
if ($accessory->qty > 0) {
|
||||||
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
||||||
|
|
|
@ -144,12 +144,12 @@ class AccessoriesController extends Controller
|
||||||
*/
|
*/
|
||||||
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
|
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
|
||||||
{
|
{
|
||||||
if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) {
|
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
|
||||||
|
|
||||||
$this->authorize($accessory);
|
$this->authorize($accessory);
|
||||||
|
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
"qty" => "required|numeric|min:$accessory->users_count"
|
"qty" => "required|numeric|min:$accessory->checkouts_count"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
|
@ -233,7 +233,7 @@ class AccessoriesController extends Controller
|
||||||
*/
|
*/
|
||||||
public function show($accessoryID = null) : View | RedirectResponse
|
public function show($accessoryID = null) : View | RedirectResponse
|
||||||
{
|
{
|
||||||
$accessory = Accessory::withCount('users as users_count')->find($accessoryID);
|
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
|
||||||
$this->authorize('view', $accessory);
|
$this->authorize('view', $accessory);
|
||||||
if (isset($accessory->id)) {
|
if (isset($accessory->id)) {
|
||||||
return view('accessories/view', compact('accessory'));
|
return view('accessories/view', compact('accessory'));
|
||||||
|
|
|
@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Accessory;
|
use App\Models\Accessory;
|
||||||
|
use App\Models\AccessoryCheckout;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
@ -24,7 +25,7 @@ class AccessoryCheckinController extends Controller
|
||||||
*/
|
*/
|
||||||
public function create($accessoryUserId = null, $backto = null) : View | RedirectResponse
|
public function create($accessoryUserId = null, $backto = null) : View | RedirectResponse
|
||||||
{
|
{
|
||||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
if (is_null($accessory_user = DB::table('accessories_checkout')->find($accessoryUserId))) {
|
||||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,16 +40,16 @@ class AccessoryCheckinController extends Controller
|
||||||
*
|
*
|
||||||
* @uses Accessory::checkin_email() to determine if an email can and should be sent
|
* @uses Accessory::checkin_email() to determine if an email can and should be sent
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
* @param null $accessoryUserId
|
* @param null $accessoryCheckoutId
|
||||||
* @param string $backto
|
* @param string $backto
|
||||||
*/
|
*/
|
||||||
public function store(Request $request, $accessoryUserId = null, $backto = null) : RedirectResponse
|
public function store(Request $request, $accessoryCheckoutId = null, $backto = null) : RedirectResponse
|
||||||
{
|
{
|
||||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryCheckoutId))) {
|
||||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||||
|
|
||||||
$this->authorize('checkin', $accessory);
|
$this->authorize('checkin', $accessory);
|
||||||
|
|
||||||
|
@ -59,10 +60,8 @@ class AccessoryCheckinController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Was the accessory updated?
|
// Was the accessory updated?
|
||||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
if ($accessory_checkout->delete()) {
|
||||||
$return_to = e($accessory_user->assigned_to);
|
event(new CheckoutableCheckedIn($accessory, $accessory_checkout->assignedTo, auth()->user(), $request->input('note'), $checkin_at));
|
||||||
|
|
||||||
event(new CheckoutableCheckedIn($accessory, User::find($return_to), auth()->user(), $request->input('note'), $checkin_at));
|
|
||||||
|
|
||||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@ namespace App\Http\Controllers\Accessories;
|
||||||
|
|
||||||
use App\Events\CheckoutableCheckedOut;
|
use App\Events\CheckoutableCheckedOut;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
|
use App\Http\Controllers\CheckInOutRequest;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||||
use App\Models\Accessory;
|
use App\Models\Accessory;
|
||||||
|
use App\Models\AccessoryCheckout;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
@ -16,6 +18,9 @@ use \Illuminate\Http\RedirectResponse;
|
||||||
|
|
||||||
class AccessoryCheckoutController extends Controller
|
class AccessoryCheckoutController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
use CheckInOutRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the form to checkout an Accessory to a user.
|
* Return the form to checkout an Accessory to a user.
|
||||||
*
|
*
|
||||||
|
@ -25,7 +30,7 @@ class AccessoryCheckoutController extends Controller
|
||||||
public function create($id) : View | RedirectResponse
|
public function create($id) : View | RedirectResponse
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($accessory = Accessory::withCount('users as users_count')->find($id)) {
|
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($id)) {
|
||||||
|
|
||||||
$this->authorize('checkout', $accessory);
|
$this->authorize('checkout', $accessory);
|
||||||
|
|
||||||
|
@ -58,30 +63,32 @@ class AccessoryCheckoutController extends Controller
|
||||||
*
|
*
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param int $accessory
|
* @param Accessory $accessory
|
||||||
*/
|
*/
|
||||||
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
|
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->authorize('checkout', $accessory);
|
$this->authorize('checkout', $accessory);
|
||||||
$accessory->assigned_to = $request->input('assigned_to');
|
|
||||||
$user = User::find($request->input('assigned_to'));
|
|
||||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
|
||||||
|
|
||||||
|
$target = $this->determineCheckoutTarget();
|
||||||
|
|
||||||
|
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||||
|
|
||||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||||
$accessory->users()->attach($accessory->id, [
|
AccessoryCheckout::create([
|
||||||
'accessory_id' => $accessory->id,
|
'accessory_id' => $accessory->id,
|
||||||
'created_at' => Carbon::now(),
|
'created_at' => Carbon::now(),
|
||||||
'user_id' => Auth::id(),
|
'user_id' => Auth::id(),
|
||||||
'assigned_to' => $request->input('assigned_to'),
|
'assigned_to' => $target->id,
|
||||||
|
'assigned_type' => $target::class,
|
||||||
'note' => $request->input('note'),
|
'note' => $request->input('note'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
|
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||||
|
|
||||||
// Set this as user since we only allow checkout to user for this item type
|
// Set this as user since we only allow checkout to user for this item type
|
||||||
$request->request->add(['checkout_to_type' => 'user']);
|
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
|
||||||
$request->request->add(['assigned_user' => $user->id]);
|
$request->request->add(['assigned_user' => $target->id]);
|
||||||
|
|
||||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Events\CheckoutableCheckedOut;
|
use App\Events\CheckoutableCheckedOut;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
|
use App\Http\Controllers\CheckInOutRequest;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||||
use App\Http\Requests\StoreAccessoryRequest;
|
use App\Http\Requests\StoreAccessoryRequest;
|
||||||
|
@ -17,10 +18,12 @@ use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Requests\ImageUploadRequest;
|
use App\Http\Requests\ImageUploadRequest;
|
||||||
|
use App\Models\AccessoryCheckout;
|
||||||
|
|
||||||
class AccessoriesController extends Controller
|
class AccessoriesController extends Controller
|
||||||
{
|
{
|
||||||
|
use CheckInOutRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*
|
*
|
||||||
|
@ -48,13 +51,13 @@ class AccessoriesController extends Controller
|
||||||
'min_amt',
|
'min_amt',
|
||||||
'company_id',
|
'company_id',
|
||||||
'notes',
|
'notes',
|
||||||
'users_count',
|
'checkouts_count',
|
||||||
'qty',
|
'qty',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier')
|
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier')
|
||||||
->withCount('users as users_count');
|
->withCount('checkouts as checkouts_count');
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$accessories = $accessories->TextSearch($request->input('search'));
|
$accessories = $accessories->TextSearch($request->input('search'));
|
||||||
|
@ -154,7 +157,7 @@ class AccessoriesController extends Controller
|
||||||
public function show($id)
|
public function show($id)
|
||||||
{
|
{
|
||||||
$this->authorize('view', Accessory::class);
|
$this->authorize('view', Accessory::class);
|
||||||
$accessory = Accessory::withCount('users as users_count')->findOrFail($id);
|
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
|
||||||
|
|
||||||
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
||||||
}
|
}
|
||||||
|
@ -197,28 +200,23 @@ class AccessoriesController extends Controller
|
||||||
$offset = request('offset', 0);
|
$offset = request('offset', 0);
|
||||||
$limit = request('limit', 50);
|
$limit = request('limit', 50);
|
||||||
|
|
||||||
$accessory_users = $accessory->users;
|
$accessory_checkouts = $accessory->checkouts;
|
||||||
$total = $accessory_users->count();
|
$total = $accessory_checkouts->count();
|
||||||
|
|
||||||
if ($total < $offset) {
|
if ($total < $offset) {
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessory_users = $accessory->users()->skip($offset)->take($limit)->get();
|
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$accessory_users = $accessory->users()
|
|
||||||
->where(function ($query) use ($request) {
|
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
|
||||||
$search_str = '%' . $request->input('search') . '%';
|
|
||||||
$query->where('first_name', 'like', $search_str)
|
|
||||||
->orWhere('last_name', 'like', $search_str)
|
|
||||||
->orWhere('note', 'like', $search_str);
|
|
||||||
})
|
|
||||||
->get();
|
->get();
|
||||||
$total = $accessory_users->count();
|
$total = $accessory_checkouts->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total);
|
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,22 +280,22 @@ class AccessoriesController extends Controller
|
||||||
public function checkout(AccessoryCheckoutRequest $request, Accessory $accessory)
|
public function checkout(AccessoryCheckoutRequest $request, Accessory $accessory)
|
||||||
{
|
{
|
||||||
$this->authorize('checkout', $accessory);
|
$this->authorize('checkout', $accessory);
|
||||||
$accessory->assigned_to = $request->input('assigned_to');
|
$target = $this->determineCheckoutTarget();
|
||||||
$user = User::find($request->input('assigned_to'));
|
|
||||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||||
|
|
||||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||||
$accessory->users()->attach($accessory->id, [
|
AccessoryCheckout::create([
|
||||||
'accessory_id' => $accessory->id,
|
'accessory_id' => $accessory->id,
|
||||||
'created_at' => Carbon::now(),
|
'created_at' => Carbon::now(),
|
||||||
'user_id' => Auth::id(),
|
'user_id' => Auth::id(),
|
||||||
'assigned_to' => $request->input('assigned_to'),
|
'assigned_to' => $target->id,
|
||||||
|
'assigned_type' => $target::class,
|
||||||
'note' => $request->input('note'),
|
'note' => $request->input('note'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set this value to be able to pass the qty through to the event
|
// Set this value to be able to pass the qty through to the event
|
||||||
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
|
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||||
|
|
||||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
|
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
|
||||||
|
|
||||||
|
@ -316,19 +314,19 @@ class AccessoriesController extends Controller
|
||||||
*/
|
*/
|
||||||
public function checkin(Request $request, $accessoryUserId = null)
|
public function checkin(Request $request, $accessoryUserId = null)
|
||||||
{
|
{
|
||||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryUserId))) {
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||||
$this->authorize('checkin', $accessory);
|
$this->authorize('checkin', $accessory);
|
||||||
|
|
||||||
$logaction = $accessory->logCheckin(User::find($accessory_user->assigned_to), $request->input('note'));
|
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||||
|
|
||||||
// Was the accessory updated?
|
// Was the accessory updated?
|
||||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
if ($accessory_checkout->delete()) {
|
||||||
if (! is_null($accessory_user->assigned_to)) {
|
if (! is_null($accessory_checkout->assigned_to)) {
|
||||||
$user = User::find($accessory_user->assigned_to);
|
$user = User::find($accessory_checkout->assigned_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data['log_id'] = $logaction->id;
|
$data['log_id'] = $logaction->id;
|
||||||
|
|
|
@ -219,7 +219,7 @@ class BulkUsersController extends Controller
|
||||||
|
|
||||||
$users = User::whereIn('id', $user_raw_array)->get();
|
$users = User::whereIn('id', $user_raw_array)->get();
|
||||||
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', \App\Models\User::class)->get();
|
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', \App\Models\User::class)->get();
|
||||||
$accessories = DB::table('accessories_users')->whereIn('assigned_to', $user_raw_array)->get();
|
$accessories = DB::table('accessories_checkout')->whereIn('assigned_to', $user_raw_array)->get();
|
||||||
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
|
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
|
||||||
$consumables = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
|
$consumables = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||||
|
|
||||||
|
|
|
@ -44,13 +44,10 @@ class AccessoryCheckoutRequest extends ImageUploadRequest
|
||||||
|
|
||||||
return array_merge(
|
return array_merge(
|
||||||
[
|
[
|
||||||
'assigned_to' => [
|
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
|
||||||
'required',
|
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
|
||||||
'integer',
|
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
|
||||||
'exists:users,id,deleted_at,NULL',
|
|
||||||
'not_array'
|
|
||||||
],
|
|
||||||
|
|
||||||
'number_remaining_after_checkout' => [
|
'number_remaining_after_checkout' => [
|
||||||
'min:0',
|
'min:0',
|
||||||
'required',
|
'required',
|
||||||
|
|
|
@ -39,7 +39,7 @@ class AccessoriesTransformer
|
||||||
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
||||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||||
'remaining_qty' => (int) $accessory->numRemaining(),
|
'remaining_qty' => (int) $accessory->numRemaining(),
|
||||||
'users_count' => $accessory->users_count,
|
'checkouts_count' => $accessory->checkouts_count,
|
||||||
|
|
||||||
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
|
||||||
|
@ -66,27 +66,42 @@ class AccessoriesTransformer
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function transformCheckedoutAccessory($accessory, $accessory_users, $total)
|
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
|
|
||||||
foreach ($accessory_users as $user) {
|
foreach ($accessory_checkouts as $checkout) {
|
||||||
$array[] = [
|
$array[] = [
|
||||||
|
'id' => $checkout->id,
|
||||||
'assigned_pivot_id' => $user->pivot->id,
|
'assigned_to' => $this->transformAssignedTo($checkout),
|
||||||
'id' => (int) $user->id,
|
'checkout_notes' => e($checkout->note),
|
||||||
'username' => e($user->username),
|
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
|
||||||
'name' => e($user->getFullNameAttribute()),
|
|
||||||
'first_name'=> e($user->first_name),
|
|
||||||
'last_name'=> e($user->last_name),
|
|
||||||
'employee_number' => e($user->employee_num),
|
|
||||||
'checkout_notes' => e($user->pivot->note),
|
|
||||||
'last_checkout' => Helper::getFormattedDateObject($user->pivot->created_at, 'datetime'),
|
|
||||||
'type' => 'user',
|
|
||||||
'available_actions' => ['checkin' => true],
|
'available_actions' => ['checkin' => true],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function transformAssignedTo($accessoryCheckout)
|
||||||
|
{
|
||||||
|
if ($accessoryCheckout->checkedOutToUser()) {
|
||||||
|
return [
|
||||||
|
'id' => (int) $accessoryCheckout->assigned->id,
|
||||||
|
'username' => e($accessoryCheckout->assigned->username),
|
||||||
|
'name' => e($accessoryCheckout->assigned->getFullNameAttribute()),
|
||||||
|
'first_name'=> e($accessoryCheckout->assigned->first_name),
|
||||||
|
'last_name'=> ($accessoryCheckout->assigned->last_name) ? e($accessoryCheckout->assigned->last_name) : null,
|
||||||
|
'email'=> ($accessoryCheckout->assigned->email) ? e($accessoryCheckout->assigned->email) : null,
|
||||||
|
'employee_number' => ($accessoryCheckout->assigned->employee_num) ? e($accessoryCheckout->assigned->employee_num) : null,
|
||||||
|
'type' => 'user',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $accessoryCheckout->assigned ? [
|
||||||
|
'id' => $accessoryCheckout->assigned->id,
|
||||||
|
'name' => e($accessoryCheckout->assigned->display_name),
|
||||||
|
'type' => $accessoryCheckout->assignedType(),
|
||||||
|
] : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,11 +205,11 @@ class ActionlogsTransformer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
|
public function transformCheckedoutActionlog (Collection $accessories_checkout, $total)
|
||||||
{
|
{
|
||||||
|
|
||||||
$array = array();
|
$array = array();
|
||||||
foreach ($accessories_users as $user) {
|
foreach ($accessories_checkout as $user) {
|
||||||
$array[] = (new UsersTransformer)->transformUser($user);
|
$array[] = (new UsersTransformer)->transformUser($user);
|
||||||
}
|
}
|
||||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||||
|
|
|
@ -253,9 +253,10 @@ class Accessory extends SnipeModel
|
||||||
* @since [v3.0]
|
* @since [v3.0]
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
*/
|
*/
|
||||||
public function users()
|
public function checkouts()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->withPivot('id', 'created_at', 'note')->withTrashed();
|
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
|
||||||
|
->with('assignedTo');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,7 +268,9 @@ class Accessory extends SnipeModel
|
||||||
*/
|
*/
|
||||||
public function hasUsers()
|
public function hasUsers()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->count();
|
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
|
||||||
|
->where('assigned_type', User::class)
|
||||||
|
->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -338,15 +341,15 @@ class Accessory extends SnipeModel
|
||||||
*/
|
*/
|
||||||
public function numCheckedOut()
|
public function numCheckedOut()
|
||||||
{
|
{
|
||||||
return $this->users_count ?? $this->users()->count();
|
return $this->checkouts_count ?? $this->checkouts()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check how many items of an accessory remain.
|
* Check how many items of an accessory remain.
|
||||||
*
|
*
|
||||||
* In order to use this model method, you MUST call withCount('users as users_count')
|
* In order to use this model method, you MUST call withCount('checkouts as checkouts_count')
|
||||||
* on the eloquent query in the controller, otherwise $this->users_count will be null and
|
* on the eloquent query in the controller, otherwise $this->checkouts_count will be null and
|
||||||
* bad things happen.
|
* bad things happen.
|
||||||
*
|
*
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
@ -370,12 +373,12 @@ class Accessory extends SnipeModel
|
||||||
*/
|
*/
|
||||||
public function declinedCheckout(User $declinedBy, $signature)
|
public function declinedCheckout(User $declinedBy, $signature)
|
||||||
{
|
{
|
||||||
if (is_null($accessory_user = \DB::table('accessories_users')->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
|
if (is_null($accessory_checkout = AccessoryCheckout::userAssigned()->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
|
||||||
// Redirect to the accessory management page with error
|
// Redirect to the accessory management page with error
|
||||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessory_user->limit(1)->delete();
|
$accessory_checkout->limit(1)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
148
app/Models/AccessoryCheckout.php
Executable file
148
app/Models/AccessoryCheckout.php
Executable file
|
@ -0,0 +1,148 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Helpers\Helper;
|
||||||
|
use App\Models\Traits\Acceptable;
|
||||||
|
use App\Models\Traits\Searchable;
|
||||||
|
use App\Presenters\Presentable;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Watson\Validating\ValidatingTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model for Accessories.
|
||||||
|
*
|
||||||
|
* @version v1.0
|
||||||
|
*/
|
||||||
|
class AccessoryCheckout extends Model
|
||||||
|
{
|
||||||
|
use Searchable;
|
||||||
|
|
||||||
|
protected $fillable = ['user_id', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
|
||||||
|
protected $table = 'accessories_checkout';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the accessory checkout -> accessory relationship
|
||||||
|
*
|
||||||
|
* @author [A. Kroeger]
|
||||||
|
* @since [v7.0.9]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function accessory()
|
||||||
|
{
|
||||||
|
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the accessory checkout -> user relationship
|
||||||
|
*
|
||||||
|
* @author [A. Kroeger]
|
||||||
|
* @since [v7.0.9]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->hasOne(\App\Models\User::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the target this asset is checked out to
|
||||||
|
*
|
||||||
|
* @author [A. Kroeger]
|
||||||
|
* @since [v7.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function assignedTo()
|
||||||
|
{
|
||||||
|
return $this->morphTo('assigned', 'assigned_type', 'assigned_to')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the lowercased name of the type of target the asset is assigned to
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v4.0]
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function assignedType()
|
||||||
|
{
|
||||||
|
return $this->assigned_type ? strtolower(class_basename($this->assigned_type)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the accessory is checked out to a user
|
||||||
|
*
|
||||||
|
* Even though we allow allow for checkout to things beyond users
|
||||||
|
* this method is an easy way of seeing if we are checked out to a user.
|
||||||
|
*
|
||||||
|
* @author [A. Kroeger]
|
||||||
|
* @since [v7.0]
|
||||||
|
*/
|
||||||
|
public function checkedOutToUser(): bool
|
||||||
|
{
|
||||||
|
return $this->assignedType() === Asset::USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeUserAssigned(Builder $query): void
|
||||||
|
{
|
||||||
|
$query->where('assigned_type', '=', User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run additional, advanced searches.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @param array $terms The search terms
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
public function advancedTextSearch(Builder $query, array $terms)
|
||||||
|
{
|
||||||
|
|
||||||
|
$userQuery = User::where(function ($query) use ($terms) {
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
$search_str = '%' . $term . '%';
|
||||||
|
$query->where('first_name', 'like', $search_str)
|
||||||
|
->orWhere('last_name', 'like', $search_str)
|
||||||
|
->orWhere('note', 'like', $search_str);
|
||||||
|
}
|
||||||
|
})->select('id');
|
||||||
|
|
||||||
|
$locationQuery = Location::where(function ($query) use ($terms) {
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
$search_str = '%' . $term . '%';
|
||||||
|
$query->where('name', 'like', $search_str);
|
||||||
|
}
|
||||||
|
})->select('id');
|
||||||
|
|
||||||
|
$assetQuery = Asset::where(function ($query) use ($terms) {
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
$search_str = '%' . $term . '%';
|
||||||
|
$query->where('name', 'like', $search_str);
|
||||||
|
}
|
||||||
|
})->select('id');
|
||||||
|
|
||||||
|
$query->where(function ($query) use ($userQuery) {
|
||||||
|
$query->where('assigned_type', User::class)
|
||||||
|
->whereIn('assigned_to', $userQuery);
|
||||||
|
})->orWhere(function($query) use ($locationQuery) {
|
||||||
|
$query->where('assigned_type', Location::class)
|
||||||
|
->whereIn('assigned_to', $locationQuery);
|
||||||
|
})->orWhere(function($query) use ($assetQuery) {
|
||||||
|
$query->where('assigned_type', Asset::class)
|
||||||
|
->whereIn('assigned_to', $assetQuery);
|
||||||
|
})->orWhere(function($query) use ($terms) {
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
$search_str = '%' . $term . '%';
|
||||||
|
$query->where('note', 'like', $search_str);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -331,7 +331,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||||
*/
|
*/
|
||||||
public function accessories()
|
public function accessories()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_users', 'assigned_to', 'accessory_id')
|
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_checkout', 'assigned_to', 'accessory_id')
|
||||||
->withPivot('id', 'created_at', 'note')->withTrashed()->orderBy('accessory_id');
|
->withPivot('id', 'created_at', 'note')->withTrashed()->orderBy('accessory_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ class AccessoryPresenter extends Presenter
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
'title' => trans('admin/accessories/general.remaining'),
|
'title' => trans('admin/accessories/general.remaining'),
|
||||||
],[
|
],[
|
||||||
'field' => 'users_count',
|
'field' => 'checkouts_count',
|
||||||
'searchable' => false,
|
'searchable' => false,
|
||||||
'sortable' => true,
|
'sortable' => true,
|
||||||
'visible' => true,
|
'visible' => true,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Database\Factories;
|
namespace Database\Factories;
|
||||||
|
|
||||||
use App\Models\Accessory;
|
use App\Models\Accessory;
|
||||||
|
use App\Models\AccessoryCheckout;
|
||||||
use App\Models\Category;
|
use App\Models\Category;
|
||||||
use App\Models\Location;
|
use App\Models\Location;
|
||||||
use App\Models\Manufacturer;
|
use App\Models\Manufacturer;
|
||||||
|
@ -125,11 +126,12 @@ class AccessoryFactory extends Factory
|
||||||
})->afterCreating(function ($accessory) {
|
})->afterCreating(function ($accessory) {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
$accessory->users()->attach($accessory->id, [
|
$accessory->checkouts()->create([
|
||||||
'accessory_id' => $accessory->id,
|
'accessory_id' => $accessory->id,
|
||||||
'created_at' => now(),
|
'created_at' => Carbon::now(),
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'assigned_to' => $user->id,
|
'assigned_to' => $user->id,
|
||||||
|
'assigned_type' => User::class,
|
||||||
'note' => '',
|
'note' => '',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -145,11 +147,12 @@ class AccessoryFactory extends Factory
|
||||||
public function checkedOutToUser(User $user = null)
|
public function checkedOutToUser(User $user = null)
|
||||||
{
|
{
|
||||||
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
||||||
$accessory->users()->attach($accessory->id, [
|
$accessory->checkouts()->create([
|
||||||
'accessory_id' => $accessory->id,
|
'accessory_id' => $accessory->id,
|
||||||
'created_at' => Carbon::now(),
|
'created_at' => Carbon::now(),
|
||||||
'user_id' => 1,
|
'user_id' => 1,
|
||||||
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
||||||
|
'assigned_type' => User::class,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('accessories_users')) {
|
||||||
|
Schema::rename('accessories_users', 'accessories_checkout');
|
||||||
|
|
||||||
|
Schema::table('accessories_checkout', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('accessories_checkout', 'assigned_type')) {
|
||||||
|
$table->string('assigned_type')->nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::update('update '.DB::getTablePrefix().'accessories_checkout set assigned_type = \'App\\\\Models\\\\User\' where assigned_type is null', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('accessories_checkout')) {
|
||||||
|
Schema::table('accessories_checkout', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('assigned_type');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::rename('accessories_checkout', 'accessories_users');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -66,7 +66,14 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- User -->
|
<!-- User -->
|
||||||
|
|
||||||
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.select_user'), 'fieldname' => 'assigned_to', 'required'=> 'true'])
|
@include ('partials.forms.checkout-selector', ['user_select' => 'true','asset_select' => 'true', 'location_select' => 'true'])
|
||||||
|
|
||||||
|
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.select_user'), 'fieldname' => 'assigned_user', 'required'=> 'true'])
|
||||||
|
|
||||||
|
<!-- We have to pass unselect here so that we don't default to the asset that's being checked out. We want that asset to be pre-selected everywhere else. -->
|
||||||
|
@include ('partials.forms.edit.asset-select', ['translated_name' => trans('general.asset'), 'fieldname' => 'assigned_asset', 'unselect' => 'true', 'style' => 'display:none;', 'required'=>'true'])
|
||||||
|
|
||||||
|
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'assigned_location', 'style' => 'display:none;', 'required'=>'true'])
|
||||||
|
|
||||||
<!-- Checkout QTY -->
|
<!-- Checkout QTY -->
|
||||||
<div class="form-group {{ $errors->has('checkout_qty') ? 'error' : '' }} ">
|
<div class="form-group {{ $errors->has('checkout_qty') ? 'error' : '' }} ">
|
||||||
|
|
|
@ -68,9 +68,9 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<table
|
<table
|
||||||
data-cookie-id-table="usersTable"
|
data-cookie-id-table="checkoutsTable"
|
||||||
data-pagination="true"
|
data-pagination="true"
|
||||||
data-id-table="usersTable"
|
data-id-table="checkoutsTable"
|
||||||
data-search="true"
|
data-search="true"
|
||||||
data-side-pagination="server"
|
data-side-pagination="server"
|
||||||
data-show-columns="true"
|
data-show-columns="true"
|
||||||
|
@ -78,16 +78,16 @@
|
||||||
data-show-export="true"
|
data-show-export="true"
|
||||||
data-show-refresh="true"
|
data-show-refresh="true"
|
||||||
data-sort-order="asc"
|
data-sort-order="asc"
|
||||||
id="usersTable"
|
id="checkoutsTable"
|
||||||
class="table table-striped snipe-table"
|
class="table table-striped snipe-table"
|
||||||
data-url="{{ route('api.accessories.checkedout', $accessory->id) }}"
|
data-url="{{ route('api.accessories.checkedout', $accessory->id) }}"
|
||||||
data-export-options='{
|
data-export-options='{
|
||||||
"fileName": "export-accessories-{{ str_slug($accessory->name) }}-users-{{ date('Y-m-d') }}",
|
"fileName": "export-accessories-{{ str_slug($accessory->name) }}-checkouts-{{ date('Y-m-d') }}",
|
||||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||||
}'>
|
}'>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-searchable="false" data-formatter="usersLinkFormatter" data-sortable="false" data-field="name">{{ trans('general.user') }}</th>
|
<th data-searchable="false" data-formatter="polymorphicItemFormatter" data-sortable="false" data-field="assigned_to">{{ trans('general.checked_out_to') }}</th>
|
||||||
<th data-searchable="false" data-sortable="false" data-field="checkout_notes">{{ trans('general.notes') }}</th>
|
<th data-searchable="false" data-sortable="false" data-field="checkout_notes">{{ trans('general.notes') }}</th>
|
||||||
<th data-searchable="false" data-formatter="dateDisplayFormatter" data-sortable="false" data-field="last_checkout">{{ trans('admin/hardware/table.checkout_date') }}</th>
|
<th data-searchable="false" data-formatter="dateDisplayFormatter" data-sortable="false" data-field="last_checkout">{{ trans('admin/hardware/table.checkout_date') }}</th>
|
||||||
<th data-searchable="false" data-sortable="false" data-field="actions" data-formatter="accessoriesInOutFormatter">{{ trans('table.actions') }}</th>
|
<th data-searchable="false" data-sortable="false" data-field="actions" data-formatter="accessoriesInOutFormatter">{{ trans('table.actions') }}</th>
|
||||||
|
@ -314,7 +314,7 @@
|
||||||
<strong>{{ trans('general.checked_out') }}</strong>
|
<strong>{{ trans('general.checked_out') }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
{{ $accessory->users_count }}
|
{{ $accessory->checkouts_count }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -337,7 +337,7 @@
|
||||||
@endcan
|
@endcan
|
||||||
|
|
||||||
@can('delete', $accessory)
|
@can('delete', $accessory)
|
||||||
@if ($accessory->users_count == 0)
|
@if ($accessory->checkouts_count == 0)
|
||||||
<div class="text-center" style="padding-top:5px;">
|
<div class="text-center" style="padding-top:5px;">
|
||||||
<button class="btn btn-block btn-danger delete-asset" style="padding-top:5px;" data-toggle="modal" data-title="{{ trans('general.delete') }}" data-content="{{ trans('general.delete_confirm_no_undo', ['item' => $accessory->name]) }}" data-target="#dataConfirmModal">
|
<button class="btn btn-block btn-danger delete-asset" style="padding-top:5px;" data-toggle="modal" data-title="{{ trans('general.delete') }}" data-content="{{ trans('general.delete_confirm_no_undo', ['item' => $accessory->name]) }}" data-target="#dataConfirmModal">
|
||||||
{{ trans('general.delete') }}
|
{{ trans('general.delete') }}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AccessoryCheckinTest extends TestCase
|
||||||
$accessory = Accessory::factory()->checkedOutToUser()->create();
|
$accessory = Accessory::factory()->checkedOutToUser()->create();
|
||||||
|
|
||||||
$this->actingAs(User::factory()->create())
|
$this->actingAs(User::factory()->create())
|
||||||
->post(route('accessories.checkin.store', $accessory->users->first()->pivot->id))
|
->post(route('accessories.checkin.store', $accessory->checkouts->first()->id))
|
||||||
->assertForbidden();
|
->assertForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,12 +28,12 @@ class AccessoryCheckinTest extends TestCase
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$accessory = Accessory::factory()->checkedOutToUser($user)->create();
|
$accessory = Accessory::factory()->checkedOutToUser($user)->create();
|
||||||
|
|
||||||
$this->assertTrue($accessory->users->contains($user));
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
|
|
||||||
$this->actingAs(User::factory()->checkinAccessories()->create())
|
$this->actingAs(User::factory()->checkinAccessories()->create())
|
||||||
->post(route('accessories.checkin.store', $accessory->users->first()->pivot->id));
|
->post(route('accessories.checkin.store', $accessory->checkouts->first()->id));
|
||||||
|
|
||||||
$this->assertFalse($accessory->fresh()->users->contains($user));
|
$this->assertFalse($accessory->fresh()->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
|
|
||||||
Event::assertDispatched(CheckoutableCheckedIn::class, 1);
|
Event::assertDispatched(CheckoutableCheckedIn::class, 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||||
->postJson(route('api.accessories.checkout', Accessory::factory()->create()), [
|
->postJson(route('api.accessories.checkout', Accessory::factory()->create()), [
|
||||||
// missing assigned_to
|
// missing assigned_user, assigned_location, assigned_asset
|
||||||
])
|
])
|
||||||
->assertStatusMessageIs('error');
|
->assertStatusMessageIs('error');
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||||
->postJson(route('api.accessories.checkout', Accessory::factory()->withoutItemsRemaining()->create()), [
|
->postJson(route('api.accessories.checkout', Accessory::factory()->withoutItemsRemaining()->create()), [
|
||||||
'assigned_to' => User::factory()->create()->id,
|
'assigned_user' => User::factory()->create()->id,
|
||||||
|
'checkout_to_type' => 'user'
|
||||||
])
|
])
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertStatusMessageIs('error')
|
->assertStatusMessageIs('error')
|
||||||
|
@ -65,7 +66,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
|
|
||||||
$this->actingAsForApi($admin)
|
$this->actingAsForApi($admin)
|
||||||
->postJson(route('api.accessories.checkout', $accessory), [
|
->postJson(route('api.accessories.checkout', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user'
|
||||||
])
|
])
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertStatusMessageIs('success')
|
->assertStatusMessageIs('success')
|
||||||
|
@ -73,7 +75,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
->assertJson(['messages' => trans('admin/accessories/message.checkout.success')])
|
->assertJson(['messages' => trans('admin/accessories/message.checkout.success')])
|
||||||
->json();
|
->json();
|
||||||
|
|
||||||
$this->assertTrue($accessory->users->contains($user));
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
1,
|
1,
|
||||||
|
@ -96,7 +98,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
|
|
||||||
$this->actingAsForApi($admin)
|
$this->actingAsForApi($admin)
|
||||||
->postJson(route('api.accessories.checkout', $accessory), [
|
->postJson(route('api.accessories.checkout', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'checkout_qty' => 2,
|
'checkout_qty' => 2,
|
||||||
])
|
])
|
||||||
->assertOk()
|
->assertOk()
|
||||||
|
@ -105,7 +108,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
->assertJson(['messages' => trans('admin/accessories/message.checkout.success')])
|
->assertJson(['messages' => trans('admin/accessories/message.checkout.success')])
|
||||||
->json();
|
->json();
|
||||||
|
|
||||||
$this->assertTrue($accessory->users->contains($user));
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
1,
|
1,
|
||||||
|
@ -128,7 +131,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
|
|
||||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||||
->postJson(route('api.accessories.checkout', $accessory), [
|
->postJson(route('api.accessories.checkout', $accessory), [
|
||||||
'assigned_to' => 'invalid-user-id',
|
'assigned_user' => 'invalid-user-id',
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'note' => 'oh hi there',
|
'note' => 'oh hi there',
|
||||||
])
|
])
|
||||||
->assertOk()
|
->assertOk()
|
||||||
|
@ -136,7 +140,7 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
->assertStatus(200)
|
->assertStatus(200)
|
||||||
->json();
|
->json();
|
||||||
|
|
||||||
$this->assertFalse($accessory->users->contains($user));
|
$this->assertFalse($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserSentNotificationUponCheckout()
|
public function testUserSentNotificationUponCheckout()
|
||||||
|
@ -148,7 +152,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
|
|
||||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||||
->postJson(route('api.accessories.checkout', $accessory), [
|
->postJson(route('api.accessories.checkout', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
|
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
|
||||||
|
@ -162,7 +167,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
|
|
||||||
$this->actingAsForApi($actor)
|
$this->actingAsForApi($actor)
|
||||||
->postJson(route('api.accessories.checkout', $accessory), [
|
->postJson(route('api.accessories.checkout', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'note' => 'oh hi there',
|
'note' => 'oh hi there',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Tests\Feature\Checkouts\Ui;
|
||||||
|
|
||||||
use App\Models\Accessory;
|
use App\Models\Accessory;
|
||||||
use App\Models\Actionlog;
|
use App\Models\Actionlog;
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\Location;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\CheckoutAccessoryNotification;
|
use App\Notifications\CheckoutAccessoryNotification;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
@ -40,7 +42,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$response = $this->actingAs(User::factory()->viewAccessories()->checkoutAccessories()->create())
|
$response = $this->actingAs(User::factory()->viewAccessories()->checkoutAccessories()->create())
|
||||||
->from(route('accessories.checkout.show', $accessory))
|
->from(route('accessories.checkout.show', $accessory))
|
||||||
->post(route('accessories.checkout.store', $accessory), [
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
'assigned_to' => User::factory()->create()->id,
|
'assigned_user' => User::factory()->create()->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
])
|
])
|
||||||
->assertStatus(302)
|
->assertStatus(302)
|
||||||
->assertSessionHas('errors')
|
->assertSessionHas('errors')
|
||||||
|
@ -56,11 +59,12 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
|
|
||||||
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||||
->post(route('accessories.checkout.store', $accessory), [
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'note' => 'oh hi there',
|
'note' => 'oh hi there',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertTrue($accessory->users->contains($user));
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
|
|
||||||
$this->assertDatabaseHas('action_logs', [
|
$this->assertDatabaseHas('action_logs', [
|
||||||
'action_type' => 'checkout',
|
'action_type' => 'checkout',
|
||||||
|
@ -80,12 +84,13 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||||
->from(route('accessories.checkout.show', $accessory))
|
->from(route('accessories.checkout.show', $accessory))
|
||||||
->post(route('accessories.checkout.store', $accessory), [
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'checkout_qty' => 3,
|
'checkout_qty' => 3,
|
||||||
'note' => 'oh hi there',
|
'note' => 'oh hi there',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertTrue($accessory->users->contains($user));
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', User::class)->where('assigned_to', $user->id)->count() > 0);
|
||||||
|
|
||||||
$this->assertDatabaseHas('action_logs', [
|
$this->assertDatabaseHas('action_logs', [
|
||||||
'action_type' => 'checkout',
|
'action_type' => 'checkout',
|
||||||
|
@ -97,6 +102,58 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAccessoryCanBeCheckedOutToLocationWithQuantity()
|
||||||
|
{
|
||||||
|
$accessory = Accessory::factory()->create(['qty'=>5]);
|
||||||
|
$location = Location::factory()->create();
|
||||||
|
|
||||||
|
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||||
|
->from(route('accessories.checkout.show', $accessory))
|
||||||
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
|
'assigned_location' => $location->id,
|
||||||
|
'checkout_to_type' => 'location',
|
||||||
|
'checkout_qty' => 3,
|
||||||
|
'note' => 'oh hi there',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', Location::class)->where('assigned_to', $location->id)->count() > 0);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('action_logs', [
|
||||||
|
'action_type' => 'checkout',
|
||||||
|
'target_id' => $location->id,
|
||||||
|
'target_type' => Location::class,
|
||||||
|
'item_id' => $accessory->id,
|
||||||
|
'item_type' => Accessory::class,
|
||||||
|
'note' => 'oh hi there',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAccessoryCanBeCheckedOutToAssetWithQuantity()
|
||||||
|
{
|
||||||
|
$accessory = Accessory::factory()->create(['qty'=>5]);
|
||||||
|
$asset = Asset::factory()->create();
|
||||||
|
|
||||||
|
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||||
|
->from(route('accessories.checkout.show', $accessory))
|
||||||
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
|
'assigned_asset' => $asset->id,
|
||||||
|
'checkout_to_type' => 'asset',
|
||||||
|
'checkout_qty' => 3,
|
||||||
|
'note' => 'oh hi there',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($accessory->checkouts()->where('assigned_type', Asset::class)->where('assigned_to', $asset->id)->count() > 0);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('action_logs', [
|
||||||
|
'action_type' => 'checkout',
|
||||||
|
'target_id' => $asset->id,
|
||||||
|
'target_type' => Asset::class,
|
||||||
|
'item_id' => $accessory->id,
|
||||||
|
'item_type' => Accessory::class,
|
||||||
|
'note' => 'oh hi there',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function testUserSentNotificationUponCheckout()
|
public function testUserSentNotificationUponCheckout()
|
||||||
{
|
{
|
||||||
Notification::fake();
|
Notification::fake();
|
||||||
|
@ -107,7 +164,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||||
->from(route('accessories.checkout.show', $accessory))
|
->from(route('accessories.checkout.show', $accessory))
|
||||||
->post(route('accessories.checkout.store', $accessory), [
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
|
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
|
||||||
|
@ -122,7 +180,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$this->actingAs($actor)
|
$this->actingAs($actor)
|
||||||
->from(route('accessories.checkout.show', $accessory))
|
->from(route('accessories.checkout.show', $accessory))
|
||||||
->post(route('accessories.checkout.store', $accessory), [
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'note' => 'oh hi there',
|
'note' => 'oh hi there',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -148,7 +207,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$this->actingAs(User::factory()->admin()->create())
|
$this->actingAs(User::factory()->admin()->create())
|
||||||
->from(route('accessories.index'))
|
->from(route('accessories.index'))
|
||||||
->post(route('accessories.checkout.store', $accessory), [
|
->post(route('accessories.checkout.store', $accessory), [
|
||||||
'assigned_to' => User::factory()->create()->id,
|
'assigned_user' => User::factory()->create()->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'redirect_option' => 'index',
|
'redirect_option' => 'index',
|
||||||
'assigned_qty' => 1,
|
'assigned_qty' => 1,
|
||||||
])
|
])
|
||||||
|
@ -163,7 +223,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$this->actingAs(User::factory()->admin()->create())
|
$this->actingAs(User::factory()->admin()->create())
|
||||||
->from(route('accessories.index'))
|
->from(route('accessories.index'))
|
||||||
->post(route('accessories.checkout.store' , $accessory), [
|
->post(route('accessories.checkout.store' , $accessory), [
|
||||||
'assigned_to' => User::factory()->create()->id,
|
'assigned_user' => User::factory()->create()->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'redirect_option' => 'item',
|
'redirect_option' => 'item',
|
||||||
'assigned_qty' => 1,
|
'assigned_qty' => 1,
|
||||||
])
|
])
|
||||||
|
@ -180,7 +241,8 @@ class AccessoryCheckoutTest extends TestCase
|
||||||
$this->actingAs(User::factory()->admin()->create())
|
$this->actingAs(User::factory()->admin()->create())
|
||||||
->from(route('accessories.index'))
|
->from(route('accessories.index'))
|
||||||
->post(route('accessories.checkout.store' , $accessory), [
|
->post(route('accessories.checkout.store' , $accessory), [
|
||||||
'assigned_to' => $user->id,
|
'assigned_user' => $user->id,
|
||||||
|
'checkout_to_type' => 'user',
|
||||||
'redirect_option' => 'target',
|
'redirect_option' => 'target',
|
||||||
'assigned_qty' => 1,
|
'assigned_qty' => 1,
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in a new issue