Merge remote-tracking branch 'origin/develop'
This commit is contained in:
@@ -70,20 +70,9 @@ class AccessoriesController extends Controller
|
||||
->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier', 'adminuser')
|
||||
->withCount('checkouts as checkouts_count');
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$accessories->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$accessories->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$accessories->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
|
||||
@@ -93,21 +93,9 @@ class AssetModelsController extends Controller
|
||||
->withCount('assignedAssets as assets_assigned_count')
|
||||
->withCount('archivedAssets as assets_archived_count');
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$assetmodels->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$assetmodels->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$assetmodels->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->input('status') == 'deleted') {
|
||||
|
||||
@@ -142,17 +142,6 @@ class AssetsController extends Controller
|
||||
$allowed_columns[] = $field->db_column_name();
|
||||
}
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
// ->addSelect([
|
||||
// 'first_checkout_at' => Actionlog::query()
|
||||
@@ -195,10 +184,9 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$assets->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$assets->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$assets->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\CategoriesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -26,62 +27,50 @@ class CategoriesController extends Controller
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index(Request $request): array
|
||||
public function index(FilterRequest $request): array
|
||||
{
|
||||
$this->authorize('view', Category::class);
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'category_type',
|
||||
'category_type',
|
||||
'use_default_eula',
|
||||
'eula_text',
|
||||
'require_acceptance',
|
||||
'checkin_email',
|
||||
'assets_count',
|
||||
'accessories_count',
|
||||
'consumables_count',
|
||||
'assets_count',
|
||||
'category_type',
|
||||
'checkin_email',
|
||||
'components_count',
|
||||
'licenses_count',
|
||||
'consumables_count',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'eula_text',
|
||||
'id',
|
||||
'image',
|
||||
'tag_color',
|
||||
'licenses_count',
|
||||
'name',
|
||||
'notes',
|
||||
'require_acceptance',
|
||||
'tag_color',
|
||||
'updated_at',
|
||||
'use_default_eula',
|
||||
];
|
||||
|
||||
$categories = Category::select([
|
||||
'id',
|
||||
'created_by',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'name', 'category_type',
|
||||
'use_default_eula',
|
||||
'eula_text',
|
||||
'require_acceptance',
|
||||
'category_type',
|
||||
'checkin_email',
|
||||
'created_at',
|
||||
'created_by',
|
||||
'eula_text',
|
||||
'id',
|
||||
'image',
|
||||
'tag_color',
|
||||
'name',
|
||||
'notes',
|
||||
'require_acceptance',
|
||||
'tag_color',
|
||||
'updated_at',
|
||||
'use_default_eula',
|
||||
])
|
||||
->with('adminuser')
|
||||
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$categories->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$categories->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$categories->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\CompaniesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -21,7 +22,7 @@ class CompaniesController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', Company::class);
|
||||
|
||||
@@ -49,8 +50,9 @@ class CompaniesController extends Controller
|
||||
->with('adminuser')
|
||||
->withCount('licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$companies->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$companies->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -73,10 +73,9 @@ class ComponentsController extends Controller
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$components->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$components->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$components->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api;
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\StoreConsumableRequest;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
@@ -25,7 +26,7 @@ class ConsumablesController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): array
|
||||
public function index(FilterRequest $request): array
|
||||
{
|
||||
$this->authorize('index', Consumable::class);
|
||||
|
||||
@@ -60,21 +61,9 @@ class ConsumablesController extends Controller
|
||||
'manufacturer',
|
||||
];
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$consumables->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$consumables->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$consumables->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Transformers\CustomFieldsetsTransformer;
|
||||
use App\Http\Transformers\CustomFieldsTransformer;
|
||||
use App\Models\CustomField;
|
||||
@@ -35,10 +36,16 @@ class CustomFieldsetsController extends Controller
|
||||
*
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function index(): array
|
||||
public function index(FilterRequest $request): array
|
||||
{
|
||||
$this->authorize('index', CustomField::class);
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count')->get();
|
||||
$fieldsets = CustomFieldset::withCount('fields as fields_count', 'models as models_count');
|
||||
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$fieldsets->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
$fieldsets->get();
|
||||
|
||||
return (new CustomFieldsetsTransformer)->transformCustomFieldsets($fieldsets, $fieldsets->count());
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\StoreDepartmentRequest;
|
||||
use App\Http\Transformers\DepartmentsTransformer;
|
||||
@@ -22,7 +23,7 @@ class DepartmentsController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', Department::class);
|
||||
$allowed_columns = ['id', 'name', 'image', 'users_count', 'notes', 'tag_color'];
|
||||
@@ -43,8 +44,9 @@ class DepartmentsController extends Controller
|
||||
'departments.notes',
|
||||
])->with('location')->with('manager')->with('company')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$departments = $departments->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$departments->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Transformers\DepreciationsTransformer;
|
||||
use App\Models\Depreciation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -18,7 +19,7 @@ class DepreciationsController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$allowed_columns = [
|
||||
@@ -33,14 +34,15 @@ class DepreciationsController extends Controller
|
||||
'licenses_count',
|
||||
];
|
||||
|
||||
$depreciations = Depreciation::select('id', 'name', 'months', 'depreciation_min', 'depreciation_type', 'created_at', 'updated_at', 'created_by')
|
||||
$depreciations = Depreciation::select(['id', 'name', 'months', 'depreciation_min', 'depreciation_type', 'created_at', 'updated_at', 'created_by'])
|
||||
->with('adminuser')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('models as models_count')
|
||||
->withCount('licenses as licenses_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$depreciations->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Transformers\GroupsTransformer;
|
||||
use App\Models\Group;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -18,7 +19,7 @@ class GroupsController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('superadmin');
|
||||
|
||||
@@ -26,8 +27,9 @@ class GroupsController extends Controller
|
||||
|
||||
$groups = Group::select(['id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by'])->with('adminuser')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$groups = $groups->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$groups->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -22,7 +23,7 @@ class LicensesController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', License::class);
|
||||
|
||||
@@ -97,8 +98,9 @@ class LicensesController extends Controller
|
||||
$licenses->whereNull('expiration_date');
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$licenses = $licenses->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$licenses->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->input('deleted') == 'true') {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
@@ -32,7 +33,7 @@ class LocationsController extends Controller
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', Location::class);
|
||||
$allowed_columns = [
|
||||
@@ -107,8 +108,9 @@ class LocationsController extends Controller
|
||||
$locations = Company::scopeCompanyables($locations);
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$locations = $locations->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$locations->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Http\Transformers\MaintenancesTransformer;
|
||||
@@ -32,15 +33,16 @@ class MaintenancesController extends Controller
|
||||
*
|
||||
* @since [v1.8]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', Asset::class);
|
||||
|
||||
$maintenances = Maintenance::select('maintenances.*')
|
||||
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'adminuser', 'asset.assignedTo');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$maintenances = $maintenances->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$maintenances->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('asset_id')) {
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\ManufacturersTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -28,7 +29,7 @@ class ManufacturersController extends Controller
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('view', Manufacturer::class);
|
||||
$allowed_columns = [
|
||||
@@ -81,8 +82,9 @@ class ManufacturersController extends Controller
|
||||
$manufacturers->onlyTrashed();
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$manufacturers = $manufacturers->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$manufacturers->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Transformers\ActionlogsTransformer;
|
||||
use App\Models\Actionlog;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ReportsController extends Controller
|
||||
{
|
||||
@@ -17,14 +17,15 @@ class ReportsController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): JsonResponse|array
|
||||
public function index(FilterRequest $request): JsonResponse|array
|
||||
{
|
||||
$this->authorize('activity.view');
|
||||
|
||||
$actionlogs = Actionlog::with('item', 'user', 'adminuser', 'target', 'location');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$actionlogs = $actionlogs->TextSearch(e($request->input('search')));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$actionlogs->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if (($request->filled('target_type')) && ($request->filled('target_id'))) {
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\PieChartTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -23,7 +24,7 @@ class StatuslabelsController extends Controller
|
||||
*
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function index(Request $request): array
|
||||
public function index(FilterRequest $request): array
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$allowed_columns = [
|
||||
@@ -38,8 +39,9 @@ class StatuslabelsController extends Controller
|
||||
|
||||
$statuslabels = Statuslabel::with('adminuser')->withCount('assets as assets_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$statuslabels = $statuslabels->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$statuslabels->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\FilterRequest;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\SuppliersTransformer;
|
||||
@@ -31,7 +32,7 @@ class SuppliersController extends Controller
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index(Request $request): array
|
||||
public function index(FilterRequest $request): array
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
$allowed_columns = [
|
||||
@@ -67,8 +68,9 @@ class SuppliersController extends Controller
|
||||
->withCount('consumables as consumables_count')
|
||||
->with('adminuser');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$suppliers->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$suppliers->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
|
||||
@@ -171,10 +171,9 @@ class UsersController extends Controller
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$users->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$users->TextSearch($request->input('search'));
|
||||
// This invokes the Searchable model trait scopeTextSearch and will handle input by search or by advanced search filter
|
||||
if ($request->filled('filter') || $request->filled('search')) {
|
||||
$users->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('activated')) {
|
||||
|
||||
+14
-95
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Http\Controllers\Api\AccessoriesController\checkedout;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
@@ -47,7 +46,15 @@ class Accessory extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'model_number', 'order_number', 'purchase_date', 'notes'];
|
||||
protected $searchableAttributes = [
|
||||
'created_at',
|
||||
'model_number',
|
||||
'name',
|
||||
'notes',
|
||||
'order_number',
|
||||
'purchase_cost',
|
||||
'purchase_date',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
@@ -57,9 +64,13 @@ class Accessory extends SnipeModel
|
||||
protected $searchableRelations = [
|
||||
'category' => ['name'],
|
||||
'company' => ['name'],
|
||||
'location' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'location' => ['name'],
|
||||
];
|
||||
|
||||
protected $searchableCounts = [
|
||||
'checkouts_count',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -295,20 +306,6 @@ class Accessory extends SnipeModel
|
||||
->with('assignedTo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the accessory -> admin user relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @since [v7.0.13]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the accessory has users
|
||||
*
|
||||
@@ -456,84 +453,6 @@ class Accessory extends SnipeModel
|
||||
* BEGIN QUERY SCOPES
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('accessories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('accessories.notes', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('accessories.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('accessories.order_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('accessories.purchase_cost', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on created_by name
|
||||
|
||||
+8
-244
@@ -37,8 +37,14 @@ class Asset extends Depreciable
|
||||
protected $with = ['model', 'adminuser'];
|
||||
|
||||
use CompanyableTrait;
|
||||
use HasFactory, Loggable, Presentable, Requestable, SoftDeletes, UniqueUndeletedTrait, ValidatingTrait;
|
||||
use HasFactory;
|
||||
use HasUploads;
|
||||
use Loggable;
|
||||
use Presentable;
|
||||
use Requestable;
|
||||
use SoftDeletes;
|
||||
use UniqueUndeletedTrait;
|
||||
use ValidatingTrait;
|
||||
|
||||
public const LOCATION = 'location';
|
||||
|
||||
@@ -953,20 +959,6 @@ class Asset extends Depreciable
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user who created the item
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v1.0]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the asset -> status relationship
|
||||
*
|
||||
@@ -1326,6 +1318,7 @@ class Asset extends Depreciable
|
||||
|
||||
/**
|
||||
* Run additional, advanced searches.
|
||||
* This overrides the advancedTextSearch method on the Searchable model trait to add searching of assigned user, location, and assets.
|
||||
*
|
||||
* @param array $terms The search terms
|
||||
* @return Builder
|
||||
@@ -1890,235 +1883,6 @@ class Asset extends Depreciable
|
||||
)->withTrashed()->whereNull('assets.deleted_at'); // workaround for laravel bug
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $key => $search_val) {
|
||||
|
||||
$fieldname = str_replace('custom_fields.', '', $key);
|
||||
|
||||
if ($fieldname == 'asset_tag') {
|
||||
$query->where('assets.asset_tag', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('assets.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('assets.serial', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_date') {
|
||||
$query->where('assets.purchase_date', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('assets.purchase_cost', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('assets.notes', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('assets.order_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'status_label') {
|
||||
$query->whereHas(
|
||||
'assetstatus', function ($query) use ($search_val) {
|
||||
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'rtd_location') {
|
||||
$query->whereHas(
|
||||
'defaultLoc', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'assigned_to') {
|
||||
$query->whereHasMorph(
|
||||
'assignedTo', [User::class], function ($query) use ($search_val) {
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.display_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.username', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
)->orWhereHasMorph(
|
||||
'assignedTo', [Location::class], function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
)->orWhereHasMorph(
|
||||
'assignedTo', [Asset::class], function ($query) use ($search_val) {
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
// Don't use the asset table prefix here because it will pull from the original asset,
|
||||
// not the subselect we're doing here to get the assigned asset
|
||||
$query->where('name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('asset_tag', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('models.name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('models.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'model') {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->where('models.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'status_label') {
|
||||
$query->whereHas(
|
||||
'assetstatus', function ($query) use ($search_val) {
|
||||
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'jobtitle') {
|
||||
$query->where(function ($query) use ($search_val) {
|
||||
if (is_array($search_val)) {
|
||||
$query->whereHasMorph(
|
||||
'assignedTo',
|
||||
[User::class],
|
||||
function ($query) use ($search_val) {
|
||||
$query->whereIn('users.jobtitle', $search_val);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$query->whereHasMorph(
|
||||
'assignedTo',
|
||||
[User::class],
|
||||
function ($query) use ($search_val) {
|
||||
$query->where(function ($query) use ($search_val) {
|
||||
$query->where('users.jobtitle', 'LIKE', '%'.$search_val.'%');
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS CLUNKY BIT IS VERY IMPORTANT
|
||||
*
|
||||
* Although inelegant, this section matters a lot when querying against fields that do not
|
||||
* exist on the asset table. There's probably a better way to do this moving forward, for
|
||||
* example using the Schema:: methods to determine whether or not a column actually exists,
|
||||
* or even just using the $searchableRelations variable earlier in this file.
|
||||
*
|
||||
* In short, this set of statements tells the query builder to ONLY query against an
|
||||
* actual field that's being passed if it doesn't meet known relational fields. This
|
||||
* allows us to query custom fields directly in the assets table
|
||||
* (regardless of their name) and *skip* any fields that we already know can only be
|
||||
* searched through relational searches that we do earlier in this method.
|
||||
*
|
||||
* For example, we do not store "location" as a field on the assets table, we store
|
||||
* that relationship through location_id on the assets table, therefore querying
|
||||
* assets.location would fail, as that field doesn't exist -- plus we're already searching
|
||||
* against those relationships earlier in this method.
|
||||
*
|
||||
* - snipe
|
||||
*/
|
||||
if (($fieldname != 'category') && ($fieldname != 'model_number') && ($fieldname != 'rtd_location') && ($fieldname != 'location') && ($fieldname != 'supplier')
|
||||
&& ($fieldname != 'status_label') && ($fieldname != 'assigned_to') && ($fieldname != 'model') && ($fieldname != 'jobtitle') && ($fieldname != 'company') && ($fieldname != 'manufacturer')
|
||||
) {
|
||||
$query->where('assets.'.$fieldname, 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on model
|
||||
*
|
||||
|
||||
+20
-64
@@ -85,10 +85,12 @@ class AssetModel extends SnipeModel
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'model_number',
|
||||
'notes',
|
||||
'created_at',
|
||||
'eol',
|
||||
'min_amt',
|
||||
'model_number',
|
||||
'name',
|
||||
'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -100,6 +102,20 @@ class AssetModel extends SnipeModel
|
||||
'depreciation' => ['name'],
|
||||
'category' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
'fieldset' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Computed aliases (withCount/withSum) that can be searched via TextSearch filters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableCounts = [
|
||||
'assets_count',
|
||||
'remaining',
|
||||
'assets_assigned_count',
|
||||
'assets_archived_count',
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
@@ -147,6 +163,7 @@ class AssetModel extends SnipeModel
|
||||
if ($this->availableAssets()->count() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->availableAssets()->count() / $this->assets()->count() * 100;
|
||||
}
|
||||
|
||||
@@ -261,73 +278,12 @@ class AssetModel extends SnipeModel
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user who created the item
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v1.0]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN QUERY SCOPES
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('models.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('models.notes', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* scopeInCategory
|
||||
* Get all models that are in the array of category ids
|
||||
|
||||
+18
-34
@@ -89,14 +89,30 @@ class Category extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'category_type', 'notes'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'category_type',
|
||||
'notes',
|
||||
'eula_text',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
protected $searchableCounts = [
|
||||
'accessories_count',
|
||||
'consumables_count',
|
||||
'components_count',
|
||||
'licenses_count',
|
||||
'models_count',
|
||||
];
|
||||
|
||||
/**
|
||||
* Checks if category can be deleted
|
||||
@@ -263,11 +279,6 @@ class Category extends SnipeModel
|
||||
return $this->hasMany(AssetModel::class, 'category_id');
|
||||
}
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a category-specific EULA, and if that doesn't exist,
|
||||
* checks for a settings level EULA
|
||||
@@ -315,33 +326,6 @@ class Category extends SnipeModel
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'category_type') {
|
||||
$query->where('categories.category_type', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope for whether or not the category requires acceptance
|
||||
*
|
||||
|
||||
+12
-7
@@ -60,14 +60,24 @@ final class Company extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'phone', 'fax', 'email', 'created_at', 'updated_at'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'phone',
|
||||
'fax',
|
||||
'email',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -317,11 +327,6 @@ final class Company extends SnipeModel
|
||||
|
||||
}
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* I legit do not know what this method does, but we can't remove it (yet).
|
||||
*
|
||||
|
||||
+1
-103
@@ -115,6 +115,7 @@ class Component extends SnipeModel
|
||||
'location' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public static function booted()
|
||||
@@ -164,22 +165,6 @@ class Component extends SnipeModel
|
||||
return $this->belongsToMany(Asset::class, 'components_assets')->withPivot('id', 'assigned_qty', 'created_at', 'created_by', 'note');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the component -> admin user relationship
|
||||
*
|
||||
* @todo this is probably not needed - refactor
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v3.0]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the component -> company relationship
|
||||
*
|
||||
@@ -410,93 +395,6 @@ class Component extends SnipeModel
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('components.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('components.notes', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('components.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('components.order_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('components.serial', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('components.serial', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('components.purchase_cost', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
*
|
||||
|
||||
+11
-95
@@ -96,7 +96,15 @@ class Consumable extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'order_number', 'purchase_cost', 'purchase_date', 'item_no', 'model_number', 'notes'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'order_number',
|
||||
'purchase_cost',
|
||||
'purchase_date',
|
||||
'item_no',
|
||||
'model_number',
|
||||
'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
@@ -109,6 +117,7 @@ class Consumable extends SnipeModel
|
||||
'location' => ['name'],
|
||||
'manufacturer' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -141,20 +150,6 @@ class Consumable extends SnipeModel
|
||||
&& ($this->deleted_at == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the consumable -> admin user relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*
|
||||
* @since [v3.0]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the component -> assignments relationship
|
||||
*
|
||||
@@ -174,6 +169,7 @@ class Consumable extends SnipeModel
|
||||
if ($this->consumables_users_count == 0) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return ($this->qty - $this->consumables_users_count) / $this->qty * 100;
|
||||
}
|
||||
|
||||
@@ -186,7 +182,6 @@ class Consumable extends SnipeModel
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class, 'company_id');
|
||||
@@ -417,85 +412,6 @@ class Consumable extends SnipeModel
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('consumables.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('consumables.notes', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('consumables.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('consumables.order_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'item_no') {
|
||||
$query->where('consumables.item_no', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('consumables.serial', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('consumables.purchase_cost', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
|
||||
@@ -88,6 +88,30 @@ class CustomField extends Model
|
||||
'show_in_requestable_list',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'format',
|
||||
'element',
|
||||
'db_column',
|
||||
'help_text',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'fieldset' => ['name'],
|
||||
'assetModels' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
/**
|
||||
* This is confusing, since it's actually the custom fields table that
|
||||
* we're usually modifying, but since we alter the assets table, we have to
|
||||
|
||||
@@ -77,14 +77,21 @@ class Department extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'notes', 'phone', 'fax'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'notes',
|
||||
'phone',
|
||||
'fax',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
|
||||
@@ -40,7 +40,10 @@ class Depreciation extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['name', 'months'];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'months',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
@@ -49,14 +52,19 @@ class Depreciation extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'months'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'months',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
|
||||
+9
-17
@@ -35,6 +35,7 @@ class Group extends SnipeModel
|
||||
* @var bool
|
||||
*/
|
||||
protected $injectUniqueIdentifier = true;
|
||||
|
||||
protected $presenter = GroupPresenter::class;
|
||||
|
||||
use Searchable;
|
||||
@@ -45,15 +46,20 @@ class Group extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'created_at', 'notes'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'created_at',
|
||||
'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
@@ -75,20 +81,6 @@ class Group extends SnipeModel
|
||||
return $this->belongsToMany(User::class, 'users_groups');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that created the group
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @since [v6.3.0]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode JSON permissions into array
|
||||
*
|
||||
|
||||
+3
-14
@@ -112,6 +112,8 @@ class License extends Depreciable
|
||||
'purchase_cost',
|
||||
'purchase_date',
|
||||
'expiration_date',
|
||||
'license_email',
|
||||
'license_name',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -125,6 +127,7 @@ class License extends Depreciable
|
||||
'category' => ['name'],
|
||||
'depreciation' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
protected $appends = ['free_seat_count'];
|
||||
@@ -520,20 +523,6 @@ class License extends Depreciable
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the license -> admin user relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @since [v2.0]
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of all license seats
|
||||
*
|
||||
|
||||
+1
-12
@@ -115,6 +115,7 @@ class Location extends SnipeModel
|
||||
protected $searchableRelations = [
|
||||
'parent' => ['name'],
|
||||
'company' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -158,18 +159,6 @@ class Location extends SnipeModel
|
||||
return $this->hasMany(User::class, 'location_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the location -> admin user relationship
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @return Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find assets with this location as their location_id
|
||||
*
|
||||
|
||||
@@ -94,6 +94,7 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
'asset.supplier' => ['name'],
|
||||
'asset.assetstatus' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public function getCompanyableParents()
|
||||
@@ -195,21 +196,6 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin who created the maintenance
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
*
|
||||
* @version v3.0
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
public function supplier()
|
||||
{
|
||||
return $this->belongsTo(Supplier::class, 'supplier_id')
|
||||
|
||||
@@ -67,14 +67,20 @@ class Manufacturer extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableAttributes = ['name', 'created_at', 'notes'];
|
||||
protected $searchableAttributes = [
|
||||
'name',
|
||||
'created_at',
|
||||
'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations and their attributes that should be included when searching the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public function isDeletable()
|
||||
{
|
||||
@@ -117,11 +123,6 @@ class Manufacturer extends SnipeModel
|
||||
return $this->hasMany(Component::class, 'manufacturer_id');
|
||||
}
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on the user that created it
|
||||
*/
|
||||
|
||||
@@ -143,12 +143,9 @@ class PredefinedKit extends SnipeModel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [];
|
||||
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Establishes the kits -> models relationship
|
||||
|
||||
@@ -7,6 +7,7 @@ use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class SnipeModel extends Model
|
||||
@@ -240,6 +241,20 @@ class SnipeModel extends Model
|
||||
return $this->hasMany(Actionlog::class, 'target_id')->where('target_type', '=', self::class)->orderBy('created_at', 'DESC')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the object -> admin user relationship
|
||||
*
|
||||
* @return Relation
|
||||
*
|
||||
* @since [v3.0]
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
*/
|
||||
public function adminuser()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by')->withTrashed();
|
||||
}
|
||||
|
||||
public function showCheckoutButton($item)
|
||||
{
|
||||
|
||||
|
||||
@@ -11,6 +11,25 @@ use Illuminate\Support\Facades\DB;
|
||||
* This trait allows for cleaner searching of models,
|
||||
* moving from complex queries to an easier declarative syntax.
|
||||
*
|
||||
* This handles all the out of the box advanced search stuff (using the "advanced search" bootstrap table plugin),
|
||||
* allowing you to just define which attributes and relations should be searched, and then it does the rest.
|
||||
*
|
||||
* You can override these trait methods (for example, advancedSearch) if you need different ebhavior, but this really
|
||||
* should cover most of the use cases, and allows you to easily add searching to your models without having to
|
||||
* write complex queries.
|
||||
*
|
||||
* To use this:
|
||||
*
|
||||
* 1. Make sure the model has $searchableAttributes and $searchableRelations set
|
||||
* 2. Make sure you import the App\Models\Traits\Searchable trait and use Searchable in the model
|
||||
* 3. Make sure you check the request for the request input filter or search and then invoke the TextSearch scope, like:
|
||||
*
|
||||
* if ($request->filled('filter') || $request->filled('search')) {
|
||||
* $whateverModel->TextSearch($request->input('filter') ? $request->input('filter') : $request->input('search'));
|
||||
* }
|
||||
* 4. Set the "data-advanced
|
||||
*
|
||||
*
|
||||
* @author Till Deeke <kontakt@tilldeeke.de>
|
||||
*/
|
||||
trait Searchable
|
||||
@@ -24,7 +43,13 @@ trait Searchable
|
||||
*/
|
||||
public function scopeTextSearch($query, $search)
|
||||
{
|
||||
$terms = $this->prepeareSearchTerms($search);
|
||||
$preparedSearch = $this->prepareSearchInput((string) $search);
|
||||
$terms = $preparedSearch['terms'];
|
||||
$filters = $preparedSearch['filters'];
|
||||
|
||||
if (! empty($filters)) {
|
||||
return $this->applySearchFilters($query, $filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the attributes of this model
|
||||
@@ -49,6 +74,78 @@ trait Searchable
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse free-text terms and structured filters for TextSearch.
|
||||
*
|
||||
* Supported filter inputs:
|
||||
* - {"field":"value"}
|
||||
* - filter:{"field":"value"}
|
||||
*/
|
||||
private function prepareSearchInput(string $search): array
|
||||
{
|
||||
$search = trim($search);
|
||||
|
||||
$parsedFilters = $this->parseStructuredFilterPayload($search);
|
||||
|
||||
if ($parsedFilters !== null) {
|
||||
return [
|
||||
'terms' => [],
|
||||
'filters' => $parsedFilters,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'terms' => $this->prepeareSearchTerms($search),
|
||||
'filters' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a structured filter payload into scalar string filters.
|
||||
*/
|
||||
private function parseStructuredFilterPayload(string $search): ?array
|
||||
{
|
||||
if ($search === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payload = $search;
|
||||
|
||||
if (str_starts_with($search, 'filter:')) {
|
||||
$payload = substr($search, 7);
|
||||
} elseif (! (str_starts_with($search, '{') && str_ends_with($search, '}'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode($payload, true);
|
||||
|
||||
if (! is_array($decoded)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$filters = [];
|
||||
|
||||
foreach ($decoded as $key => $value) {
|
||||
if (! is_string($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_scalar($value) && $value !== null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalizedValue = trim((string) ($value ?? ''));
|
||||
|
||||
if ($normalizedValue === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filters[$key] = $normalizedValue;
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the search term, splitting and cleaning it up
|
||||
*
|
||||
@@ -60,11 +157,88 @@ trait Searchable
|
||||
return explode(' OR ', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply structured filters to searchable attributes and relations.
|
||||
*
|
||||
* @param array<string, string> $filters
|
||||
*/
|
||||
private function applySearchFilters(Builder $query, array $filters): Builder
|
||||
{
|
||||
$searchableAttributes = $this->getSearchableAttributes();
|
||||
$searchableCounts = $this->getSearchableCounts();
|
||||
$searchableRelations = $this->getSearchableRelations();
|
||||
$table = $this->getTable();
|
||||
|
||||
foreach ($filters as $filterKey => $filterValue) {
|
||||
if (in_array($filterKey, $searchableAttributes, true)) {
|
||||
$query->where($table.'.'.$filterKey, 'LIKE', '%'.$filterValue.'%');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($filterKey, $searchableCounts, true)) {
|
||||
$query = $this->applyCountAliasFilter($query, $filterKey, $filterValue);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! array_key_exists($filterKey, $searchableRelations)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$relationColumns = (array) $searchableRelations[$filterKey];
|
||||
|
||||
$query->whereHas($filterKey, function (Builder $relationQuery) use ($filterKey, $relationColumns, $filterValue) {
|
||||
$relationTable = $this->getRelationTable($filterKey);
|
||||
$firstConditionAdded = false;
|
||||
|
||||
foreach ($relationColumns as $relationColumn) {
|
||||
if (! $firstConditionAdded) {
|
||||
$relationQuery->where($relationTable.'.'.$relationColumn, 'LIKE', '%'.$filterValue.'%');
|
||||
$firstConditionAdded = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$relationQuery->orWhere($relationTable.'.'.$relationColumn, 'LIKE', '%'.$filterValue.'%');
|
||||
}
|
||||
|
||||
if (($filterKey === 'adminuser') || ($filterKey === 'user')) {
|
||||
$relationQuery->orWhereRaw(
|
||||
$this->buildMultipleColumnSearch(
|
||||
[
|
||||
'users.first_name',
|
||||
'users.last_name',
|
||||
'users.display_name',
|
||||
]
|
||||
),
|
||||
["%{$filterValue}%"]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filtering on computed count aliases (for example withCount aliases).
|
||||
*/
|
||||
private function applyCountAliasFilter(Builder $query, string $countAlias, string $filterValue): Builder
|
||||
{
|
||||
if (is_numeric($filterValue)) {
|
||||
return $query->having($countAlias, '=', (int) $filterValue);
|
||||
}
|
||||
|
||||
return $query->having($countAlias, 'LIKE', '%'.$filterValue.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the models attributes for the search terms
|
||||
*
|
||||
* @param Illuminate\Database\Eloquent\Builder $query
|
||||
* @return Illuminate\Database\Eloquent\Builder
|
||||
* @param $query Builder
|
||||
* @param $terms array
|
||||
* @return Builder
|
||||
*/
|
||||
private function searchAttributes(Builder $query, array $terms)
|
||||
{
|
||||
@@ -107,8 +281,9 @@ trait Searchable
|
||||
/**
|
||||
* Searches the models custom fields for the search terms
|
||||
*
|
||||
* @param Illuminate\Database\Eloquent\Builder $query
|
||||
* @return Illuminate\Database\Eloquent\Builder
|
||||
* @param $query Builder
|
||||
* @param $terms array
|
||||
* @return Builder
|
||||
*/
|
||||
private function searchCustomFields(Builder $query, array $terms)
|
||||
{
|
||||
@@ -134,8 +309,9 @@ trait Searchable
|
||||
/**
|
||||
* Searches the models relations for the search terms
|
||||
*
|
||||
* @param Illuminate\Database\Eloquent\Builder $query
|
||||
* @return Illuminate\Database\Eloquent\Builder
|
||||
* @param $query Builder
|
||||
* @param $terms array
|
||||
* @return Builder
|
||||
*/
|
||||
private function searchRelations(Builder $query, array $terms)
|
||||
{
|
||||
@@ -188,9 +364,9 @@ trait Searchable
|
||||
*
|
||||
* This is a noop in this trait, but can be overridden in the implementing model, to allow more advanced searches
|
||||
*
|
||||
* @param Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $terms The search terms
|
||||
* @return Illuminate\Database\Eloquent\Builder
|
||||
* @param $query Builder
|
||||
* @param $terms array
|
||||
* @return Builder
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
@@ -219,6 +395,14 @@ trait Searchable
|
||||
return $this->searchableRelations ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get searchable computed count aliases, if defined.
|
||||
*/
|
||||
private function getSearchableCounts(): array
|
||||
{
|
||||
return $this->searchableCounts ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table name of a relation.
|
||||
*
|
||||
|
||||
+11
-131
@@ -167,6 +167,17 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
'groups' => ['name'],
|
||||
'company' => ['name'],
|
||||
'manager' => ['first_name', 'last_name', 'username', 'display_name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
protected $searchableCounts = [
|
||||
'accessories_count',
|
||||
'assets_count',
|
||||
'licenses_count',
|
||||
'consumables_count',
|
||||
'accessories_count',
|
||||
'manages_users_count',
|
||||
'manages_locations_count',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -948,137 +959,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
return new \stdClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'first_name') {
|
||||
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
if ($fieldname == 'last_name') {
|
||||
$query->where('users.last_name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'display_name') {
|
||||
$query->where('users.display_name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('users.last_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.first_name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'username') {
|
||||
$query->where('users.username', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'email') {
|
||||
$query->where('users.email', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'phone') {
|
||||
$query->where('users.phone', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'mobile') {
|
||||
$query->where('users.mobile', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'phone') {
|
||||
$query->where('users.phone', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'jobtitle') {
|
||||
$query->where('users.jobtitle', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'created_at') {
|
||||
$query->where('users.created_at', '=', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'updated_at') {
|
||||
$query->where('users.updated_at', '=', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'start_date') {
|
||||
$query->where('users.start_date', '=', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'end_date') {
|
||||
$query->where('users.end_date', '=', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'employee_num') {
|
||||
$query->where('users.employee_num', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'locale') {
|
||||
$query->where('users.locale', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'address') {
|
||||
$query->where('users.address', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'state') {
|
||||
$query->where('users.state', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'zip') {
|
||||
$query->where('users.zip', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'country') {
|
||||
$query->where('users.country', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'vip') {
|
||||
$query->where('users.vip', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'remote') {
|
||||
$query->where('users.remote', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'start_date') {
|
||||
$query->where('users.purchase_date', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('users.notes', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to search user by name with spaces in it.
|
||||
* We don't use the advancedTextSearch() scope because that searches
|
||||
|
||||
@@ -72,7 +72,7 @@ class AssetModelPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('mail.min_QTY'),
|
||||
@@ -83,7 +83,7 @@ class AssetModelPresenter extends Presenter
|
||||
|
||||
[
|
||||
'field' => 'assets_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/models/table.numassets'),
|
||||
@@ -93,7 +93,7 @@ class AssetModelPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'assets_assigned_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.assigned'),
|
||||
@@ -103,7 +103,7 @@ class AssetModelPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'remaining',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.remaining'),
|
||||
@@ -114,15 +114,15 @@ class AssetModelPresenter extends Presenter
|
||||
[
|
||||
'field' => 'percent_remaining',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => '% ' . trans('general.remaining'),
|
||||
'title' => '% '.trans('general.remaining'),
|
||||
'visible' => true,
|
||||
'formatter' => 'progressBarFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'assets_archived_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.archived'),
|
||||
@@ -132,7 +132,7 @@ class AssetModelPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'depreciation',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.depreciation'),
|
||||
@@ -158,7 +158,7 @@ class AssetModelPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'fieldset',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/models/general.fieldset'),
|
||||
@@ -192,7 +192,7 @@ class AssetModelPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => false,
|
||||
@@ -290,8 +290,9 @@ class AssetModelPresenter extends Presenter
|
||||
public function imageUrl()
|
||||
{
|
||||
if (! empty($this->image)) {
|
||||
$url = Storage::disk('public')->url(app('models_upload_path') . e($this->image));
|
||||
return '<img src="' . $url . '" alt="' . e($this->name) . '" height="50" width="50">';
|
||||
$url = Storage::disk('public')->url(app('models_upload_path').e($this->image));
|
||||
|
||||
return '<img src="'.$url.'" alt="'.e($this->name).'" height="50" width="50">';
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -305,7 +306,7 @@ class AssetModelPresenter extends Presenter
|
||||
public function imageSrc()
|
||||
{
|
||||
if (! empty($this->image)) {
|
||||
return Storage::disk('public')->url(app('models_upload_path') . e($this->image));
|
||||
return Storage::disk('public')->url(app('models_upload_path').e($this->image));
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
@@ -108,7 +108,7 @@ class CategoryPresenter extends Presenter
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'created_at',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.created_at'),
|
||||
|
||||
@@ -248,7 +248,7 @@ class UserPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'assets_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'escape' => true,
|
||||
@@ -259,7 +259,7 @@ class UserPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'licenses_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'class' => 'css-license',
|
||||
@@ -269,7 +269,7 @@ class UserPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'consumables_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'class' => 'css-consumable',
|
||||
@@ -279,7 +279,7 @@ class UserPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'accessories_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'class' => 'css-accessory',
|
||||
@@ -289,7 +289,7 @@ class UserPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'manages_users_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'class' => 'css-users',
|
||||
@@ -299,7 +299,7 @@ class UserPresenter extends Presenter
|
||||
],
|
||||
[
|
||||
'field' => 'manages_locations_count',
|
||||
'searchable' => false,
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'class' => 'css-location',
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
buttons="categoryButtons"
|
||||
fixed_right_number="1"
|
||||
fixed_number="1"
|
||||
show_advanced_search="true"
|
||||
api_url="{{ route('api.categories.index') }}"
|
||||
:presenter="\App\Presenters\CategoryPresenter::dataTableLayout()"
|
||||
export_filename="export-categories-{{ date('Y-m-d') }}"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
fixed_right_number="2"
|
||||
fixed_number="1"
|
||||
show_footer="true"
|
||||
show_advanced_search="true"
|
||||
name="licenses"
|
||||
:route="route('api.licenses.index', ['status' => e(request('status'))])"/>
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
namespace Tests\Feature\Accessories\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Testing\Fluent\AssertableJson;
|
||||
use Tests\Concerns\TestsFullMultipleCompaniesSupport;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\TestCase;
|
||||
@@ -67,4 +69,31 @@ class IndexAccessoryTest extends TestCase implements TestsFullMultipleCompaniesS
|
||||
->assertResponseContainsInRows($accessoryA)
|
||||
->assertResponseContainsInRows($accessoryB);
|
||||
}
|
||||
|
||||
public function test_can_filter_accessories_by_searchable_count_alias()
|
||||
{
|
||||
$this->markIncompleteIfSqlite('This test is not compatible with SQLite');
|
||||
$user = User::factory()->viewAccessories()->create();
|
||||
|
||||
$targetAccessory = Accessory::factory()->create(['name' => 'Accessory With Two Checkouts']);
|
||||
$otherAccessory = Accessory::factory()->create(['name' => 'Accessory With One Checkout']);
|
||||
|
||||
AccessoryCheckout::factory()->count(2)->create(['accessory_id' => $targetAccessory->id]);
|
||||
AccessoryCheckout::factory()->create(['accessory_id' => $otherAccessory->id]);
|
||||
|
||||
$this->actingAsForApi($user)
|
||||
->getJson(route('api.accessories.index', [
|
||||
'filter' => json_encode(['checkouts_count' => 2]),
|
||||
'sort' => 'id',
|
||||
'order' => 'asc',
|
||||
'offset' => '0',
|
||||
'limit' => '20',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 1)->where('rows.0.name', 'Accessory With Two Checkouts')->etc());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Tests\Feature\AssetModels\Api;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\User;
|
||||
use Illuminate\Testing\Fluent\AssertableJson;
|
||||
@@ -62,4 +63,30 @@ class IndexAssetModelsTest extends TestCase
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 1)->etc());
|
||||
}
|
||||
|
||||
public function test_asset_model_index_filter_can_search_computed_count_aliases()
|
||||
{
|
||||
$this->markIncompleteIfSqlite('This test is not compatible with SQLite');
|
||||
$targetModel = AssetModel::factory()->create(['name' => 'Two Assets Model']);
|
||||
$otherModel = AssetModel::factory()->create(['name' => 'One Asset Model']);
|
||||
|
||||
Asset::factory()->count(2)->create(['model_id' => $targetModel->id]);
|
||||
Asset::factory()->create(['model_id' => $otherModel->id]);
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->getJson(route('api.models.index', [
|
||||
'filter' => '{"assets_count":"2"}',
|
||||
'sort' => 'id',
|
||||
'order' => 'asc',
|
||||
'offset' => '0',
|
||||
'limit' => '20',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 1)->etc())
|
||||
->assertJsonFragment(['name' => 'Two Assets Model']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Tests\Feature\Licenses\Api;
|
||||
use App\Models\Company;
|
||||
use App\Models\License;
|
||||
use App\Models\User;
|
||||
use Illuminate\Testing\Fluent\AssertableJson;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LicenseIndexTest extends TestCase
|
||||
@@ -54,4 +55,76 @@ class LicenseIndexTest extends TestCase
|
||||
->assertResponseDoesNotContainInRows($licenseA)
|
||||
->assertResponseContainsInRows($licenseB);
|
||||
}
|
||||
|
||||
public function test_returns_result_via_filter()
|
||||
{
|
||||
|
||||
License::factory()->create(['name' => 'MY AWESOME LICENSE NAME 1']);
|
||||
License::factory()->count(2)->create(['name' => 'MY AWESOME LICENSE NAME 2']);
|
||||
License::factory()->count(2)->create(['name' => 'MY AWESOME LICENSE NAME 3']);
|
||||
License::factory()->count(2)->create(['name' => 'MY TERRIBLE LICENSE NAME']);
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewLicenses()->create())
|
||||
->getJson(route('api.licenses.index', [
|
||||
'filter' => '{"name":"AWESOME LICENSE NAME"}',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 5)->etc());
|
||||
}
|
||||
|
||||
public function test_returns_result_via_filter_for_manufacturer()
|
||||
{
|
||||
|
||||
License::factory()->count(5)->office()->create();
|
||||
License::factory()->count(3)->indesign()->create();
|
||||
License::factory()->count(3)->acrobat()->create();
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewLicenses()->create())
|
||||
->getJson(route('api.licenses.index', [
|
||||
'filter' => '{"manufacturer":"adobe"}',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 6)->etc());
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewLicenses()->create())
|
||||
->getJson(route('api.licenses.index', [
|
||||
'filter' => '{"manufacturer":"blah"}',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 0)->etc());
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewLicenses()->create())
|
||||
->getJson(route('api.licenses.index', [
|
||||
'filter' => '{"manufacturer":"microsoft"}',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 5)->etc());
|
||||
|
||||
$this->actingAsForApi(User::factory()->viewLicenses()->create())
|
||||
->getJson(route('api.licenses.index', [
|
||||
'search' => 'adobe',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'total',
|
||||
'rows',
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 6)->etc());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user