Merge pull request #15973 from uberbrady/next_try_accessories_to_locations_rebased
Some checks failed
Crowdin Action / upload-sources-to-crowdin (push) Has been cancelled
Docker images (Alpine) / docker (push) Has been cancelled
Docker images / docker (push) Has been cancelled
Tests in MySQL / PHP ${{ matrix.php-version }} (8.1) (push) Has been cancelled
Tests in MySQL / PHP ${{ matrix.php-version }} (8.2) (push) Has been cancelled
Tests in MySQL / PHP ${{ matrix.php-version }} (8.3) (push) Has been cancelled
Tests in SQLite / PHP ${{ matrix.php-version }} (8.1.1) (push) Has been cancelled

Next try accessories to locations rebased
This commit is contained in:
snipe 2024-12-16 20:25:25 +00:00 committed by GitHub
commit 04c3481734
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 861 additions and 306 deletions

View file

@ -75,20 +75,23 @@ class AccessoryCheckoutController extends Controller
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
AccessoryCheckout::create([
$accessory_checkout = new AccessoryCheckout([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'created_by' => auth()->id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
}
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' => request('checkout_to_type')]);
$request->request->add(['assigned_user' => $target->id]);
$request->request->add(['assigned_to' => $target->id]);
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);

View file

@ -13,6 +13,7 @@ use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\Company;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
@ -184,39 +185,33 @@ class AccessoriesController extends Controller
/**
* Display the specified resource.
* Get the list of checkouts for a specific accessory
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
* @return | array
*/
public function checkedout($id, Request $request)
public function checkedout(Request $request, $id)
{
$this->authorize('view', Accessory::class);
$accessory = Accessory::with('lastCheckout')->findOrFail($id);
$offset = request('offset', 0);
$limit = request('limit', 50);
$accessory_checkouts = $accessory->checkouts;
$total = $accessory_checkouts->count();
if ($total < $offset) {
$offset = 0;
}
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
// Total count of all checkouts for this asset
$accessory_checkouts = $accessory->checkouts();
// Check for search text in the request
if ($request->filled('search')) {
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
->get();
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->TextSearch($request->input('search'));
}
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory_checkouts, $total);
}
@ -227,7 +222,7 @@ class AccessoriesController extends Controller
* @since [v4.0]
* @param \App\Http\Requests\ImageUploadRequest $request
* @param int $id
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function update(ImageUploadRequest $request, $id)
{
@ -249,7 +244,7 @@ class AccessoriesController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @param int $id
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($id)
{
@ -284,14 +279,17 @@ class AccessoriesController extends Controller
$accessory->checkout_qty = $request->input('checkout_qty', 1);
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
AccessoryCheckout::create([
$accessory_checkout = new AccessoryCheckout([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'created_by' => auth()->id(),
'assigned_to' => $target->id,
'assigned_type' => $target::class,
'note' => $request->input('note'),
]);
$accessory_checkout->created_by = auth()->id();
$accessory_checkout->save();
}
// Set this value to be able to pass the qty through to the event

View file

@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use App\Http\Requests\UpdateAssetRequest;
use App\Http\Traits\MigratesLegacyAssetLocations;
use App\Models\AccessoryCheckout;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use Illuminate\Database\Eloquent\Builder;
@ -26,11 +27,9 @@ use App\Models\License;
use App\Models\Location;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use App\View\Label;
@ -129,6 +128,7 @@ class AssetsController extends Controller
$assets = Asset::select('assets.*')
->with(
'model',
'location',
'assetstatus',
'company',
@ -140,7 +140,7 @@ class AssetsController extends Controller
'model.manufacturer',
'model.fieldset',
'supplier'
); //it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
); // it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.
if ($filter_non_deprecable_assets) {
@ -1214,6 +1214,27 @@ class AssetsController extends Controller
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
public function assignedAssets(Request $request, Asset $asset) : JsonResponse | array
{
return [];
// to do
}
public function assignedAccessories(Request $request, Asset $asset) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $asset);
$accessory_checkouts = AccessoryCheckout::AssetsAssigned()->with('adminuser')->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new AssetsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
/**
* Generate asset labels by tag
*

View file

@ -3,17 +3,20 @@
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Controllers\Controller;
use App\Http\Requests\ImageUploadRequest;
use App\Http\Transformers\AccessoriesTransformer;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\LocationsTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Asset;
use App\Models\Location;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Http\JsonResponse;
class LocationsController extends Controller
{
@ -28,26 +31,28 @@ class LocationsController extends Controller
{
$this->authorize('view', Location::class);
$allowed_columns = [
'id',
'name',
'accessories_count',
'address',
'address2',
'assets_count',
'assets_count',
'assigned_accessories_count',
'assigned_assets_count',
'assigned_assets_count',
'city',
'state',
'country',
'zip',
'created_at',
'updated_at',
'manager_id',
'image',
'assigned_assets_count',
'users_count',
'assets_count',
'assigned_assets_count',
'assets_count',
'rtd_assets_count',
'currency',
'id',
'image',
'ldap_ou',
'manager_id',
'name',
'rtd_assets_count',
'state',
'updated_at',
'users_count',
'zip',
];
$locations = Location::with('parent', 'manager', 'children')->select([
@ -68,8 +73,11 @@ class LocationsController extends Controller
'locations.image',
'locations.ldap_ou',
'locations.currency',
])->withCount('assignedAssets as assigned_assets_count')
])
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('assignedAccessories as assigned_accessories_count')
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count');
@ -224,7 +232,17 @@ class LocationsController extends Controller
return response()->json(Helper::formatStandardApiResponse('error', null, $location->getErrors()));
}
public function assets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
$assets = Asset::where('location_id', '=', $location->id)->with('model', 'model.category', 'assetstatus', 'location', 'company', 'defaultLoc');
$assets = $assets->get();
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
public function assignedAssets(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Asset::class);
$this->authorize('view', $location);
@ -233,6 +251,20 @@ class LocationsController extends Controller
return (new AssetsTransformer)->transformAssets($assets, $assets->count(), $request);
}
public function assignedAccessories(Request $request, Location $location) : JsonResponse | array
{
$this->authorize('view', Accessory::class);
$this->authorize('view', $location);
$accessory_checkouts = AccessoryCheckout::LocationAssigned()->with('adminuser')->with('accessories');
$offset = ($request->input('offset') > $accessory_checkouts->count()) ? $accessory_checkouts->count() : app('api_offset_value');
$limit = app('api_limit_value');
$total = $accessory_checkouts->count();
$accessory_checkouts = $accessory_checkouts->skip($offset)->take($limit)->get();
return (new LocationsTransformer)->transformCheckedoutAccessories($accessory_checkouts, $total);
}
/**
* Remove the specified resource from storage.
*

View file

@ -104,7 +104,7 @@ class CustomFieldsController extends Controller
"auto_add_to_fieldsets" => $request->get("auto_add_to_fieldsets", 0),
"show_in_listview" => $request->get("show_in_listview", 0),
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
"user_id" => auth()->id()
"created_by" => auth()->id()
]);

View file

@ -69,7 +69,7 @@ class AccessoriesTransformer
return $array;
}
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
public function transformCheckedoutAccessory($accessory_checkouts, $total)
{
$array = [];
@ -77,9 +77,13 @@ class AccessoriesTransformer
$array[] = [
'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],
'note' => $checkout->note ? e($checkout->note) : null,
'created_by' => $checkout->adminuser ? [
'id' => (int) $checkout->adminuser->id,
'name'=> e($checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
'available_actions' => Gate::allows('checkout', Accessory::class) ? ['checkin' => true] : ['checkin' => false],
];
}
@ -89,22 +93,11 @@ class AccessoriesTransformer
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 (new UsersTransformer)->transformUserCompact($accessoryCheckout->assigned);
} elseif ($accessoryCheckout->checkedOutToLocation()) {
return (new LocationsTransformer())->transformLocationCompact($accessoryCheckout->assigned);
} elseif ($accessoryCheckout->checkedOutToAsset()) {
return (new AssetsTransformer())->transformAssetCompact($accessoryCheckout->assigned);
}
return $accessoryCheckout->assigned ? [
'id' => $accessoryCheckout->assigned->id,
'name' => e($accessoryCheckout->assigned->display_name),
'type' => $accessoryCheckout->assignedType(),
] : null;
}
}

View file

@ -3,12 +3,14 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Asset;
use App\Models\Setting;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class AssetsTransformer
{
@ -225,7 +227,7 @@ class AssetsTransformer
public function transformRequestedAsset(Asset $asset)
{
$array = [
'id' => (int) $asset->id,
'id' => (int)$asset->id,
'name' => e($asset->name),
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
@ -234,7 +236,7 @@ class AssetsTransformer
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
'location' => ($asset->location) ? e($asset->location->name) : null,
'status'=> ($asset->assetstatus) ? $asset->present()->statusMeta : null,
'status' => ($asset->assetstatus) ? $asset->present()->statusMeta : null,
'assigned_to_self' => ($asset->assigned_to == auth()->id()),
];
@ -244,7 +246,7 @@ class AssetsTransformer
foreach ($asset->model->fieldset->fields as $field) {
// Only display this if it's allowed via the custom field setting
if (($field->field_encrypted=='0') && ($field->show_in_requestable_list=='1')) {
if (($field->field_encrypted == '0') && ($field->show_in_requestable_list == '1')) {
$value = $asset->{$field->db_column};
if (($field->format == 'DATE') && (!is_null($value)) && ($value != '')) {
@ -268,7 +270,61 @@ class AssetsTransformer
$array += $permissions_array;
return $array;
}
public function transformAssetCompact(Asset $asset)
{
$array = [
'id' => (int) $asset->id,
'image' => ($asset->getImageUrl()) ? $asset->getImageUrl() : null,
'type' => 'asset',
'name' => e($asset->present()->fullName()),
'model' => ($asset->model) ? e($asset->model->name) : null,
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
'asset_tag' => e($asset->asset_tag),
'serial' => e($asset->serial),
];
return $array;
}
public function transformCheckedoutAccessories($accessory_checkouts, $total)
{
$array = [];
foreach ($accessory_checkouts as $checkout) {
$array[] = self::transformCheckedoutAccessory($checkout);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
{
$array = [
'id' => $accessory_checkout->id,
'accessory' => [
'id' => $accessory_checkout->accessory->id,
'name' => $accessory_checkout->accessory->name,
],
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
'created_by' => $accessory_checkout->adminuser ? [
'id' => (int) $accessory_checkout->adminuser->id,
'name'=> e($accessory_checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'checkout' => false,
'checkin' => Gate::allows('checkin', Accessory::class),
];
$array += $permissions_array;
return $array;
}
}

View file

@ -3,6 +3,8 @@
namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Location;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
@ -45,6 +47,8 @@ class LocationsTransformer
'zip' => ($location->zip) ? e($location->zip) : null,
'phone' => ($location->phone!='') ? e($location->phone): null,
'fax' => ($location->fax!='') ? e($location->fax): null,
'accessories_count' => (int) $location->accessories_count,
'assigned_accessories_count' => (int) $location->assigned_accessories_count,
'assigned_assets_count' => (int) $location->assigned_assets_count,
'assets_count' => (int) $location->assets_count,
'rtd_assets_count' => (int) $location->rtd_assets_count,
@ -76,4 +80,75 @@ class LocationsTransformer
return $array;
}
}
}
public function transformCheckedoutAccessories($accessory_checkouts, $total)
{
$array = [];
foreach ($accessory_checkouts as $checkout) {
$array[] = self::transformCheckedoutAccessory($checkout);
}
return (new DatatablesTransformer)->transformDatatables($array, $total);
}
public function transformCheckedoutAccessory(AccessoryCheckout $accessory_checkout)
{
$array = [
'id' => $accessory_checkout->id,
'accessory' => [
'id' => $accessory_checkout->accessory->id,
'name' => $accessory_checkout->accessory->name,
],
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
'created_by' => $accessory_checkout->adminuser ? [
'id' => (int) $accessory_checkout->adminuser->id,
'name'=> e($accessory_checkout->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($accessory_checkout->created_at, 'datetime'),
];
$permissions_array['available_actions'] = [
'checkout' => false,
'checkin' => Gate::allows('checkin', Accessory::class),
];
$array += $permissions_array;
return $array;
}
/**
* This gives a compact view of the location data without any additional relational queries,
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
* have not been easy/lazy loaded already
*
* @param Location $location
* @return array
* @throws \Exception
*/
public function transformLocationCompact(Location $location = null)
{
if ($location) {
$array = [
'id' => (int) $location->id,
'image' => ($location->image) ? Storage::disk('public')->url('locations/'.e($location->image)) : null,
'type' => "location",
'name' => e($location->name),
'created_by' => $location->adminuser ? [
'id' => (int) $location->adminuser->id,
'name'=> e($location->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($location->created_at, 'datetime'),
];
return $array;
}
}
}

View file

@ -4,8 +4,8 @@ namespace App\Http\Transformers;
use App\Helpers\Helper;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Gate;
class UsersTransformer
{
@ -106,6 +106,37 @@ class UsersTransformer
return $array;
}
/**
* This gives a compact view of the user data without any additional relational queries,
* allowing us to 1) deliver a smaller payload and 2) avoid additional queries on relations that
* have not been easy/lazy loaded already
*
* @param User $user
* @return array
* @throws \Exception
*/
public function transformUserCompact(User $user) : array
{
$array = [
'id' => (int) $user->id,
'image' => e($user->present()->gravatar) ?? null,
'type' => 'user',
'name' => e($user->getFullNameAttribute()),
'first_name' => e($user->first_name),
'last_name' => e($user->last_name),
'username' => e($user->username),
'created_by' => $user->adminuser ? [
'id' => (int) $user->adminuser->id,
'name'=> e($user->adminuser->present()->fullName),
]: null,
'created_at' => Helper::getFormattedDateObject($user->created_at, 'datetime'),
'deleted_at' => ($user->deleted_at) ? Helper::getFormattedDateObject($user->deleted_at, 'datetime') : null,
];
return $array;
}
public function transformUsersDatatable($users)
{
return (new DatatablesTransformer)->transformDatatables($users);

View file

@ -22,7 +22,14 @@ class AccessoryCheckout extends Model
{
use Searchable;
protected $fillable = ['created_by', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
protected $fillable = [
'accessory_id',
'assigned_to',
'assigned_type',
'note'
];
protected $presenter = \App\Presenters\AccessoryPresenter::class;
protected $table = 'accessories_checkout';
/**
@ -34,9 +41,13 @@ class AccessoryCheckout extends Model
*/
public function accessory()
{
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
return $this->hasOne(Accessory::class, 'id', 'accessory_id');
}
public function accessories()
{
return $this->hasMany(Accessory::class, 'id', 'accessory_id');
}
/**
* Establishes the accessory checkout -> user relationship
*
@ -44,9 +55,9 @@ class AccessoryCheckout extends Model
* @since [v7.0.9]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function user()
public function adminuser()
{
return $this->hasOne(\App\Models\User::class, 'user_id');
return $this->hasOne(\App\Models\User::class, 'created_by');
}
/**
@ -76,7 +87,7 @@ class AccessoryCheckout extends Model
/**
* Determines whether the accessory is checked out to a user
*
* Even though we allow allow for checkout to things beyond users
* Even though we 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]
@ -84,7 +95,17 @@ class AccessoryCheckout extends Model
*/
public function checkedOutToUser(): bool
{
return $this->assignedType() === Asset::USER;
return $this->assigned_type == User::class;
}
public function checkedOutToLocation(): bool
{
return $this->assigned_type == Location::class;
}
public function checkedOutToAsset(): bool
{
return $this->assigned_type == Asset::class;
}
public function scopeUserAssigned(Builder $query): void
@ -92,6 +113,16 @@ class AccessoryCheckout extends Model
$query->where('assigned_type', '=', User::class);
}
public function scopeLocationAssigned(Builder $query): void
{
$query->where('assigned_type', '=', Location::class);
}
public function scopeAssetAssigned(Builder $query): void
{
$query->where('assigned_type', '=', Asset::class);
}
/**
* Run additional, advanced searches.
*

View file

@ -253,6 +253,18 @@ class Location extends SnipeModel
return $this->morphMany(\App\Models\Asset::class, 'assigned', 'assigned_type', 'assigned_to')->withTrashed();
}
/**
* Establishes the accessory -> location assignment relationship
*
* @author A. Gianotto <snipe@snipe.net>
* @since [v3.0]
* @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function assignedAccessories()
{
return $this->morphMany(\App\Models\AccessoryCheckout::class, 'assigned', 'assigned_type', 'assigned_to');
}
public function setLdapOuAttribute($ldap_ou)
{
return $this->attributes['ldap_ou'] = empty($ldap_ou) ? null : $ldap_ou;

View file

@ -333,6 +333,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
public function accessories()
{
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_checkout', 'assigned_to', 'accessory_id')
->where('assigned_type', '=', 'App\Models\User')
->withPivot('id', 'created_at', 'note')->withTrashed()->orderBy('accessory_id');
}

View file

@ -158,7 +158,7 @@ class AccessoryPresenter extends Presenter
'title' => trans('general.change'),
'formatter' => 'accessoriesInOutFormatter',
], [
'field' => 'actions',
'field' => 'available_actions',
'searchable' => false,
'sortable' => false,
'switchable' => false,
@ -170,6 +170,74 @@ class AccessoryPresenter extends Presenter
return json_encode($layout);
}
public static function assignedDataTableLayout()
{
$layout = [
[
'field' => 'id',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.id'),
'visible' => false,
],
[
'field' => 'assigned_to.image',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.image'),
'visible' => true,
'formatter' => 'imageFormatter',
],
[
'field' => 'assigned_to',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.checked_out_to'),
'visible' => true,
'formatter' => 'polymorphicItemFormatter',
],
[
'field' => 'note',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.notes'),
'visible' => true,
],
[
'field' => 'created_at',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('admin/hardware/table.checkout_date'),
'visible' => true,
'formatter' => 'dateDisplayFormatter',
],
[
'field' => 'created_by',
'searchable' => false,
'sortable' => false,
'title' => trans('general.admin'),
'visible' => false,
'formatter' => 'usersLinkObjFormatter',
],
[
'field' => 'available_actions',
'searchable' => false,
'sortable' => false,
'switchable' => false,
'title' => trans('table.actions'),
'formatter' => 'accessoriesInOutFormatter',
],
];
return json_encode($layout);
}
/**
* Pregenerated link to this accessories view page.
* @return string

View file

@ -18,16 +18,14 @@ class LocationPresenter extends Presenter
'field' => 'bulk_selectable',
'checkbox' => true,
'formatter' => 'checkboxEnabledFormatter',
],
[
], [
'field' => 'id',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.id'),
'visible' => false,
],
[
], [
'field' => 'name',
'searchable' => true,
'sortable' => true,
@ -35,8 +33,7 @@ class LocationPresenter extends Presenter
'title' => trans('admin/locations/table.name'),
'visible' => true,
'formatter' => 'locationsLinkFormatter',
],
[
], [
'field' => 'image',
'searchable' => false,
'sortable' => true,
@ -44,8 +41,7 @@ class LocationPresenter extends Presenter
'title' => trans('general.image'),
'visible' => true,
'formatter' => 'imageFormatter',
],
[
], [
'field' => 'parent',
'searchable' => false,
'sortable' => true,
@ -53,100 +49,111 @@ class LocationPresenter extends Presenter
'title' => trans('admin/locations/table.parent'),
'visible' => true,
'formatter' => 'locationsLinkObjFormatter',
],
[
], [
'field' => 'assets_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/message.current_location'),
'visible' => true,
],
[
], [
'field' => 'rtd_assets_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/hardware/form.default_location'),
'titleTooltip' => trans('admin/hardware/form.default_location'),
'tooltip' => 'true',
'visible' => false,
],
[
'class' => 'css-house-flag',
], [
'field' => 'assigned_assets_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/message.assigned_assets'),
'titleTooltip' => trans('admin/locations/message.assigned_assets'),
'visible' => true,
],
[
'class' => 'css-house-laptop',
], [
'field' => 'accessories_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.accessories'),
'titleTooltip' => trans('general.accessories'),
'visible' => true,
'class' => 'css-accessory',
], [
'field' => 'assigned_accessories_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.accessories_assigned'),
'titleTooltip' => trans('general.accessories_assigned'),
'visible' => true,
'class' => 'css-accessory-alt',
], [
'field' => 'users_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.people'),
'titleTooltip' => trans('general.people'),
'visible' => true,
],
[
'class' => 'css-house-user',
// 'data-tooltip' => true, - not working, but I want to try to use regular tooltips here
], [
'field' => 'currency',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('general.currency'),
'visible' => true,
],
[
'class' => 'css-currency',
], [
'field' => 'address',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.address'),
'visible' => true,
],
[
], [
'field' => 'address2',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.address2'),
'visible' => false,
],
[
], [
'field' => 'city',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.city'),
'visible' => true,
],
[
], [
'field' => 'state',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.state'),
'visible' => true,
],
[
], [
'field' => 'zip',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.zip'),
'visible' => false,
],
[
], [
'field' => 'country',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.country'),
'visible' => false,
],
[
], [
'field' => 'phone',
'searchable' => true,
'sortable' => true,
@ -154,8 +161,7 @@ class LocationPresenter extends Presenter
'title' => trans('admin/users/table.phone'),
'visible' => false,
'formatter' => 'phoneFormatter',
],
[
], [
'field' => 'fax',
'searchable' => true,
'sortable' => true,
@ -163,16 +169,14 @@ class LocationPresenter extends Presenter
'title' => trans('admin/suppliers/table.fax'),
'visible' => false,
'formatter' => 'phoneFormatter',
],
[
], [
'field' => 'ldap_ou',
'searchable' => true,
'sortable' => true,
'switchable' => true,
'title' => trans('admin/locations/table.ldap_ou'),
'visible' => false,
],
[
], [
'field' => 'manager',
'searchable' => false,
'sortable' => true,
@ -180,9 +184,7 @@ class LocationPresenter extends Presenter
'title' => trans('admin/users/table.manager'),
'visible' => false,
'formatter' => 'usersLinkObjFormatter',
],
[
], [
'field' => 'created_at',
'searchable' => true,
'sortable' => true,
@ -190,9 +192,7 @@ class LocationPresenter extends Presenter
'title' => trans('general.created_at'),
'visible' => false,
'formatter' => 'dateDisplayFormatter',
],
[
], [
'field' => 'actions',
'searchable' => false,
'sortable' => false,
@ -206,6 +206,73 @@ class LocationPresenter extends Presenter
return json_encode($layout);
}
public static function assignedAccessoriesDataTableLayout()
{
$layout = [
[
'field' => 'id',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.id'),
'visible' => false,
],
[
'field' => 'accessory',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.accessory'),
'visible' => true,
'formatter' => 'accessoriesLinkObjFormatter',
],
[
'field' => 'image',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.image'),
'visible' => true,
'formatter' => 'imageFormatter',
],
[
'field' => 'note',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('general.notes'),
'visible' => true,
],
[
'field' => 'created_at',
'searchable' => false,
'sortable' => false,
'switchable' => true,
'title' => trans('admin/hardware/table.checkout_date'),
'visible' => true,
'formatter' => 'dateDisplayFormatter',
],
[
'field' => 'created_by',
'searchable' => false,
'sortable' => false,
'title' => trans('general.admin'),
'visible' => false,
'formatter' => 'usersLinkObjFormatter',
],
[
'field' => 'available_actions',
'searchable' => false,
'sortable' => false,
'switchable' => false,
'title' => trans('table.actions'),
'formatter' => 'accessoriesInOutFormatter',
],
];
return json_encode($layout);
}
/**
* Link to this locations name
* @return string

View file

@ -163,7 +163,7 @@ class AccessoryFactory extends Factory
$accessory->checkouts()->create([
'accessory_id' => $accessory->id,
'created_at' => Carbon::now(),
'user_id' => 1,
'created_by' => 1,
'assigned_to' => $user->id,
'assigned_type' => User::class,
]);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/css/dist/skins/_all-skins.css.map vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/dist/all.js vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,25 +1,25 @@
{
"/js/build/app.js": "/js/build/app.js?id=65d7af7b9fa7fd0e05737526a0d1d282",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=06c13e817cc022028b3f4a33c0ca303a",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=e71ef4171dee5da63af390966ac60ffc",
"/css/build/overrides.css": "/css/build/overrides.css?id=5b8f0ac249624a9dbe1b6fb6c756dbf6",
"/css/build/app.css": "/css/build/app.css?id=a62a2076217c53ca0206bbb25a233010",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=ea22079836a432d7f46a5d390c445e13",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=44bf834f2110504a793dadec132a5898",
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=05cc02539441b717012c4efc3303192f",
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=6fe68325d5356197672c27bc77cedcb4",
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=fca90dff303e17dc2991ca395c7f7fa8",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=71b7731f7ae692eada36b9b08a2e04a9",
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=bb302302d9566adf783a2b7dc31e840c",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=da6c7997d9de2f8329142399f0ce50da",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=f677207c6cf9678eb539abecb408c374",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=6ea836d8126de101081c49abbdb89417",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/all.css": "/css/dist/all.css?id=f65de09436486bd82fede79db3ffc621",
"/js/build/app.js": "/js/build/app.js?id=6d4d575774a1be993efe0598cc6b1c20",
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=d34ae2483cbe2c77478c45f4006eba55",
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=b1c78591f51b52beab05b52f407ad6e6",
"/css/build/overrides.css": "/css/build/overrides.css?id=10d3f63eef985a3df55e66506af9b23f",
"/css/build/app.css": "/css/build/app.css?id=6e3067fde8016a951134eee87737b831",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=53edc92eb2d272744bc7404ec259930e",
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=cdbb4f543fb448f03617c7ed9d2cbec3",
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=cfa51820f16533fd62b3c3d720e368ec",
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=98bd6927e46418c642fdd33fe56f4f26",
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=16dc04eb54142bd3c32c2768d365d988",
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=18787b3f00a3be7be38ee4e26cbd2a07",
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
"/css/dist/all.css": "/css/dist/all.css?id=72da33da732e29f66595d0acce3ece3f",
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
@ -82,34 +82,34 @@
"/js/select2/i18n/vi.js": "/js/select2/i18n/vi.js?id=097a5b75b3e146e2d94ab8e1510be607",
"/js/select2/i18n/zh-CN.js": "/js/select2/i18n/zh-CN.js?id=2cff662ec5f972b4613566cf5988cda2",
"/js/select2/i18n/zh-TW.js": "/js/select2/i18n/zh-TW.js?id=04554a227c2ba0f3bb6ca3d2e01e5440",
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=23b596145d70dadd6f88ef0b64a48adc",
"/css/webfonts/fa-brands-400.woff2": "/css/webfonts/fa-brands-400.woff2?id=4e435ccc04df22ce2fa30e0c55eca5f2",
"/css/webfonts/fa-regular-400.ttf": "/css/webfonts/fa-regular-400.ttf?id=290b61faa8646722457d62ae3245d516",
"/css/webfonts/fa-regular-400.woff2": "/css/webfonts/fa-regular-400.woff2?id=e00ee9f79d4cce84b7ce996b15e4a2e5",
"/css/webfonts/fa-solid-900.ttf": "/css/webfonts/fa-solid-900.ttf?id=e194bc47ced3f3575cdc008e775e8b1b",
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=a603254be8dd413edfd039ebf9f0d801",
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=11fd1ac2e62708f4d1a30b71ef196a37",
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=c1bf0df91d0829b264be085404d3f9bb",
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=c5445e15be5ce91a9ffef05e08ad6898",
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=0f6e85ae692d03a3b11cab445ff263ab",
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=e71ef4171dee5da63af390966ac60ffc",
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=06c13e817cc022028b3f4a33c0ca303a",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=6ea836d8126de101081c49abbdb89417",
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=f677207c6cf9678eb539abecb408c374",
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=da6c7997d9de2f8329142399f0ce50da",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=bb302302d9566adf783a2b7dc31e840c",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=0a82a6ae6bb4e58fe62d162c4fb50397",
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=71b7731f7ae692eada36b9b08a2e04a9",
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=6f0563e726c2fe4fab4026daaa5bfdf2",
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=fca90dff303e17dc2991ca395c7f7fa8",
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=6fe68325d5356197672c27bc77cedcb4",
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=05cc02539441b717012c4efc3303192f",
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=ea22079836a432d7f46a5d390c445e13",
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=c384582a6ba08903af353be861ffe74e",
"/js/build/vendor.js": "/js/build/vendor.js?id=89dffa552c6e3abe3a2aac6c9c7b466b",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=b4c3069f1a292527a96c058b77b28d69",
"/js/dist/all.js": "/js/dist/all.js?id=21e041dec60e0785db6d64961b13a9b0"
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=67c719c0d21dfb09066b4ecdc2310cb8",
"/css/webfonts/fa-brands-400.woff2": "/css/webfonts/fa-brands-400.woff2?id=ea42d181254fd563d365feb2b03a07f4",
"/css/webfonts/fa-regular-400.ttf": "/css/webfonts/fa-regular-400.ttf?id=60e2f6dc1bf9de9da0aa485ccf81b40e",
"/css/webfonts/fa-regular-400.woff2": "/css/webfonts/fa-regular-400.woff2?id=f43a0d63897b14b80bcf10f2fae2d6f8",
"/css/webfonts/fa-solid-900.ttf": "/css/webfonts/fa-solid-900.ttf?id=d1d4daff53a4c06220c5b90933fca9dc",
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=541cafc702f56f57de95f3d1f792f428",
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=51ade19e1b10d7a0031b18568a2b01d5",
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=1cc408d68a27c3757b4460bbc542433e",
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=467938d6a524df8e62c4fb8ae5e7f3f1",
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=d4ef3db8dc9f809258218c187de5ee2a",
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=b1c78591f51b52beab05b52f407ad6e6",
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=d34ae2483cbe2c77478c45f4006eba55",
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=18787b3f00a3be7be38ee4e26cbd2a07",
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=f0fbbb0ac729ea092578fb05ca615460",
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=16dc04eb54142bd3c32c2768d365d988",
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=98bd6927e46418c642fdd33fe56f4f26",
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=268041e902b019730c23ee3875838005",
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=cfa51820f16533fd62b3c3d720e368ec",
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=cdbb4f543fb448f03617c7ed9d2cbec3",
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=53edc92eb2d272744bc7404ec259930e",
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=393d720a0f9aba560094fbc8d3b0c0f0",
"/js/build/vendor.js": "/js/build/vendor.js?id=5269eb5a6beb74f03387c78938cf17b2",
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=6660df122e24940d42d03c06775fec7b",
"/js/dist/all.js": "/js/dist/all.js?id=262c933ac5d4c02c006d9bd531896c7b"
}

View file

@ -729,23 +729,27 @@ h4 {
any HTML used in the UserPresenter "title" attribute breaks the column selector HTML.
Instead, we use CSS to add the icon into the table header, which leaves the column selector
"title" text as-is.
"title" text as-is and hides the icon.
See https://github.com/snipe/snipe-it/issues/7989
*/
th.css-accessory > .th-inner,
th.css-accessory-alt > .th-inner,
th.css-barcode > .th-inner,
th.css-license > .th-inner,
th.css-component > .th-inner,
th.css-consumable > .th-inner,
th.css-envelope > .th-inner,
th.css-users > .th-inner,
th.css-house-flag > .th-inner,
th.css-house-laptop > .th-inner,
th.css-house-user > .th-inner,
th.css-license > .th-inner,
th.css-location > .th-inner,
th.css-component > .th-inner,
th.css-accessory > .th-inner
th.css-users > .th-inner,
th.css-currency > .th-inner,
th.css-history > .th-inner
{
font-size: 0px;
line-height: .75!important;
line-height: 0.75 !important;
text-align: left;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
@ -753,16 +757,22 @@ th.css-accessory > .th-inner
}
th.css-padlock > .th-inner::before,
th.css-location > .th-inner::before,
th.css-accessory > .th-inner::before,
th.css-accessory-alt > .th-inner::before,
th.css-barcode > .th-inner::before,
th.css-license > .th-inner::before,
th.css-component > .th-inner::before,
th.css-consumable > .th-inner::before,
th.css-envelope > .th-inner::before,
th.css-users > .th-inner::before,
th.css-house-flag > .th-inner::before,
th.css-house-laptop > .th-inner::before,
th.css-house-user > .th-inner::before,
th.css-license > .th-inner::before,
th.css-location > .th-inner::before,
th.css-component > .th-inner::before,
th.css-accessory > .th-inner::before
th.css-padlock > .th-inner::before,
th.css-users > .th-inner::before,
th.css-currency > .th-inner::before,
th.css-history > .th-inner::before
{
display: inline-block;
font-size: 20px;
@ -770,15 +780,6 @@ th.css-accessory > .th-inner::before
font-weight: 900;
}
th.css-padlock > .th-inner::before
{
content: "\f023";
font-family: "Font Awesome 5 Free";
font-weight: 900;
padding-right: 4px;
font-size: 12px;
}
/**
BEGIN ICON TABLE HEADERS
Set the font-weight css property as 900 (For Solid), 400 (Regular or Brands), 300 (Light for pro icons).
@ -821,6 +822,50 @@ th.css-component > .th-inner::before
content: "\f0a0"; font-family: "Font Awesome 5 Free"; font-weight: 500;
}
th.css-padlock > .th-inner::before
{
content: "\f023"; font-family: "Font Awesome 5 Free"; font-weight: 900;
}
th.css-house-user > .th-inner::before {
content: "\e1b0";
font-family: "Font Awesome 5 Free";
font-size: 19px;
margin-bottom: 0px;
}
th.css-house-flag > .th-inner::before {
content: "\e50d";
font-family: "Font Awesome 5 Free";
font-size: 19px;
margin-bottom: 0px;
}
th.css-house-laptop > .th-inner::before {
content: "\e066";
font-family: "Font Awesome 5 Free";
font-size: 19px;
margin-bottom: 0px;
}
th.css-accessory-alt > .th-inner::before {
content: "\f11c";
font-family: "Font Awesome 5 Free";
font-size: 19px;
margin-bottom: 0px;
}
th.css-currency > .th-inner::before {
content: "\24"; // change this to f51e for coins
font-family: "Font Awesome 5 Free";
font-size: 19px;
margin-bottom: 0px;
}
th.css-history > .th-inner::before {
content: "\f1da"; // change this to f51e for coins
font-family: "Font Awesome 5 Free";
font-size: 19px;
margin-bottom: 0px;
}
.small-box .inner {
padding-left: 15px;

View file

@ -565,5 +565,6 @@ return [
'label' => 'Label',
'import_asset_tag_exists' => 'An asset with the asset tag :asset_tag already exists and an update was not requested. No change was made.',
'countries_manually_entered_help' => 'Values with an asterisk (*) were manually entered and do not match existing ISO 3166 dropdown values',
'accessories_assigned' => 'Assigned Accessories',
];

View file

@ -64,9 +64,15 @@
<p class="form-control-static">{{ $accessory->numRemaining() }}</p>
</div>
</div>
<!-- User -->
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.select_user'), 'fieldname' => 'assigned_user', 'required'=> 'true'])
<!-- checkout selector -->
@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'])
<!-- 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;'])
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'assigned_location', 'style' => 'display:none;'])
<!-- Checkout QTY -->

View file

@ -76,6 +76,7 @@
<div class="row">
<div class="col-md-12">
<table
data-columns="{{ \App\Presenters\AccessoryPresenter::assignedDataTableLayout() }}"
data-cookie-id-table="checkoutsTable"
data-pagination="true"
data-id-table="checkoutsTable"
@ -93,14 +94,6 @@
"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="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>
</tr>
</thead>
</table>
</div><!--col-md-9-->
</div> <!-- close tab-pane div -->

View file

@ -32,7 +32,6 @@
data-bulk-button-id="#bulkLocationsEditButton"
data-bulk-form-id="#locationsBulkForm"
data-search="true"
data-show-footer="true"
data-side-pagination="server"
data-show-columns="true"
data-show-fullscreen="true"

View file

@ -22,100 +22,133 @@
<div class="nav-tabs-custom">
<ul class="nav nav-tabs hidden-print">
<li class="active">
<a href="#users" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="users" class="fa-2x" />
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.users') }}
{!! ($location->users->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->users->count()).'</badge>' : '' !!}
@can('view', \App\Models\User::class)
@if ($location->users->count() > 0)
<li class="active">
<a href="#users" data-toggle="tab">
<i class="fa-solid fa-house-user" style="font-size: 17px" aria-hidden="true"></i>
<span class="sr-only">
{{ trans('general.users') }}
</span>
<span class="badge">
{{ number_format($location->users->count()) }}
</span>
</a>
</li>
@endif
@endcan
@can('view', \App\Models\Asset::class)
@if ($location->assets()->AssetsForShow()->count() > 0)
<li>
<a href="#assets" data-toggle="tab" data-tooltip="true" title="{{ trans('admin/locations/message.current_location') }}">
<i class="fa-solid fa-house-laptop" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->assets()->AssetsForShow()->count()) }}
</span>
</a>
</li>
<li>
<a href="#assets" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="assets" class="fa-2x" />
</span>
<span class="hidden-xs hidden-sm">
<span class="sr-only">
{{ trans('admin/locations/message.current_location') }}
{!! ($location->assets()->AssetsForShow()->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->assets()->AssetsForShow()->count()).'</badge>' : '' !!}
</span>
</a>
</li>
</span>
</a>
</li>
@endif
<li>
<a href="#rtd_assets" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="assets" class="fa-2x" />
</span>
<span class="hidden-xs hidden-sm">
@if ($location->rtd_assets()->AssetsForShow()->count() > 0)
<li>
<a href="#rtd_assets" data-toggle="tab" data-tooltip="true" title="{{ trans('admin/hardware/form.default_location') }}">
<i class="fa-solid fa-house-flag" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->rtd_assets()->AssetsForShow()->count()) }}
</span>
<span class="sr-only">
{{ trans('admin/hardware/form.default_location') }}
{!! ($location->rtd_assets()->AssetsForShow()->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->rtd_assets()->AssetsForShow()->count()).'</badge>' : '' !!}
</span>
</a>
</li>
</span>
</a>
</li>
@endif
<li>
<a href="#assets_assigned" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="assets" class="fa-2x" />
</span>
<span class="hidden-xs hidden-sm">
@if ($location->assignedAssets()->AssetsForShow()->count() > 0)
<li>
<a href="#assets_assigned" data-toggle="tab" data-tooltip="true" title="{{ trans('admin/locations/message.assigned_assets') }}">
<i class="fas fa-barcode" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->assignedAssets()->AssetsForShow()->count()) }}
</span>
<span class="sr-only">
{{ trans('admin/locations/message.assigned_assets') }}
{!! ($location->assignedAssets()->AssetsForShow()->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->assignedAssets()->AssetsForShow()->count()).'</badge>' : '' !!}
</span>
</a>
</li>
</span>
</a>
</li>
@endif
@endcan
@can('view', \App\Models\Accessory::class)
@if ($location->accessories->count() > 0)
<li>
<a href="#accessories" data-toggle="tab" data-tooltip="true" title="{{ trans('general.accessories') }}">
<i class="far fa-keyboard" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->accessories->count()) }}
</span>
<span class="sr-only">
{{ trans('general.accessories') }}
</span>
</a>
</li>
@endif
@if ($location->assignedAccessories->count() > 0)
<li>
<a href="#accessories_assigned" data-toggle="tab" data-tooltip="true" title="{{ trans('general.accessories_assigned') }}">
<i class="fas fa-keyboard" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->assignedAccessories->count()) }}
</span>
<span class="sr-only">
{{ trans('general.accessories_assigned') }}
</span>
</a>
</li>
@endif
@endcan
<li>
<a href="#accessories" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="accessories" class="fa-2x "/>
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.accessories') }}
{!! ($location->accessories->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->accessories->count()).'</badge>' : '' !!}
</span>
</a>
</li>
@can('view', \App\Models\Consumable::class)
@if ($location->consumables->count() > 0)
<li>
<a href="#consumables" data-toggle="tab" data-tooltip="true" title="{{ trans('general.consumables') }}">
<i class="fas fa-tint" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->consumables->count()) }}
</span>
<span class="sr-only">
{{ trans('general.consumables') }}
</span>
</a>
</li>
@endif
@endcan
<li>
<a href="#consumables" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="consumables" class="fa-2x "/>
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.consumables') }}
{!! ($location->consumables->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->consumables->count()).'</badge>' : '' !!}
</span>
</a>
</li>
<li>
<a href="#components" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="components" class="fa-2x "/>
</span>
<span class="hidden-xs hidden-sm">
{{ trans('general.components') }}
{!! ($location->components->count() > 0 ) ? '<badge class="badge badge-secondary">'.number_format($location->components->count()).'</badge>' : '' !!}
</span>
</a>
</li>
@can('view', \App\Models\Component::class)
@if ($location->components->count() > 0)
<li>
<a href="#components" data-toggle="tab" data-tooltip="true" title="{{ trans('general.components') }}">
<i class="fas fa-hdd" style="font-size: 17px" aria-hidden="true"></i>
<span class="badge">
{{ number_format($location->components->count()) }}
</span>
<span class="sr-only">
{{ trans('general.components') }}
</span>
</a>
</li>
@endif
@endcan
<li>
<a href="#history" data-toggle="tab">
<span class="hidden-lg hidden-md">
<x-icon type="history" class="fa-2x "/>
</span>
<span class="hidden-xs hidden-sm">
<a href="#history" data-toggle="tab" data-toggle="tab" data-tooltip="true" title="{{ trans('general.history') }}">
<i class="fa-solid fa-clock-rotate-left" style="font-size: 17px" aria-hidden="true"></i>
<span class="sr-only">
{{ trans('general.history') }}
</span>
</a>
@ -211,7 +244,7 @@
data-click-to-select="true"
id="assetsListingTable"
class="table table-striped snipe-table"
data-url="{{route('api.assets.index', ['assigned_to' => $location->id, 'assigned_type' => \App\Models\Location::class]) }}"
data-url="{{route('api.locations.assigned_assets', ['location' => $location]) }}"
data-export-options='{
"fileName": "export-locations-{{ str_slug($location->name) }}-assets-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
@ -280,6 +313,36 @@
</div><!-- /.table-responsive -->
</div><!-- /.tab-pane -->
<div class="tab-pane" id="accessories_assigned">
<h2 class="box-title">
Assigned Accessories
</h2>
<div class="table table-responsive">
<table
data-columns="{{ \App\Presenters\LocationPresenter::assignedAccessoriesDataTableLayout() }}"
data-cookie-id-table="accessoriesAssignedListingTable"
data-pagination="true"
data-id-table="accessoriesAssignedListingTable"
data-search="true"
data-side-pagination="server"
data-show-columns="true"
data-show-export="true"
data-show-refresh="true"
data-sort-order="asc"
data-click-to-select="true"
id="accessoriesAssignedListingTable"
class="table table-striped snipe-table"
data-url="{{ route('api.locations.assigned_accessories', ['location' => $location]) }}"
data-export-options='{
"fileName": "export-locations-{{ str_slug($location->name) }}-accessories-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
}'>
</table>
</div><!-- /.table-responsive -->
</div><!-- /.tab-pane -->
<div class="tab-pane" id="consumables">
<h2 class="box-title">{{ trans('general.consumables') }}</h2>
@ -387,7 +450,7 @@
<div class="col-md-3">
@if ($location->image!='')
<div class="col-md-12 text-center" style="padding-bottom: 20px;">
<div class="col-md-12 text-center" style="padding-bottom: 17px;">
<img src="{{ Storage::disk('public')->url('locations/'.e($location->image)) }}" class="img-responsive img-thumbnail" style="width:100%" alt="{{ $location->name }}">
</div>
@endif

View file

@ -430,13 +430,13 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
'parameters' => ['group' => 'group_id'],
]
); // end groups API routes
/**
* Assets API routes
*/
Route::group(['prefix' => 'hardware'], function () {
Route::get('selectlist',
[
Api\AssetsController::class,
@ -524,18 +524,19 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
)->name('api.asset.checkin');
Route::post('{id}/checkout',
[
[
Api\AssetsController::class,
'checkout'
]
]
)->name('api.asset.checkout');
Route::post('{asset_id}/restore',
Route::post('{asset_id}/restore',
[
Api\AssetsController::class,
'restore'
]
)->name('api.assets.restore');
)->name('api.assets.restore');
Route::post('{asset_id}/files',
[
Api\AssetFilesController::class,
@ -563,8 +564,30 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
'destroy'
]
)->name('api.assets.files.destroy');
/** Begin assigned routes */
Route::get('{asset}/assigned/assets',
[
Api\AssetsController::class,
'assignedAssets'
]
)->name('api.assets.assigned_assets');
Route::get('{asset}/assigned/accessories',
[
Api\AssetsController::class,
'assignedAccessories'
]
)->name('api.assets.assigned_accessories');
/** End assigned routes */
});
// pulling this out of resource route group to begin normalizing for route-model binding.
// this would probably keep working with the resource route group, but the general practice is for
// the model name to be the parameter - and i think it's a good differentiation in the code while we convert the others.
@ -698,6 +721,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
]
)->name('api.locations.selectlist');
// Users within a location
Route::get('{location}/users',
[
Api\LocationsController::class,
@ -705,13 +729,32 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'throttle:api']], functi
]
)->name('api.locations.viewusers');
// Get list of assets with a default location
Route::get('{location}/assets',
[
Api\LocationsController::class,
'assets'
]
)->name('api.locations.viewassets');
// Add a comment here, you moron
/** Begin assigned routes */
Route::get('{location}/assigned/assets',
[
Api\LocationsController::class,
'assignedAssets'
]
)->name('api.locations.assigned_assets');
Route::get('{location}/assigned/accessories',
[
Api\LocationsController::class,
'assignedAccessories'
]
)->name('api.locations.assigned_accessories');
/** End assigned routes */
});
Route::resource('locations',

View file

@ -28,7 +28,7 @@ class LocationsViewTest extends TestCase
public function testViewingLocationAssetIndex()
{
$location = Location::factory()->create();
Asset::factory()->count(3)->assignedToLocation($location)->create();
Asset::factory()->count(3)->create(['location_id' => $location->id]);
$this->actingAsForApi(User::factory()->superuser()->create())
->getJson(route('api.locations.viewassets', $location->id))
@ -41,4 +41,21 @@ class LocationsViewTest extends TestCase
'total' => 3,
]);
}
public function testViewingAssignedLocationAssetIndex()
{
$location = Location::factory()->create();
Asset::factory()->count(3)->assignedToLocation($location)->create();
$this->actingAsForApi(User::factory()->superuser()->create())
->getJson(route('api.locations.assigned_assets', $location->id))
->assertOk()
->assertJsonStructure([
'total',
'rows',
])
->assertJson([
'total' => 3,
]);
}
}