mirror of
https://github.com/snipe/snipe-it.git
synced 2024-11-09 23:24:06 -08:00
added options to checkout accessoires to locations and assets
Added #14979: add checkout functionality to accessoires
This commit is contained in:
parent
c3a296c19f
commit
b18baf74d2
|
@ -92,7 +92,7 @@ class SQLStreamer {
|
|||
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
|
||||
|
||||
$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'
|
||||
foreach($check_tables as $check_table => $_ignore) {
|
||||
foreach ($parser->tablenames as $tablename => $_count) {
|
||||
|
|
|
@ -721,7 +721,7 @@ class Helper
|
|||
{
|
||||
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
||||
$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();
|
||||
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
||||
$licenses = License::where('min_amt', '>', 0)->get();
|
||||
|
@ -749,7 +749,7 @@ class Helper
|
|||
}
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
$avail = $accessory->qty - $accessory->users_count;
|
||||
$avail = $accessory->qty - $accessory->checkouts_count;
|
||||
if ($avail < ($accessory->min_amt) + $alert_threshold) {
|
||||
if ($accessory->qty > 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
|
||||
{
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) {
|
||||
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
"qty" => "required|numeric|min:$accessory->users_count"
|
||||
"qty" => "required|numeric|min:$accessory->checkouts_count"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
|
@ -233,7 +233,7 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
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);
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
|||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
@ -24,7 +25,7 @@ class AccessoryCheckinController extends Controller
|
|||
*/
|
||||
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'));
|
||||
}
|
||||
|
||||
|
@ -39,16 +40,16 @@ class AccessoryCheckinController extends Controller
|
|||
*
|
||||
* @uses Accessory::checkin_email() to determine if an email can and should be sent
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param null $accessoryUserId
|
||||
* @param null $accessoryCheckoutId
|
||||
* @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'));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
|
@ -59,10 +60,8 @@ class AccessoryCheckinController extends Controller
|
|||
}
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
$return_to = e($accessory_user->assigned_to);
|
||||
|
||||
event(new CheckoutableCheckedIn($accessory, User::find($return_to), auth()->user(), $request->input('note'), $checkin_at));
|
||||
if ($accessory_checkout->delete()) {
|
||||
event(new CheckoutableCheckedIn($accessory, $accessory_checkout->assignedTo, auth()->user(), $request->input('note'), $checkin_at));
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
|
|
|
@ -4,9 +4,11 @@ namespace App\Http\Controllers\Accessories;
|
|||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -16,6 +18,9 @@ use \Illuminate\Http\RedirectResponse;
|
|||
|
||||
class AccessoryCheckoutController extends Controller
|
||||
{
|
||||
|
||||
use CheckInOutRequest;
|
||||
|
||||
/**
|
||||
* Return the form to checkout an Accessory to a user.
|
||||
*
|
||||
|
@ -25,7 +30,7 @@ class AccessoryCheckoutController extends Controller
|
|||
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);
|
||||
|
||||
|
@ -58,30 +63,32 @@ class AccessoryCheckoutController extends Controller
|
|||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param int $accessory
|
||||
* @param Accessory $accessory
|
||||
*/
|
||||
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
|
||||
{
|
||||
|
||||
|
||||
$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++) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
AccessoryCheckout::create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->input('assigned_to'),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'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
|
||||
$request->request->add(['checkout_to_type' => 'user']);
|
||||
$request->request->add(['assigned_user' => $user->id]);
|
||||
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
|
||||
$request->request->add(['assigned_user' => $target->id]);
|
||||
|
||||
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\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||
use App\Http\Requests\StoreAccessoryRequest;
|
||||
|
@ -17,10 +18,12 @@ use Carbon\Carbon;
|
|||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
use App\Models\AccessoryCheckout;
|
||||
|
||||
class AccessoriesController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -48,13 +51,13 @@ class AccessoriesController extends Controller
|
|||
'min_amt',
|
||||
'company_id',
|
||||
'notes',
|
||||
'users_count',
|
||||
'checkouts_count',
|
||||
'qty',
|
||||
];
|
||||
|
||||
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier')
|
||||
->withCount('users as users_count');
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier')
|
||||
->withCount('checkouts as checkouts_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
|
@ -154,7 +157,7 @@ class AccessoriesController extends Controller
|
|||
public function show($id)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
@ -197,28 +200,23 @@ class AccessoriesController extends Controller
|
|||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$accessory_users = $accessory->users;
|
||||
$total = $accessory_users->count();
|
||||
$accessory_checkouts = $accessory->checkouts;
|
||||
$total = $accessory_checkouts->count();
|
||||
|
||||
if ($total < $offset) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$accessory_users = $accessory->users()->skip($offset)->take($limit)->get();
|
||||
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessory_users = $accessory->users()
|
||||
->where(function ($query) use ($request) {
|
||||
$search_str = '%' . $request->input('search') . '%';
|
||||
$query->where('first_name', 'like', $search_str)
|
||||
->orWhere('last_name', 'like', $search_str)
|
||||
->orWhere('note', 'like', $search_str);
|
||||
})
|
||||
|
||||
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
|
||||
->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)
|
||||
{
|
||||
$this->authorize('checkout', $accessory);
|
||||
$accessory->assigned_to = $request->input('assigned_to');
|
||||
$user = User::find($request->input('assigned_to'));
|
||||
$target = $this->determineCheckoutTarget();
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
AccessoryCheckout::create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->input('assigned_to'),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
}
|
||||
|
||||
// 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')));
|
||||
|
||||
|
@ -316,19 +314,19 @@ class AccessoriesController extends Controller
|
|||
*/
|
||||
public function checkin(Request $request, $accessoryUserId = null)
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_checkout = DB::table('accessories_checkout')->find($accessoryUserId))) {
|
||||
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);
|
||||
|
||||
$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?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
if (! is_null($accessory_user->assigned_to)) {
|
||||
$user = User::find($accessory_user->assigned_to);
|
||||
if (DB::table('accessories_checkout')->where('id', '=', $accessory_checkout->id)->delete()) {
|
||||
if (! is_null($accessory_checkout->assigned_to)) {
|
||||
$user = User::find($accessory_checkout->assigned_to);
|
||||
}
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
|
|
|
@ -219,7 +219,7 @@ class BulkUsersController extends Controller
|
|||
|
||||
$users = User::whereIn('id', $user_raw_array)->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();
|
||||
$consumables = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
|
||||
|
|
|
@ -44,13 +44,10 @@ class AccessoryCheckoutRequest extends ImageUploadRequest
|
|||
|
||||
return array_merge(
|
||||
[
|
||||
'assigned_to' => [
|
||||
'required',
|
||||
'integer',
|
||||
'exists:users,id,deleted_at,NULL',
|
||||
'not_array'
|
||||
],
|
||||
|
||||
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
|
||||
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
|
||||
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
|
||||
|
||||
'number_remaining_after_checkout' => [
|
||||
'min:0',
|
||||
'required',
|
||||
|
|
|
@ -39,7 +39,7 @@ class AccessoriesTransformer
|
|||
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||
'remaining_qty' => (int) $accessory->numRemaining(),
|
||||
'users_count' => $accessory->users_count,
|
||||
'checkouts_count' => $accessory->checkouts_count,
|
||||
|
||||
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
|
||||
|
@ -66,27 +66,42 @@ class AccessoriesTransformer
|
|||
return $array;
|
||||
}
|
||||
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_users, $total)
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach ($accessory_users as $user) {
|
||||
foreach ($accessory_checkouts as $checkout) {
|
||||
$array[] = [
|
||||
|
||||
'assigned_pivot_id' => $user->pivot->id,
|
||||
'id' => (int) $user->id,
|
||||
'username' => e($user->username),
|
||||
'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',
|
||||
'id' => $checkout->id,
|
||||
'assigned_to' => $this->transformAssignedTo($checkout),
|
||||
'checkout_notes' => e($checkout->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
|
||||
'available_actions' => ['checkin' => true],
|
||||
];
|
||||
}
|
||||
|
||||
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();
|
||||
foreach ($accessories_users as $user) {
|
||||
foreach ($accessories_checkout as $user) {
|
||||
$array[] = (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
|
|
|
@ -253,9 +253,10 @@ class Accessory extends SnipeModel
|
|||
* @since [v3.0]
|
||||
* @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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
return $this->users_count ?? $this->users()->count();
|
||||
return $this->checkouts_count ?? $this->checkouts()->count();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check how many items of an accessory remain.
|
||||
*
|
||||
* In order to use this model method, you MUST call withCount('users as users_count')
|
||||
* on the eloquent query in the controller, otherwise $this->users_count will be null and
|
||||
* In order to use this model method, you MUST call withCount('checkouts as checkouts_count')
|
||||
* on the eloquent query in the controller, otherwise $this->checkouts_count will be null and
|
||||
* bad things happen.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
|
@ -370,12 +373,12 @@ class Accessory extends SnipeModel
|
|||
*/
|
||||
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
|
||||
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()
|
||||
{
|
||||
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');
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ class AccessoryPresenter extends Presenter
|
|||
'visible' => false,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
],[
|
||||
'field' => 'users_count',
|
||||
'field' => 'checkouts_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Category;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
|
@ -125,11 +126,12 @@ class AccessoryFactory extends Factory
|
|||
})->afterCreating(function ($accessory) {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => now(),
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => $user->id,
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_type' => User::class,
|
||||
'note' => '',
|
||||
]);
|
||||
});
|
||||
|
@ -145,11 +147,12 @@ class AccessoryFactory extends Factory
|
|||
public function checkedOutToUser(User $user = null)
|
||||
{
|
||||
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => 1,
|
||||
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
||||
'assigned_type' => User::class,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?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::rename('accessories_checkout', 'accessories_users');
|
||||
}
|
||||
}
|
||||
};
|
|
@ -66,7 +66,14 @@
|
|||
</div>
|
||||
<!-- 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 -->
|
||||
<div class="form-group {{ $errors->has('checkout_qty') ? 'error' : '' }} ">
|
||||
|
|
|
@ -68,9 +68,9 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table
|
||||
data-cookie-id-table="usersTable"
|
||||
data-cookie-id-table="checkoutsTable"
|
||||
data-pagination="true"
|
||||
data-id-table="usersTable"
|
||||
data-id-table="checkoutsTable"
|
||||
data-search="true"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
|
@ -78,16 +78,16 @@
|
|||
data-show-export="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
id="usersTable"
|
||||
id="checkoutsTable"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.accessories.checkedout', $accessory->id) }}"
|
||||
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"]
|
||||
}'>
|
||||
<thead>
|
||||
<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-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>
|
||||
|
@ -314,7 +314,7 @@
|
|||
<strong>{{ trans('general.checked_out') }}</strong>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ $accessory->users_count }}
|
||||
{{ $accessory->checkouts_count }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -337,7 +337,7 @@
|
|||
@endcan
|
||||
|
||||
@can('delete', $accessory)
|
||||
@if ($accessory->users_count == 0)
|
||||
@if ($accessory->checkouts_count == 0)
|
||||
<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">
|
||||
{{ trans('general.delete') }}
|
||||
|
|
|
@ -17,7 +17,7 @@ class AccessoryCheckinTest extends TestCase
|
|||
$accessory = Accessory::factory()->checkedOutToUser()->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();
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,12 @@ class AccessoryCheckinTest extends TestCase
|
|||
$user = User::factory()->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())
|
||||
->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);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class AccessoryCheckoutTest extends TestCase
|
|||
{
|
||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||
->postJson(route('api.accessories.checkout', Accessory::factory()->create()), [
|
||||
// missing assigned_to
|
||||
// missing assigned_user, assigned_location, assigned_asset
|
||||
])
|
||||
->assertStatusMessageIs('error');
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
{
|
||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->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()
|
||||
->assertStatusMessageIs('error')
|
||||
|
@ -65,7 +66,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
|
||||
$this->actingAsForApi($admin)
|
||||
->postJson(route('api.accessories.checkout', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user'
|
||||
])
|
||||
->assertOk()
|
||||
->assertStatusMessageIs('success')
|
||||
|
@ -73,7 +75,7 @@ class AccessoryCheckoutTest extends TestCase
|
|||
->assertJson(['messages' => trans('admin/accessories/message.checkout.success')])
|
||||
->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(
|
||||
1,
|
||||
|
@ -96,7 +98,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
|
||||
$this->actingAsForApi($admin)
|
||||
->postJson(route('api.accessories.checkout', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
'checkout_qty' => 2,
|
||||
])
|
||||
->assertOk()
|
||||
|
@ -105,7 +108,7 @@ class AccessoryCheckoutTest extends TestCase
|
|||
->assertJson(['messages' => trans('admin/accessories/message.checkout.success')])
|
||||
->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(
|
||||
1,
|
||||
|
@ -128,7 +131,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
|
||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||
->postJson(route('api.accessories.checkout', $accessory), [
|
||||
'assigned_to' => 'invalid-user-id',
|
||||
'assigned_user' => 'invalid-user-id',
|
||||
'checkout_to_type' => 'user',
|
||||
'note' => 'oh hi there',
|
||||
])
|
||||
->assertOk()
|
||||
|
@ -136,7 +140,7 @@ class AccessoryCheckoutTest extends TestCase
|
|||
->assertStatus(200)
|
||||
->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()
|
||||
|
@ -148,7 +152,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
|
||||
$this->actingAsForApi(User::factory()->checkoutAccessories()->create())
|
||||
->postJson(route('api.accessories.checkout', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
]);
|
||||
|
||||
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
|
||||
|
@ -162,7 +167,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
|
||||
$this->actingAsForApi($actor)
|
||||
->postJson(route('api.accessories.checkout', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
'note' => 'oh hi there',
|
||||
]);
|
||||
|
||||
|
|
|
@ -40,7 +40,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
$response = $this->actingAs(User::factory()->viewAccessories()->checkoutAccessories()->create())
|
||||
->from(route('accessories.checkout.show', $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)
|
||||
->assertSessionHas('errors')
|
||||
|
@ -56,11 +57,12 @@ class AccessoryCheckoutTest extends TestCase
|
|||
|
||||
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||
->post(route('accessories.checkout.store', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
'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', [
|
||||
'action_type' => 'checkout',
|
||||
|
@ -80,12 +82,13 @@ class AccessoryCheckoutTest extends TestCase
|
|||
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||
->from(route('accessories.checkout.show', $accessory))
|
||||
->post(route('accessories.checkout.store', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
'checkout_qty' => 3,
|
||||
'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', [
|
||||
'action_type' => 'checkout',
|
||||
|
@ -107,7 +110,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
$this->actingAs(User::factory()->checkoutAccessories()->create())
|
||||
->from(route('accessories.checkout.show', $accessory))
|
||||
->post(route('accessories.checkout.store', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
]);
|
||||
|
||||
Notification::assertSentTo($user, CheckoutAccessoryNotification::class);
|
||||
|
@ -122,7 +126,8 @@ class AccessoryCheckoutTest extends TestCase
|
|||
$this->actingAs($actor)
|
||||
->from(route('accessories.checkout.show', $accessory))
|
||||
->post(route('accessories.checkout.store', $accessory), [
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_user' => $user->id,
|
||||
'checkout_to_type' => 'user',
|
||||
'note' => 'oh hi there',
|
||||
]);
|
||||
|
||||
|
|
Loading…
Reference in a new issue