snipe-it/app/Http/Controllers/Api/AssetsController.php

817 lines
32 KiB
PHP
Raw Normal View History

2017-01-11 18:14:06 -08:00
<?php
2017-01-11 18:14:06 -08:00
namespace App\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
2017-11-27 21:17:16 -08:00
use App\Http\Requests\AssetCheckoutRequest;
use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer;
2017-01-11 18:14:06 -08:00
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\CustomField;
use App\Models\License;
2017-01-11 18:14:06 -08:00
use App\Models\Location;
use App\Models\Setting;
2017-01-11 18:14:06 -08:00
use App\Models\User;
use Auth;
use Carbon\Carbon;
use DB;
2017-01-11 18:14:06 -08:00
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
2021-06-29 02:26:24 -07:00
use App\Http\Requests\ImageUploadRequest;
use Input;
use Paginator;
use Slack;
use Str;
use TCPDF;
use Validator;
2017-01-11 18:14:06 -08:00
/**
* This class controls all actions related to assets for
* the Snipe-IT Asset Management application.
*
* @version v1.0
* @author [A. Gianotto] [<snipe@snipe.net>]
*/
class AssetsController extends Controller
{
/**
* Returns JSON listing of all assets
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
public function index(Request $request, $audit = null)
2017-01-11 18:14:06 -08:00
{
2017-01-13 11:41:00 -08:00
$this->authorize('index', Asset::class);
$settings = Setting::getSettings();
2017-01-13 11:41:00 -08:00
$allowed_columns = [
'id',
'name',
'asset_tag',
'serial',
'model_number',
'last_checkout',
'notes',
'expected_checkin',
'order_number',
'image',
'assigned_to',
'created_at',
'updated_at',
2017-01-13 11:41:00 -08:00
'purchase_date',
'purchase_cost',
'last_audit_date',
'next_audit_date',
'warranty_months',
'checkout_counter',
'checkin_counter',
'requests_counter',
2017-01-13 11:41:00 -08:00
];
$filter = [];
2019-05-23 17:39:50 -07:00
if ($request->filled('filter')) {
$filter = json_decode($request->input('filter'), true);
2017-03-11 04:26:01 -08:00
}
2017-01-13 11:41:00 -08:00
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
foreach ($all_custom_fields as $field) {
$allowed_columns[] = $field->db_column_name();
2017-01-13 11:41:00 -08:00
}
$assets = Company::scopeCompanyables(Asset::select('assets.*'), 'company_id', 'assets')
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier');
2017-10-18 10:07:35 -07:00
// These are used by the API to query against specific ID numbers.
// They are also used by the individual searches on detail pages like
// locations, etc.
2019-05-23 17:39:50 -07:00
if ($request->filled('status_id')) {
$assets->where('assets.status_id', '=', $request->input('status_id'));
}
if ($request->input('requestable') == 'true') {
WIP - Improved requested assets (#5289) * WIP - beginning of improved requested assets - Use Ajax tables for faster loading - Use new notifications for requesting an asset TODO: - Use ajax tables for requestable asset models - Use new notifications for canceling an asset request - Expire requests once the asset has been checked out to the requesting user * Only show asset name in email if it has one * Refactor requested method to only include non-canceled requests * Refactored requestable assets to log request and cancelation * Added softdeletes on checkout requests * Differentiate between canceling and deleting requests * Added asset request cancelation notification * Added timestamps and corrected unique key on requests table * Improved requests view * Re-use blade for cancel/request email * Refactored BS table formatter for requested assets * Location name min reduced to 2 * Added PAT test as maintenance option This needs to be refactored into database-driven options with a UI * Better slack message * Added getImageUrl method for assets * Include qty in request notifications TODO: - Try to pull requested info from original request for cancelation, otherwise it will default to 1 * Removed old asset request/cancel emails * Added user profile asset request routes * Added profile controller requested assets method * Added blade link to requested assets for profile view * Sort user history desc * Added requested assets blade * Added canceled at to checkoutRequest method * Include qty in request * Fixed comment, removed allowed_columns * Removed Queable methods, since we don’t use a queue * Fixed return type in method doc * Fixed version number * Changed id to user_id for clarity
2018-04-04 17:33:02 -07:00
$assets->where('assets.requestable', '=', '1');
}
2019-05-23 17:39:50 -07:00
if ($request->filled('model_id')) {
$assets->InModelList([$request->input('model_id')]);
}
2019-05-23 17:39:50 -07:00
if ($request->filled('category_id')) {
$assets->InCategory($request->input('category_id'));
}
2019-05-23 17:39:50 -07:00
if ($request->filled('location_id')) {
2017-11-03 20:10:36 -07:00
$assets->where('assets.location_id', '=', $request->input('location_id'));
}
if ($request->filled('rtd_location_id')) {
$assets->where('assets.rtd_location_id', '=', $request->input('rtd_location_id'));
}
2019-05-23 17:39:50 -07:00
if ($request->filled('supplier_id')) {
2017-05-15 20:55:39 -07:00
$assets->where('assets.supplier_id', '=', $request->input('supplier_id'));
}
2019-05-23 17:39:50 -07:00
if (($request->filled('assigned_to')) && ($request->filled('assigned_type'))) {
$assets->where('assets.assigned_to', '=', $request->input('assigned_to'))
2019-02-13 04:45:21 -08:00
->where('assets.assigned_type', '=', $request->input('assigned_type'));
}
2019-05-23 17:39:50 -07:00
if ($request->filled('company_id')) {
$assets->where('assets.company_id', '=', $request->input('company_id'));
}
2019-05-23 17:39:50 -07:00
if ($request->filled('manufacturer_id')) {
2017-02-03 19:52:00 -08:00
$assets->ByManufacturer($request->input('manufacturer_id'));
}
2019-05-23 17:39:50 -07:00
if ($request->filled('depreciation_id')) {
2017-10-17 11:20:05 -07:00
$assets->ByDepreciationId($request->input('depreciation_id'));
}
2019-05-23 17:39:50 -07:00
$request->filled('order_number') ? $assets = $assets->where('assets.order_number', '=', e($request->get('order_number'))) : '';
// Set the offset to the API call's offset, unless the offset is higher than the actual count of items in which
// case we override with the actual count, so we should return 0 items.
$offset = (($assets) && ($request->get('offset') > $assets->count())) ? $assets->count() : $request->get('offset', 0);
// Check to make sure the limit is not higher than the max allowed
((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
2017-01-13 11:41:00 -08:00
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
2017-01-11 18:14:06 -08:00
// This is used by the audit reporting routes
if (Gate::allows('audit', Asset::class)) {
switch ($audit) {
case 'due':
$assets->DueOrOverdueForAudit($settings);
break;
case 'overdue':
$assets->overdueForAudit($settings);
break;
}
}
2017-05-15 20:55:39 -07:00
// This is used by the sidenav, mostly
// We switched from using query scopes here because of a Laravel bug
// related to fulltext searches on complex queries.
// I am sad. :(
2017-02-08 03:37:44 -08:00
switch ($request->input('status')) {
2017-01-11 18:14:06 -08:00
case 'Deleted':
$assets->onlyTrashed();
2017-01-11 18:14:06 -08:00
break;
case 'Pending':
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.deployable', '=', 0)
->where('status_alias.pending', '=', 1)
->where('status_alias.archived', '=', 0);
});
2017-01-11 18:14:06 -08:00
break;
case 'RTD':
$assets->whereNull('assets.assigned_to')
->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.deployable', '=', 1)
->where('status_alias.pending', '=', 0)
2019-02-13 04:45:21 -08:00
->where('status_alias.archived', '=', 0);
});
2017-01-11 18:14:06 -08:00
break;
case 'Undeployable':
$assets->Undeployable();
break;
case 'Archived':
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.deployable', '=', 0)
->where('status_alias.pending', '=', 0)
->where('status_alias.archived', '=', 1);
});
2017-01-11 18:14:06 -08:00
break;
case 'Requestable':
$assets->where('assets.requestable', '=', 1)
->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.deployable', '=', 1)
->where('status_alias.pending', '=', 0)
2019-02-13 04:45:21 -08:00
->where('status_alias.archived', '=', 0);
});
2017-01-11 18:14:06 -08:00
break;
case 'Deployed':
// more sad, horrible workarounds for laravel bugs when doing full text searches
$assets->where('assets.assigned_to', '>', '0');
2017-01-11 18:14:06 -08:00
break;
2017-10-17 11:20:05 -07:00
default:
if ((! $request->filled('status_id')) && ($settings->show_archived_in_list != '1')) {
// terrible workaround for complex-query Laravel bug in fulltext
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id')
->where('status_alias.archived', '=', 0);
});
2019-02-13 04:45:21 -08:00
// If there is a status ID, don't take show_archived_in_list into consideration
} else {
$assets->join('status_labels AS status_alias', function ($join) {
$join->on('status_alias.id', '=', 'assets.status_id');
});
}
2017-01-11 18:14:06 -08:00
}
if ((! is_null($filter)) && (count($filter)) > 0) {
$assets->ByFilter($filter);
2019-05-23 17:39:50 -07:00
} elseif ($request->filled('search')) {
$assets->TextSearch($request->input('search'));
}
// This is kinda gross, but we need to do this because the Bootstrap Tables
// API passes custom field ordering as custom_fields.fieldname, and we have to strip
// that out to let the default sorter below order them correctly on the assets table.
$sort_override = str_replace('custom_fields.', '', $request->input('sort'));
// This handles all of the pivot sorting (versus the assets.* fields
// in the allowed_columns array)
$column_sort = in_array($sort_override, $allowed_columns) ? $sort_override : 'assets.created_at';
2019-02-13 04:45:21 -08:00
switch ($sort_override) {
2017-01-11 18:14:06 -08:00
case 'model':
2017-01-13 11:41:00 -08:00
$assets->OrderModels($order);
2017-01-11 18:14:06 -08:00
break;
case 'model_number':
2017-01-13 11:41:00 -08:00
$assets->OrderModelNumber($order);
2017-01-11 18:14:06 -08:00
break;
case 'category':
2017-01-13 11:41:00 -08:00
$assets->OrderCategory($order);
2017-01-11 18:14:06 -08:00
break;
case 'manufacturer':
2017-01-13 11:41:00 -08:00
$assets->OrderManufacturer($order);
2017-01-11 18:14:06 -08:00
break;
2017-01-13 11:41:00 -08:00
case 'company':
$assets->OrderCompany($order);
2017-01-11 18:14:06 -08:00
break;
case 'location':
2017-01-13 11:41:00 -08:00
$assets->OrderLocation($order);
2018-01-24 14:27:12 -08:00
case 'rtd_location':
$assets->OrderRtdLocation($order);
2017-01-11 18:14:06 -08:00
break;
case 'status_label':
2017-01-13 11:41:00 -08:00
$assets->OrderStatus($order);
2017-01-11 18:14:06 -08:00
break;
2017-05-15 20:55:39 -07:00
case 'supplier':
$assets->OrderSupplier($order);
break;
2017-01-11 18:14:06 -08:00
case 'assigned_to':
2017-01-13 11:41:00 -08:00
$assets->OrderAssigned($order);
2017-01-11 18:14:06 -08:00
break;
default:
2017-05-15 20:55:39 -07:00
$assets->orderBy($column_sort, $order);
2017-01-11 18:14:06 -08:00
break;
}
2017-01-13 11:41:00 -08:00
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
// dd($assets);
2017-01-13 11:41:00 -08:00
return (new AssetsTransformer)->transformAssets($assets, $total);
2017-01-11 18:14:06 -08:00
}
/**
* Returns JSON with information about an asset (by tag) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $tag
* @since [v4.2.1]
* @return JsonResponse
*/
public function showByTag($tag)
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->where('asset_tag', $tag)->first()) {
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
}
/**
* Returns JSON with information about an asset (by serial) for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param string $serial
* @since [v4.2.1]
* @return JsonResponse
*/
public function showBySerial($serial)
{
$this->authorize('index', Asset::class);
if ($assets = Asset::with('assetstatus')->with('assignedTo')
->withTrashed()->where('serial', $serial)->get()) {
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
}
return response()->json(Helper::formatStandardApiResponse('error', null, 'Asset not found'), 200);
}
2019-02-13 04:45:21 -08:00
/**
2017-01-11 19:00:34 -08:00
* Returns JSON with information about an asset for detail view.
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
2017-01-11 19:00:34 -08:00
*/
2017-01-12 02:19:55 -08:00
public function show($id)
2017-01-11 19:00:34 -08:00
{
if ($asset = Asset::with('assetstatus')->with('assignedTo')->withTrashed()
->withCount('checkins as checkins_count', 'checkouts as checkouts_count', 'userRequests as user_requests_count')->findOrFail($id)) {
2017-01-11 19:00:34 -08:00
$this->authorize('view', $asset);
return (new AssetsTransformer)->transformAsset($asset);
2017-01-11 19:00:34 -08:00
}
}
public function licenses($id)
{
$this->authorize('view', Asset::class);
$this->authorize('view', License::class);
$asset = Asset::where('id', $id)->withTrashed()->first();
$licenses = $asset->licenses()->get();
return (new LicensesTransformer())->transformLicenses($licenses, $licenses->count());
}
/**
* Gets a paginated collection for the select2 menus
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0.16]
* @see \App\Http\Transformers\SelectlistTransformer
*/
public function selectlist(Request $request)
{
$assets = Company::scopeCompanyables(Asset::select([
'assets.id',
'assets.name',
'assets.asset_tag',
'assets.model_id',
'assets.assigned_to',
'assets.assigned_type',
'assets.status_id',
2019-02-13 06:52:36 -08:00
])->with('model', 'assetstatus', 'assignedTo')->NotArchived(), 'company_id', 'assets');
2019-05-23 17:39:50 -07:00
if ($request->filled('assetStatusType') && $request->input('assetStatusType') === 'RTD') {
$assets = $assets->RTD();
}
2019-05-23 17:39:50 -07:00
if ($request->filled('search')) {
$assets = $assets->AssignedSearch($request->input('search'));
}
$assets = $assets->paginate(50);
// Loop through and set some custom properties for the transformer to use.
// This lets us have more flexibility in special cases like assets, where
// they may not have a ->name value but we want to display something anyway
foreach ($assets as $asset) {
$asset->use_text = $asset->present()->fullName;
2017-11-22 15:07:34 -08:00
if (($asset->checkedOutToUser()) && ($asset->assigned)) {
$asset->use_text .= ' → '.$asset->assigned->getFullNameAttribute();
}
if ($asset->assetstatus->getStatuslabelType() == 'pending') {
$asset->use_text .= '('.$asset->assetstatus->getStatuslabelType().')';
}
$asset->use_image = ($asset->getImageUrl()) ? $asset->getImageUrl() : null;
}
return (new SelectlistTransformer)->transformSelectlist($assets);
}
/**
* Accepts a POST request to create a new asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
2021-06-29 02:26:24 -07:00
* @param \App\Http\Requests\ImageUploadRequest $request
* @since [v4.0]
2017-01-12 02:19:55 -08:00
* @return JsonResponse
*/
2021-06-29 02:26:24 -07:00
public function store(ImageUploadRequest $request)
{
2017-08-25 03:26:50 -07:00
$this->authorize('create', Asset::class);
$asset = new Asset();
$asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
$asset->name = $request->get('name');
$asset->serial = $request->get('serial');
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id'));
$asset->model_id = $request->get('model_id');
$asset->order_number = $request->get('order_number');
$asset->notes = $request->get('notes');
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
$asset->user_id = Auth::id();
$asset->archived = '0';
$asset->physical = '1';
$asset->depreciate = '0';
$asset->status_id = $request->get('status_id', 0);
$asset->warranty_months = $request->get('warranty_months', null);
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost'));
$asset->purchase_date = $request->get('purchase_date', null);
$asset->assigned_to = $request->get('assigned_to', null);
$asset->supplier_id = $request->get('supplier_id', 0);
$asset->requestable = $request->get('requestable', 0);
$asset->rtd_location_id = $request->get('rtd_location_id', null);
$asset->location_id = $request->get('rtd_location_id', null);
2021-06-29 02:26:24 -07:00
$asset = $request->handleImages($asset);
// Update custom fields in the database.
// Validation for these fields is handled through the AssetRequest form request
$model = AssetModel::find($request->get('model_id'));
if (($model) && ($model->fieldset)) {
foreach ($model->fieldset->fields as $field) {
// Set the field value based on what was sent in the request
$field_val = $request->input($field->convertUnicodeDbSlug(), null);
// If input value is null, use custom field's default value
if ($field_val == null) {
\Log::debug('Field value for '.$field->convertUnicodeDbSlug().' is null');
$field_val = $field->defaultValue($request->get('model_id'));
\Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
}
// if the field is set to encrypted, make sure we encrypt the value
if ($field->field_encrypted == '1') {
\Log::debug('This model field is encrypted in this fieldset.');
if (Gate::allows('admin')) {
// If input value is null, use custom field's default value
if (($field_val == null) && ($request->has('model_id') != '')) {
$field_val = \Crypt::encrypt($field->defaultValue($request->get('model_id')));
} else {
$field_val = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
}
}
$asset->{$field->convertUnicodeDbSlug()} = $field_val;
}
}
if ($asset->save()) {
if ($request->get('assigned_user')) {
$target = User::find(request('assigned_user'));
} elseif ($request->get('assigned_asset')) {
$target = Asset::find(request('assigned_asset'));
} elseif ($request->get('assigned_location')) {
$target = Location::find(request('assigned_location'));
}
2017-01-12 02:19:55 -08:00
if (isset($target)) {
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')));
}
if ($asset->image) {
$asset->image = $asset->getImageUrl();
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.create.success')));
}
2017-08-25 03:26:50 -07:00
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
}
2017-01-12 03:48:18 -08:00
/**
* Accepts a POST request to update an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
2021-06-29 02:26:24 -07:00
* @param \App\Http\Requests\ImageUploadRequest $request
2017-01-12 03:48:18 -08:00
* @since [v4.0]
* @return JsonResponse
*/
2021-06-29 02:26:24 -07:00
public function update(ImageUploadRequest $request, $id)
2017-01-12 03:48:18 -08:00
{
$this->authorize('update', Asset::class);
2017-01-12 03:48:18 -08:00
if ($asset = Asset::find($id)) {
$asset->fill($request->all());
2019-05-23 17:39:50 -07:00
($request->filled('model_id')) ?
$asset->model()->associate(AssetModel::find($request->get('model_id'))) : null;
($request->filled('rtd_location_id')) ?
2017-11-03 19:42:45 -07:00
$asset->location_id = $request->get('rtd_location_id') : '';
2019-05-23 17:39:50 -07:00
($request->filled('company_id')) ?
2017-01-12 03:48:18 -08:00
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id')) : '';
($request->filled('rtd_location_id')) ?
Merge branch 'develop' into v5-master-develop-integration # Conflicts: # .env.example # .travis.yml # Dockerfile # README.md # app/Console/Commands/LdapSync.php # app/Console/Kernel.php # app/Http/Controllers/AccessoriesController.php # app/Http/Controllers/Api/AccessoriesController.php # app/Http/Controllers/Api/AssetsController.php # app/Http/Controllers/Api/LocationsController.php # app/Http/Controllers/Api/SettingsController.php # app/Http/Controllers/Api/UsersController.php # app/Http/Controllers/AssetModelsController.php # app/Http/Controllers/Assets/AssetFilesController.php # app/Http/Controllers/Assets/AssetsController.php # app/Http/Controllers/CategoriesController.php # app/Http/Controllers/CompaniesController.php # app/Http/Controllers/ComponentsController.php # app/Http/Controllers/ConsumablesController.php # app/Http/Controllers/DepartmentsController.php # app/Http/Controllers/LicensesController.php # app/Http/Controllers/LocationsController.php # app/Http/Controllers/ManufacturersController.php # app/Http/Controllers/ReportsController.php # app/Http/Controllers/SettingsController.php # app/Http/Controllers/SuppliersController.php # app/Http/Controllers/UsersController.php # app/Http/Middleware/EncryptCookies.php # app/Http/Requests/AssetRequest.php # app/Http/Transformers/AssetMaintenancesTransformer.php # app/Importer/AssetImporter.php # app/Models/AssetMaintenance.php # app/Models/Location.php # app/Models/User.php # composer.json # composer.lock # config/backup.php # config/database.php # config/version.php # public/mix-manifest.json # resources/lang/en-ID/general.php # resources/lang/vi/admin/settings/general.php # resources/views/accessories/edit.blade.php # resources/views/hardware/view.blade.php # resources/views/layouts/default.blade.php # tests/api/ApiCategoriesCest.php
2019-11-18 19:49:39 -08:00
$asset->location_id = $request->get('rtd_location_id') : null;
2021-06-29 02:26:24 -07:00
$asset = $request->handleImages($asset);
// Update custom fields
if (($model = AssetModel::find($asset->model_id)) && (isset($model->fieldset))) {
foreach ($model->fieldset->fields as $field) {
if ($request->has($field->convertUnicodeDbSlug())) {
if ($field->field_encrypted == '1') {
if (Gate::allows('admin')) {
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt($request->input($field->convertUnicodeDbSlug()));
}
} else {
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
}
2017-01-12 03:48:18 -08:00
}
}
}
if ($asset->save()) {
2019-05-23 17:39:50 -07:00
if (($request->filled('assigned_user')) && ($target = User::find($request->get('assigned_user')))) {
$location = $target->location_id;
2019-05-23 17:39:50 -07:00
} elseif (($request->filled('assigned_asset')) && ($target = Asset::find($request->get('assigned_asset')))) {
2019-02-13 04:45:21 -08:00
$location = $target->location_id;
Asset::where('assigned_type', \App\Models\Asset::class)->where('assigned_to', $id)
->update(['location_id' => $target->location_id]);
2019-05-23 17:39:50 -07:00
} elseif (($request->filled('assigned_location')) && ($target = Location::find($request->get('assigned_location')))) {
2017-11-03 19:42:45 -07:00
$location = $target->id;
2017-01-12 03:48:18 -08:00
}
if (isset($target)) {
2017-11-03 19:42:45 -07:00
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset update', e($request->get('name')), $location);
2017-01-12 03:48:18 -08:00
}
if ($asset->image) {
$asset->image = $asset->getImageUrl();
}
return response()->json(Helper::formatStandardApiResponse('success', $asset, trans('admin/hardware/message.update.success')));
2017-01-12 03:48:18 -08:00
}
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()), 200);
2017-01-12 03:48:18 -08:00
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
2017-01-12 03:48:18 -08:00
}
/**
* Delete a given asset (mark as deleted).
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
2017-01-12 02:19:55 -08:00
* @return JsonResponse
*/
public function destroy($id)
{
$this->authorize('delete', Asset::class);
if ($asset = Asset::find($id)) {
$this->authorize('delete', $asset);
DB::table('assets')
->where('id', $asset->id)
->update(['assigned_to' => null]);
$asset->delete();
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.delete.success')));
}
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
}
/**
* Checkout an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
2017-11-27 21:17:16 -08:00
public function checkout(AssetCheckoutRequest $request, $asset_id)
{
$this->authorize('checkout', Asset::class);
$asset = Asset::findOrFail($asset_id);
if (! $asset->availableForCheckout()) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.not_available')));
}
$this->authorize('checkout', $asset);
$error_payload = [];
$error_payload['asset'] = [
'id' => $asset->id,
'asset_tag' => $asset->asset_tag,
];
// This item is checked out to a location
if (request('checkout_to_type') == 'location') {
$target = Location::find(request('assigned_location'));
$asset->location_id = ($target) ? $target->id : '';
$error_payload['target_id'] = $request->input('assigned_location');
$error_payload['target_type'] = 'location';
} elseif (request('checkout_to_type') == 'asset') {
$target = Asset::where('id', '!=', $asset_id)->find(request('assigned_asset'));
$asset->location_id = $target->rtd_location_id;
// Override with the asset's location_id if it has one
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_asset');
$error_payload['target_type'] = 'asset';
} elseif (request('checkout_to_type') == 'user') {
// Fetch the target and set the asset's new location_id
$target = User::find(request('assigned_user'));
$asset->location_id = (($target) && (isset($target->location_id))) ? $target->location_id : '';
$error_payload['target_id'] = $request->input('assigned_user');
$error_payload['target_type'] = 'user';
}
if (! isset($target)) {
return response()->json(Helper::formatStandardApiResponse('error', $error_payload, 'Checkout target for asset '.e($asset->asset_tag).' is invalid - '.$error_payload['target_type'].' does not exist.'));
}
$checkout_at = request('checkout_at', date('Y-m-d H:i:s'));
$expected_checkin = request('expected_checkin', null);
$note = request('note', null);
$asset_name = request('name', null);
2019-02-13 04:45:21 -08:00
// Set the location ID to the RTD location id if there is one
// Wait, why are we doing this? This overrides the stuff we set further up, which makes no sense.
// TODO: Follow up here. WTF. Commented out for now.
// if ((isset($target->rtd_location_id)) && ($asset->rtd_location_id!='')) {
// $asset->location_id = $target->rtd_location_id;
// }
if ($asset->checkOut($target, Auth::user(), $checkout_at, $expected_checkin, $note, $asset_name, $asset->location_id)) {
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.success')));
}
2019-08-22 21:36:47 -07:00
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkout.error')));
}
/**
* Checkin an asset
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $assetId
* @since [v4.0]
* @return JsonResponse
*/
2017-11-03 19:42:45 -07:00
public function checkin(Request $request, $asset_id)
{
$this->authorize('checkin', Asset::class);
$asset = Asset::findOrFail($asset_id);
$this->authorize('checkin', $asset);
$user = $asset->assignedUser;
if (is_null($target = $asset->assignedTo)) {
return response()->json(Helper::formatStandardApiResponse('error', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.already_checked_in')));
}
$asset->expected_checkin = null;
$asset->last_checkout = null;
$asset->assigned_to = null;
$asset->assignedTo()->disassociate($asset);
$asset->accepted = null;
if ($request->filled('name')) {
$asset->name = $request->input('name');
}
$asset->location_id = $asset->rtd_location_id;
2017-11-03 19:42:45 -07:00
2019-05-23 17:39:50 -07:00
if ($request->filled('location_id')) {
$asset->location_id = $request->input('location_id');
2017-11-03 19:42:45 -07:00
}
if ($request->has('status_id')) {
$asset->status_id = $request->input('status_id');
}
if ($asset->save()) {
$asset->logCheckin($target, e($request->input('note')));
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.success')));
}
return response()->json(Helper::formatStandardApiResponse('success', ['asset'=> e($asset->asset_tag)], trans('admin/hardware/message.checkin.error')));
}
/**
* Mark an asset as audited
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @param int $id
* @since [v4.0]
* @return JsonResponse
*/
public function audit(Request $request)
{
$this->authorize('audit', Asset::class);
$rules = [
'asset_tag' => 'required',
2017-08-25 18:40:20 -07:00
'location_id' => 'exists:locations,id|nullable|numeric',
'next_audit_date' => 'date|nullable',
];
$validator = Validator::make($request->all(), $rules);
2017-08-25 18:40:20 -07:00
if ($validator->fails()) {
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
}
$settings = Setting::getSettings();
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
if ($asset) {
// We don't want to log this as a normal update, so let's bypass that
$asset->unsetEventDispatcher();
$asset->next_audit_date = $dt;
if ($request->filled('next_audit_date')) {
$asset->next_audit_date = $request->input('next_audit_date');
}
// Check to see if they checked the box to update the physical location,
// not just note it in the audit notes
if ($request->input('update_location') == '1') {
$asset->location_id = $request->input('location_id');
}
$asset->last_audit_date = date('Y-m-d H:i:s');
if ($asset->save()) {
$log = $asset->logAudit(request('note'), request('location_id'));
return response()->json(Helper::formatStandardApiResponse('success', [
'asset_tag'=> e($asset->asset_tag),
'note'=> e($request->input('note')),
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
], trans('admin/hardware/message.audit.success')));
}
}
return response()->json(Helper::formatStandardApiResponse('error', ['asset_tag'=> e($request->input('asset_tag'))], 'Asset with tag '.$request->input('asset_tag').' not found'));
WIP - Improved requested assets (#5289) * WIP - beginning of improved requested assets - Use Ajax tables for faster loading - Use new notifications for requesting an asset TODO: - Use ajax tables for requestable asset models - Use new notifications for canceling an asset request - Expire requests once the asset has been checked out to the requesting user * Only show asset name in email if it has one * Refactor requested method to only include non-canceled requests * Refactored requestable assets to log request and cancelation * Added softdeletes on checkout requests * Differentiate between canceling and deleting requests * Added asset request cancelation notification * Added timestamps and corrected unique key on requests table * Improved requests view * Re-use blade for cancel/request email * Refactored BS table formatter for requested assets * Location name min reduced to 2 * Added PAT test as maintenance option This needs to be refactored into database-driven options with a UI * Better slack message * Added getImageUrl method for assets * Include qty in request notifications TODO: - Try to pull requested info from original request for cancelation, otherwise it will default to 1 * Removed old asset request/cancel emails * Added user profile asset request routes * Added profile controller requested assets method * Added blade link to requested assets for profile view * Sort user history desc * Added requested assets blade * Added canceled at to checkoutRequest method * Include qty in request * Fixed comment, removed allowed_columns * Removed Queable methods, since we don’t use a queue * Fixed return type in method doc * Fixed version number * Changed id to user_id for clarity
2018-04-04 17:33:02 -07:00
}
/**
* Returns JSON listing of all requestable assets
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v4.0]
* @return JsonResponse
*/
public function requestable(Request $request)
{
$this->authorize('viewRequestable', Asset::class);
$assets = Company::scopeCompanyables(Asset::select('assets.*'), 'company_id', 'assets')
WIP - Improved requested assets (#5289) * WIP - beginning of improved requested assets - Use Ajax tables for faster loading - Use new notifications for requesting an asset TODO: - Use ajax tables for requestable asset models - Use new notifications for canceling an asset request - Expire requests once the asset has been checked out to the requesting user * Only show asset name in email if it has one * Refactor requested method to only include non-canceled requests * Refactored requestable assets to log request and cancelation * Added softdeletes on checkout requests * Differentiate between canceling and deleting requests * Added asset request cancelation notification * Added timestamps and corrected unique key on requests table * Improved requests view * Re-use blade for cancel/request email * Refactored BS table formatter for requested assets * Location name min reduced to 2 * Added PAT test as maintenance option This needs to be refactored into database-driven options with a UI * Better slack message * Added getImageUrl method for assets * Include qty in request notifications TODO: - Try to pull requested info from original request for cancelation, otherwise it will default to 1 * Removed old asset request/cancel emails * Added user profile asset request routes * Added profile controller requested assets method * Added blade link to requested assets for profile view * Sort user history desc * Added requested assets blade * Added canceled at to checkoutRequest method * Include qty in request * Fixed comment, removed allowed_columns * Removed Queable methods, since we don’t use a queue * Fixed return type in method doc * Fixed version number * Changed id to user_id for clarity
2018-04-04 17:33:02 -07:00
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset', 'supplier')->where('assets.requestable', '=', '1');
WIP - Improved requested assets (#5289) * WIP - beginning of improved requested assets - Use Ajax tables for faster loading - Use new notifications for requesting an asset TODO: - Use ajax tables for requestable asset models - Use new notifications for canceling an asset request - Expire requests once the asset has been checked out to the requesting user * Only show asset name in email if it has one * Refactor requested method to only include non-canceled requests * Refactored requestable assets to log request and cancelation * Added softdeletes on checkout requests * Differentiate between canceling and deleting requests * Added asset request cancelation notification * Added timestamps and corrected unique key on requests table * Improved requests view * Re-use blade for cancel/request email * Refactored BS table formatter for requested assets * Location name min reduced to 2 * Added PAT test as maintenance option This needs to be refactored into database-driven options with a UI * Better slack message * Added getImageUrl method for assets * Include qty in request notifications TODO: - Try to pull requested info from original request for cancelation, otherwise it will default to 1 * Removed old asset request/cancel emails * Added user profile asset request routes * Added profile controller requested assets method * Added blade link to requested assets for profile view * Sort user history desc * Added requested assets blade * Added canceled at to checkoutRequest method * Include qty in request * Fixed comment, removed allowed_columns * Removed Queable methods, since we don’t use a queue * Fixed return type in method doc * Fixed version number * Changed id to user_id for clarity
2018-04-04 17:33:02 -07:00
$offset = request('offset', 0);
$limit = $request->input('limit', 50);
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
$assets->TextSearch($request->input('search'));
switch ($request->input('sort')) {
case 'model':
$assets->OrderModels($order);
break;
case 'model_number':
$assets->OrderModelNumber($order);
break;
case 'category':
$assets->OrderCategory($order);
break;
case 'manufacturer':
$assets->OrderManufacturer($order);
break;
default:
$assets->orderBy('assets.created_at', $order);
break;
}
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
WIP - Improved requested assets (#5289) * WIP - beginning of improved requested assets - Use Ajax tables for faster loading - Use new notifications for requesting an asset TODO: - Use ajax tables for requestable asset models - Use new notifications for canceling an asset request - Expire requests once the asset has been checked out to the requesting user * Only show asset name in email if it has one * Refactor requested method to only include non-canceled requests * Refactored requestable assets to log request and cancelation * Added softdeletes on checkout requests * Differentiate between canceling and deleting requests * Added asset request cancelation notification * Added timestamps and corrected unique key on requests table * Improved requests view * Re-use blade for cancel/request email * Refactored BS table formatter for requested assets * Location name min reduced to 2 * Added PAT test as maintenance option This needs to be refactored into database-driven options with a UI * Better slack message * Added getImageUrl method for assets * Include qty in request notifications TODO: - Try to pull requested info from original request for cancelation, otherwise it will default to 1 * Removed old asset request/cancel emails * Added user profile asset request routes * Added profile controller requested assets method * Added blade link to requested assets for profile view * Sort user history desc * Added requested assets blade * Added canceled at to checkoutRequest method * Include qty in request * Fixed comment, removed allowed_columns * Removed Queable methods, since we don’t use a queue * Fixed return type in method doc * Fixed version number * Changed id to user_id for clarity
2018-04-04 17:33:02 -07:00
return (new AssetsTransformer)->transformRequestedAssets($assets, $total);
}
2017-01-11 18:14:06 -08:00
}