mirror of
https://github.com/snipe/snipe-it.git
synced 2025-03-05 20:52:15 -08:00
# Conflicts: # README.md # app/Http/Controllers/AccessoriesController.php # app/Http/Controllers/Api/AssetsController.php # app/Http/Controllers/Api/LicensesController.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/AssetsController.php # app/Http/Controllers/Auth/ForgotPasswordController.php # app/Http/Controllers/CategoriesController.php # app/Http/Controllers/CompaniesController.php # app/Http/Controllers/ComponentsController.php # app/Http/Controllers/ConsumablesController.php # app/Http/Controllers/CustomFieldsetsController.php # app/Http/Controllers/DepartmentsController.php # app/Http/Controllers/LicensesController.php # app/Http/Controllers/LocationsController.php # app/Http/Controllers/ManufacturersController.php # app/Http/Controllers/SettingsController.php # app/Http/Controllers/SuppliersController.php # app/Http/Controllers/UsersController.php # app/Http/Requests/AssetRequest.php # app/Http/Requests/ImageUploadRequest.php # app/Models/LicenseSeat.php # app/Models/Location.php # app/Models/Setting.php # composer.json # composer.lock # config/database.php # config/version.php # npm-shrinkwrap.json # package.json # public/css/AdminLTE.css # public/css/AdminLTE.css.map # public/css/overrides.css # public/css/overrides.css.map # public/css/skins/skin-blue-light.css # public/css/skins/skin-blue.css # public/css/skins/skin-green-dark.min.css # public/js/app.js # public/js/bootstrap-table.js # public/js/bootstrap/js/bootstrap.js # public/js/bootstrap/js/bootstrap.min.js # public/js/build/all.js # public/js/build/vue.js # public/js/build/vue.js.map # public/js/demo.js # public/js/ekko-lightbox.js # public/js/ekko-lightbox.min.js # public/js/extensions/export/bootstrap-table-export.js # public/js/extensions/multiple-sort/bootstrap-table-multiple-sort.js # public/js/extensions/multiple-sort/bootstrap-table-multiple-sort.min.js # public/js/extensions/toolbar/bootstrap-table-toolbar.min.js # public/js/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.js # public/js/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js # public/js/plugins/timepicker/bootstrap-timepicker.js # public/js/plugins/timepicker/bootstrap-timepicker.min.js # public/js/vue.js # public/mix-manifest.json # resources/assets/js/bootstrap-js.js # resources/assets/js/bootstrap.min.js # resources/assets/js/ekko-lightbox.js # resources/assets/js/ekko-lightbox.min.js # resources/assets/js/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.js # resources/assets/js/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js # resources/assets/js/plugins/chartjs/Chart.js # resources/assets/js/plugins/timepicker/bootstrap-timepicker.js # resources/assets/js/plugins/timepicker/bootstrap-timepicker.min.js # resources/assets/less/AdminLTE.less # resources/assets/less/overrides.less # resources/assets/less/skins/_all-skins.less # resources/assets/less/skins/skin-black.less # resources/assets/less/skins/skin-blue.less # resources/assets/less/skins/skin-green.less # resources/assets/less/skins/skin-purple.less # resources/assets/less/skins/skin-red.less # resources/assets/less/skins/skin-yellow.less # resources/assets/less/variables.less # resources/js/components/importer/importer-file.vue # resources/lang/en/auth/message.php # resources/lang/en/passwords.php # resources/lang/es-CO/general.php # resources/lang/es-ES/general.php # resources/lang/es-VE/general.php # resources/less/skins/skin-black-dark.less # resources/less/skins/skin-blue-dark.less # resources/less/skins/skin-contrast.less # resources/less/skins/skin-green-dark.less # resources/less/skins/skin-orange-dark.less # resources/less/skins/skin-orange.less # resources/less/skins/skin-purple-dark.less # resources/less/skins/skin-red-dark.less # resources/less/skins/skin-yellow-dark.less # resources/views/accessories/checkin.blade.php # resources/views/accessories/checkout.blade.php # resources/views/accessories/edit.blade.php # resources/views/account/profile.blade.php # resources/views/account/view-assets.blade.php # resources/views/asset_maintenances/edit.blade.php # resources/views/auth/passwords/email.blade.php # resources/views/auth/passwords/reset.blade.php # resources/views/categories/edit.blade.php # resources/views/companies/edit.blade.php # resources/views/components/checkin.blade.php # resources/views/components/checkout.blade.php # resources/views/components/edit.blade.php # resources/views/consumables/checkout.blade.php # resources/views/consumables/edit.blade.php # resources/views/custom_fields/fields/edit.blade.php # resources/views/custom_fields/fieldsets/edit.blade.php # resources/views/dashboard.blade.php # resources/views/departments/edit.blade.php # resources/views/groups/edit.blade.php # resources/views/hardware/audit.blade.php # resources/views/hardware/bulk-checkout.blade.php # resources/views/hardware/bulk.blade.php # resources/views/hardware/checkin.blade.php # resources/views/hardware/checkout.blade.php # resources/views/hardware/edit.blade.php # resources/views/hardware/index.blade.php # resources/views/hardware/quickscan.blade.php # resources/views/hardware/view.blade.php # resources/views/importer/import.blade.php # resources/views/layouts/basic.blade.php # resources/views/layouts/default.blade.php # resources/views/layouts/edit-form.blade.php # resources/views/licenses/checkin.blade.php # resources/views/licenses/checkout.blade.php # resources/views/licenses/edit.blade.php # resources/views/locations/edit.blade.php # resources/views/manufacturers/edit.blade.php # resources/views/modals/upload-file.blade.php # resources/views/models/bulk-edit.blade.php # resources/views/models/custom_fields_form.blade.php # resources/views/models/edit.blade.php # resources/views/partials/bootstrap-table.blade.php # resources/views/partials/forms/edit/address.blade.php # resources/views/partials/forms/edit/asset-select.blade.php # resources/views/partials/forms/edit/category-select.blade.php # resources/views/partials/forms/edit/category.blade.php # resources/views/partials/forms/edit/company-select.blade.php # resources/views/partials/forms/edit/company.blade.php # resources/views/partials/forms/edit/department-select.blade.php # resources/views/partials/forms/edit/depreciation.blade.php # resources/views/partials/forms/edit/email.blade.php # resources/views/partials/forms/edit/image-upload.blade.php # resources/views/partials/forms/edit/item_number.blade.php # resources/views/partials/forms/edit/location-profile-select.blade.php # resources/views/partials/forms/edit/location-select.blade.php # resources/views/partials/forms/edit/location.blade.php # resources/views/partials/forms/edit/maintenance_type.blade.php # resources/views/partials/forms/edit/manufacturer-select.blade.php # resources/views/partials/forms/edit/manufacturer.blade.php # resources/views/partials/forms/edit/minimum_quantity.blade.php # resources/views/partials/forms/edit/model-select.blade.php # resources/views/partials/forms/edit/model_number.blade.php # resources/views/partials/forms/edit/name.blade.php # resources/views/partials/forms/edit/notes.blade.php # resources/views/partials/forms/edit/order_number.blade.php # resources/views/partials/forms/edit/phone.blade.php # resources/views/partials/forms/edit/purchase_cost.blade.php # resources/views/partials/forms/edit/purchase_date.blade.php # resources/views/partials/forms/edit/quantity.blade.php # resources/views/partials/forms/edit/serial.blade.php # resources/views/partials/forms/edit/status.blade.php # resources/views/partials/forms/edit/submit.blade.php # resources/views/partials/forms/edit/supplier-select.blade.php # resources/views/partials/forms/edit/supplier.blade.php # resources/views/partials/forms/edit/user-select.blade.php # resources/views/reports/custom.blade.php # resources/views/settings/alerts.blade.php # resources/views/settings/asset_tags.blade.php # resources/views/settings/barcodes.blade.php # resources/views/settings/branding.blade.php # resources/views/settings/general.blade.php # resources/views/settings/labels.blade.php # resources/views/settings/ldap.blade.php # resources/views/settings/localization.blade.php # resources/views/settings/security.blade.php # resources/views/setup/user.blade.php # resources/views/suppliers/edit.blade.php # resources/views/users/bulk-edit.blade.php # resources/views/users/edit.blade.php # resources/views/users/ldap.blade.php # resources/views/users/print.blade.php # resources/views/users/view.blade.php # routes/api.php # routes/web/hardware.php # webpack.mix.js
866 lines
34 KiB
PHP
Executable file
866 lines
34 KiB
PHP
Executable file
<?php
|
|
namespace App\Http\Controllers\Assets;
|
|
|
|
use App\Helpers\Helper;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\ImageUploadRequest;
|
|
use App\Models\Actionlog;
|
|
use App\Models\Asset;
|
|
use App\Models\AssetModel;
|
|
use App\Models\CheckoutRequest;
|
|
use App\Models\Company;
|
|
use App\Models\Location;
|
|
use App\Models\Setting;
|
|
use App\Models\User;
|
|
use Auth;
|
|
use Carbon\Carbon;
|
|
use DB;
|
|
use Gate;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Input;
|
|
use League\Csv\Reader;
|
|
use League\Csv\Statement;
|
|
use Paginator;
|
|
use Redirect;
|
|
use Response;
|
|
use Slack;
|
|
use Str;
|
|
use TCPDF;
|
|
use View;
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
protected $qrCodeDimensions = array( 'height' => 3.5, 'width' => 3.5);
|
|
protected $barCodeDimensions = array( 'height' => 2, 'width' => 22);
|
|
|
|
|
|
public function __construct()
|
|
{
|
|
$this->middleware('auth');
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* Returns a view that invokes the ajax tables which actually contains
|
|
* the content for the assets listing, which is generated in getDatatable.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @see AssetController::getDatatable() method that generates the JSON response
|
|
* @since [v1.0]
|
|
* @param Request $request
|
|
* @return View
|
|
* @throws \Illuminate\Auth\Access\AuthorizationException
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorize('index', Asset::class);
|
|
if ($request->filled('company_id')) {
|
|
$company = Company::find($request->input('company_id'));
|
|
} else {
|
|
$company = null;
|
|
}
|
|
return view('hardware/index')->with('company', $company);
|
|
}
|
|
|
|
/**
|
|
* Returns a view that presents a form to create a new asset.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @since [v1.0]
|
|
* @param Request $request
|
|
* @return View
|
|
* @internal param int $model_id
|
|
*/
|
|
public function create(Request $request)
|
|
{
|
|
$this->authorize('create', Asset::class);
|
|
$view = View::make('hardware/edit')
|
|
->with('statuslabel_list', Helper::statusLabelList())
|
|
->with('item', new Asset)
|
|
->with('statuslabel_types', Helper::statusTypeList());
|
|
|
|
if ($request->filled('model_id')) {
|
|
$selected_model = AssetModel::find($request->input('model_id'));
|
|
$view->with('selected_model', $selected_model);
|
|
}
|
|
return $view;
|
|
}
|
|
|
|
/**
|
|
* Validate and process new asset form data.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @since [v1.0]
|
|
* @return Redirect
|
|
*/
|
|
public function store(ImageUploadRequest $request)
|
|
{
|
|
$this->authorize(Asset::class);
|
|
|
|
// Handle asset tags - there could be one, or potentially many.
|
|
// This is only necessary on create, not update, since bulk editing is handled
|
|
// differently
|
|
$asset_tags = $request->input('asset_tags');
|
|
|
|
$success = false;
|
|
$serials = $request->input('serials');
|
|
|
|
for ($a = 1; $a <= count($asset_tags); $a++) {
|
|
|
|
$asset = new Asset();
|
|
$asset->model()->associate(AssetModel::find($request->input('model_id')));
|
|
$asset->name = $request->input('name');
|
|
|
|
// Check for a corresponding serial
|
|
if (($serials) && (array_key_exists($a, $serials))) {
|
|
$asset->serial = $serials[$a];
|
|
}
|
|
|
|
if (($asset_tags) && (array_key_exists($a, $asset_tags))) {
|
|
$asset->asset_tag = $asset_tags[$a];
|
|
}
|
|
|
|
$asset->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
|
$asset->model_id = $request->input('model_id');
|
|
$asset->order_number = $request->input('order_number');
|
|
$asset->notes = $request->input('notes');
|
|
$asset->user_id = Auth::id();
|
|
$asset->archived = '0';
|
|
$asset->physical = '1';
|
|
$asset->depreciate = '0';
|
|
$asset->status_id = request('status_id', 0);
|
|
$asset->warranty_months = request('warranty_months', null);
|
|
$asset->purchase_cost = Helper::ParseFloat($request->get('purchase_cost'));
|
|
$asset->purchase_date = request('purchase_date', null);
|
|
$asset->assigned_to = request('assigned_to', null);
|
|
$asset->supplier_id = request('supplier_id', 0);
|
|
$asset->requestable = request('requestable', 0);
|
|
$asset->rtd_location_id = request('rtd_location_id', null);
|
|
|
|
if (!empty($settings->audit_interval)) {
|
|
$asset->next_audit_date = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
|
}
|
|
|
|
if ($asset->assigned_to=='') {
|
|
$asset->location_id = $request->input('rtd_location_id', null);
|
|
}
|
|
|
|
// Create the image (if one was chosen.)
|
|
if ($request->has('image')) {
|
|
$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) {
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate the asset before saving
|
|
if ($asset->isValid() && $asset->save()) {
|
|
|
|
if (request('assigned_user')) {
|
|
$target = User::find(request('assigned_user'));
|
|
$location = $target->location_id;
|
|
} elseif (request('assigned_asset')) {
|
|
$target = Asset::find(request('assigned_asset'));
|
|
$location = $target->location_id;
|
|
} elseif (request('assigned_location')) {
|
|
$target = Location::find(request('assigned_location'));
|
|
$location = $target->id;
|
|
}
|
|
|
|
if (isset($target)) {
|
|
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')), $location);
|
|
}
|
|
|
|
$success = true;
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($success) {
|
|
$asset->checkOut($target, Auth::user(), date('Y-m-d H:i:s'), '', 'Checked out on asset creation', e($request->get('name')), $location);
|
|
}
|
|
// Redirect to the asset listing page
|
|
return redirect()->route('hardware.index')
|
|
->with('success', trans('admin/hardware/message.create.success'));
|
|
}
|
|
|
|
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns a view that presents a form to edit an existing asset.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return View
|
|
*/
|
|
public function edit($assetId = null)
|
|
{
|
|
if (!$item = Asset::find($assetId)) {
|
|
// Redirect to the asset management page with error
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
//Handles company checks and permissions.
|
|
$this->authorize($item);
|
|
|
|
return view('hardware/edit', compact('item'))
|
|
->with('statuslabel_list', Helper::statusLabelList())
|
|
->with('statuslabel_types', Helper::statusTypeList());
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a view that presents information about an asset for detail view.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return View
|
|
*/
|
|
public function show($assetId = null)
|
|
{
|
|
$asset = Asset::withTrashed()->find($assetId);
|
|
$this->authorize('view', $asset);
|
|
$settings = Setting::getSettings();
|
|
|
|
if (isset($asset)) {
|
|
$audit_log = Actionlog::where('action_type', '=', 'audit')
|
|
->where('item_id', '=', $assetId)
|
|
->where('item_type', '=', Asset::class)
|
|
->orderBy('created_at', 'DESC')->first();
|
|
|
|
if ($asset->location) {
|
|
$use_currency = $asset->location->currency;
|
|
} else {
|
|
if ($settings->default_currency!='') {
|
|
$use_currency = $settings->default_currency;
|
|
} else {
|
|
$use_currency = trans('general.currency');
|
|
}
|
|
}
|
|
|
|
$qr_code = (object) array(
|
|
'display' => $settings->qr_code == '1',
|
|
'url' => route('qr_code/hardware', $asset->id)
|
|
);
|
|
|
|
return view('hardware/view', compact('asset', 'qr_code', 'settings'))
|
|
->with('use_currency', $use_currency)->with('audit_log', $audit_log);
|
|
}
|
|
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate and process asset edit form.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return Redirect
|
|
*/
|
|
|
|
public function update(ImageUploadRequest $request, $assetId = null)
|
|
{
|
|
// Check if the asset exists
|
|
if (!$asset = Asset::find($assetId)) {
|
|
// Redirect to the asset management page with error
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
$this->authorize($asset);
|
|
|
|
$asset->status_id = $request->input('status_id', null);
|
|
$asset->warranty_months = $request->input('warranty_months', null);
|
|
$asset->purchase_cost = Helper::ParseFloat($request->input('purchase_cost', null));
|
|
$asset->purchase_date = $request->input('purchase_date', null);
|
|
$asset->supplier_id = $request->input('supplier_id', null);
|
|
|
|
// If the box isn't checked, it's not in the request at all.
|
|
$asset->requestable = $request->filled('requestable');
|
|
$asset->rtd_location_id = $request->input('rtd_location_id', null);
|
|
|
|
if ($asset->assigned_to=='') {
|
|
$asset->location_id = $request->input('rtd_location_id', null);
|
|
}
|
|
|
|
|
|
if ($request->filled('image_delete')) {
|
|
try {
|
|
unlink(public_path().'/uploads/assets/'.$asset->image);
|
|
$asset->image = '';
|
|
} catch (\Exception $e) {
|
|
\Log::debug($e);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Update the asset data
|
|
$asset_tag = $request->input('asset_tags');
|
|
$serial = $request->input('serials');
|
|
$asset->name = $request->input('name');
|
|
$asset->serial = $serial[1];
|
|
$asset->company_id = Company::getIdForCurrentUser($request->input('company_id'));
|
|
$asset->model_id = $request->input('model_id');
|
|
$asset->order_number = $request->input('order_number');
|
|
$asset->asset_tag = $asset_tag[1];
|
|
$asset->notes = $request->input('notes');
|
|
$asset->physical = '1';
|
|
|
|
$asset = $request->handleImages($asset);
|
|
|
|
// Update custom fields in the database.
|
|
// Validation for these fields is handlded through the AssetRequest form request
|
|
// FIXME: No idea why this is returning a Builder error on db_column_name.
|
|
// Need to investigate and fix. Using static method for now.
|
|
$model = AssetModel::find($request->get('model_id'));
|
|
if (($model) && ($model->fieldset)) {
|
|
foreach ($model->fieldset->fields as $field) {
|
|
if ($field->field_encrypted=='1') {
|
|
if (Gate::allows('admin')) {
|
|
$asset->{$field->convertUnicodeDbSlug()} = \Crypt::encrypt(e($request->input($field->convertUnicodeDbSlug())));
|
|
}
|
|
} else {
|
|
$asset->{$field->convertUnicodeDbSlug()} = $request->input($field->convertUnicodeDbSlug());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ($asset->save()) {
|
|
|
|
// Update any assigned assets with the new location_id from the parent asset
|
|
|
|
Asset::where('assigned_type', '\\App\\Models\\Asset')->where('assigned_to', $asset->id)
|
|
->update(['location_id' => $asset->location_id]);
|
|
|
|
// Redirect to the new asset page
|
|
\Session::flash('success', trans('admin/hardware/message.update.success'));
|
|
return response()->json(['redirect_url' => route("hardware.show", $assetId)]);
|
|
}
|
|
\Input::flash();
|
|
\Session::flash('errors', $asset->getErrors());
|
|
return redirect()->route("hardware.show", $assetId)
|
|
->with('success', trans('admin/hardware/message.update.success'));
|
|
}
|
|
|
|
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
|
}
|
|
|
|
/**
|
|
* Delete a given asset (mark as deleted).
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return Redirect
|
|
*/
|
|
public function destroy($assetId)
|
|
{
|
|
// Check if the asset exists
|
|
if (is_null($asset = Asset::find($assetId))) {
|
|
// Redirect to the asset management page with error
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
|
|
$this->authorize('delete', $asset);
|
|
|
|
DB::table('assets')
|
|
->where('id', $asset->id)
|
|
->update(array('assigned_to' => null));
|
|
|
|
if ($asset->image) {
|
|
try {
|
|
Storage::disk('public')->delete('assets'.'/'.$asset->image);
|
|
} catch (\Exception $e) {
|
|
\Log::debug($e);
|
|
}
|
|
}
|
|
|
|
$asset->delete();
|
|
|
|
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.delete.success'));
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Searches the assets table by tag, and redirects if it finds one.
|
|
*
|
|
* This is used by the top search box in Snipe-IT, but as of 4.9.x
|
|
* can also be used as a url segment.
|
|
*
|
|
* https://yoursnipe.com/hardware/bytag/?assetTag=foo
|
|
*
|
|
* OR
|
|
*
|
|
* https://yoursnipe.com/hardware/bytag/foo
|
|
*
|
|
* The latter is useful if you're doing home-grown barcodes, or
|
|
* some other automation where you don't always know the internal ID of
|
|
* an asset and don't want to query for it.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param string $tag
|
|
* @since [v3.0]
|
|
* @return Redirect
|
|
*/
|
|
public function getAssetByTag(Request $request, $tag = null)
|
|
{
|
|
|
|
$topsearch = ($request->get('topsearch')=="true");
|
|
|
|
// We need this part to determine whether a url query parameter has been passed, OR
|
|
// whether it's the url fragment we need to look at
|
|
$tag = ($request->get('assetTag')) ? $request->get('assetTag') : $tag;
|
|
|
|
if (!$asset = Asset::where('asset_tag', '=', $tag)->first()) {
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
$this->authorize('view', $asset);
|
|
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
|
}
|
|
|
|
|
|
/**
|
|
* Searches the assets table by serial, and redirects if it finds one
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param string $serial
|
|
* @since [v4.9.1]
|
|
* @return Redirect
|
|
*/
|
|
public function getAssetBySerial(Request $request, $serial = null)
|
|
{
|
|
|
|
$serial = ($request->get('serial')) ? $request->get('serial') : $serial;
|
|
if (!$asset = Asset::where('serial', '=', $serial)->first()) {
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
$this->authorize('view', $asset);
|
|
return redirect()->route('hardware.show', $asset->id);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a QR code for the asset
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return Response
|
|
*/
|
|
public function getQrCode($assetId = null)
|
|
{
|
|
$settings = Setting::getSettings();
|
|
|
|
if ($settings->qr_code == '1') {
|
|
$asset = Asset::withTrashed()->find($assetId);
|
|
if ($asset) {
|
|
$size = Helper::barcodeDimensions($settings->barcode_type);
|
|
$qr_file = public_path().'/uploads/barcodes/qr-'.str_slug($asset->asset_tag).'-'.str_slug($asset->id).'.png';
|
|
|
|
if (isset($asset->id, $asset->asset_tag)) {
|
|
if (file_exists($qr_file)) {
|
|
$header = ['Content-type' => 'image/png'];
|
|
return response()->file($qr_file, $header);
|
|
} else {
|
|
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
|
$barcode_obj = $barcode->getBarcodeObj($settings->barcode_type, route('hardware.show', $asset->id), $size['height'], $size['width'], 'black', array(-2, -2, -2, -2));
|
|
file_put_contents($qr_file, $barcode_obj->getPngData());
|
|
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
|
|
}
|
|
}
|
|
}
|
|
return 'That asset is invalid';
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a 2D barcode for the asset
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return Response
|
|
*/
|
|
public function getBarCode($assetId = null)
|
|
{
|
|
$settings = Setting::getSettings();
|
|
$asset = Asset::find($assetId);
|
|
$barcode_file = public_path().'/uploads/barcodes/'.str_slug($settings->alt_barcode).'-'.str_slug($asset->asset_tag).'.png';
|
|
|
|
if (isset($asset->id, $asset->asset_tag)) {
|
|
|
|
if (file_exists($barcode_file)) {
|
|
$header = ['Content-type' => 'image/png'];
|
|
return response()->file($barcode_file, $header);
|
|
} else {
|
|
// Calculate barcode width in pixel based on label width (inch)
|
|
$barcode_width = ($settings->labels_width - $settings->labels_display_sgutter) * 96.000000000001;
|
|
|
|
$barcode = new \Com\Tecnick\Barcode\Barcode();
|
|
|
|
try {
|
|
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
|
|
}
|
|
}
|
|
}
|
|
|
|
$barcode_obj = $barcode->getBarcodeObj($settings->alt_barcode,$asset->asset_tag,($barcode_width < 300 ? $barcode_width : 300),50);
|
|
|
|
/**
|
|
* Return a label for an individual asset.
|
|
*
|
|
* @author [L. Swartzendruber] [<logan.swartzendruber@gmail.com>
|
|
* @param int $assetId
|
|
* @return View
|
|
*/
|
|
file_put_contents($barcode_file, $barcode_obj->getPngData());
|
|
{
|
|
if (isset($assetId)) {
|
|
$asset = Asset::find($assetId);
|
|
return response($barcode_obj->getPngData())->header('Content-type', 'image/png');
|
|
|
|
|
|
|
|
->with('settings', Setting::getSettings())
|
|
\Log::debug('This usually happens because the asset tags are of a format that is not compatible with the selected barcode type.');
|
|
$img = file_get_contents(public_path().'/uploads/barcodes/invalid_barcode.gif');
|
|
return response($img)->header('Content-type', 'image/gif');
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a view that presents a form to clone an asset.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return View
|
|
*/
|
|
public function getClone($assetId = null)
|
|
{
|
|
// Check if the asset exists
|
|
if (is_null($asset_to_clone = Asset::find($assetId))) {
|
|
// Redirect to the asset management page
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
|
|
$this->authorize('create', $asset_to_clone);
|
|
|
|
$asset = clone $asset_to_clone;
|
|
$asset->id = null;
|
|
$asset->asset_tag = '';
|
|
$asset->serial = '';
|
|
$asset->assigned_to = '';
|
|
|
|
return view('hardware/edit')
|
|
->with('statuslabel_list', Helper::statusLabelList())
|
|
->with('statuslabel_types', Helper::statusTypeList())
|
|
->with('item', $asset);
|
|
}
|
|
|
|
/**
|
|
* Return history import view
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @since [v1.0]
|
|
* @return View
|
|
*/
|
|
public function getImportHistory()
|
|
{
|
|
$this->authorize('admin');
|
|
return view('hardware/history');
|
|
}
|
|
|
|
/**
|
|
* Import history
|
|
*
|
|
* This needs a LOT of love. It's done very inelegantly right now, and there are
|
|
* a ton of optimizations that could (and should) be done.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @since [v3.3]
|
|
* @return View
|
|
*/
|
|
public function postImportHistory(Request $request)
|
|
{
|
|
|
|
if (!$request->hasFile('user_import_csv')) {
|
|
return back()->with('error', 'No file provided. Please select a file for import and try again. ');
|
|
}
|
|
|
|
if (!ini_get("auto_detect_line_endings")) {
|
|
ini_set("auto_detect_line_endings", '1');
|
|
}
|
|
$csv = Reader::createFromPath(Input::file('user_import_csv'));
|
|
$csv->setHeaderOffset(0);
|
|
$results = $csv->getRecords();
|
|
$item = array();
|
|
$status = array();
|
|
$status['error'] = array();
|
|
$status['success'] = array();
|
|
foreach ($results as $row) {
|
|
if (is_array($row)) {
|
|
$row = array_change_key_case($row, CASE_LOWER);
|
|
$asset_tag = Helper::array_smart_fetch($row, "asset tag");
|
|
if (!array_key_exists($asset_tag, $item)) {
|
|
$item[$asset_tag] = array();
|
|
}
|
|
$batch_counter = count($item[$asset_tag]);
|
|
$item[$asset_tag][$batch_counter]['checkout_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkout date"))->format('Y-m-d H:i:s');
|
|
$item[$asset_tag][$batch_counter]['checkin_date'] = Carbon::parse(Helper::array_smart_fetch($row, "checkin date"))->format('Y-m-d H:i:s');
|
|
\Log::debug($item[$asset_tag][$batch_counter]['checkin_date']);
|
|
$item[$asset_tag][$batch_counter]['asset_tag'] = Helper::array_smart_fetch($row, "asset tag");
|
|
$item[$asset_tag][$batch_counter]['name'] = Helper::array_smart_fetch($row, "name");
|
|
$item[$asset_tag][$batch_counter]['email'] = Helper::array_smart_fetch($row, "email");
|
|
if ($asset = Asset::where('asset_tag', '=', $asset_tag)->first()) {
|
|
$item[$asset_tag][$batch_counter]['asset_id'] = $asset->id;
|
|
$base_username = User::generateFormattedNameFromFullName(Setting::getSettings()->username_format, $item[$asset_tag][$batch_counter]['name']);
|
|
$user = User::where('username', '=', $base_username['username']);
|
|
$user_query = ' on username '.$base_username['username'];
|
|
if ($request->input('match_firstnamelastname')=='1') {
|
|
$firstnamedotlastname = User::generateFormattedNameFromFullName('firstname.lastname', $item[$asset_tag][$batch_counter]['name']);
|
|
$item[$asset_tag][$batch_counter]['username'][] = $firstnamedotlastname['username'];
|
|
$user->orWhere('username', '=', $firstnamedotlastname['username']);
|
|
$user_query .= ', or on username '.$firstnamedotlastname['username'];
|
|
}
|
|
if ($request->input('match_flastname')=='1') {
|
|
$flastname = User::generateFormattedNameFromFullName('filastname', $item[$asset_tag][$batch_counter]['name']);
|
|
$item[$asset_tag][$batch_counter]['username'][] = $flastname['username'];
|
|
$user->orWhere('username', '=', $flastname['username']);
|
|
$user_query .= ', or on username '.$flastname['username'];
|
|
}
|
|
if ($request->input('match_firstname')=='1') {
|
|
$firstname = User::generateFormattedNameFromFullName('firstname', $item[$asset_tag][$batch_counter]['name']);
|
|
$item[$asset_tag][$batch_counter]['username'][] = $firstname['username'];
|
|
$user->orWhere('username', '=', $firstname['username']);
|
|
$user_query .= ', or on username '.$firstname['username'];
|
|
}
|
|
if ($request->input('match_email')=='1') {
|
|
if ($item[$asset_tag][$batch_counter]['email']=='') {
|
|
$item[$asset_tag][$batch_counter]['username'][] = $user_email = User::generateEmailFromFullName($item[$asset_tag][$batch_counter]['name']);
|
|
$user->orWhere('username', '=', $user_email);
|
|
$user_query .= ', or on username '.$user_email;
|
|
}
|
|
}
|
|
// A matching user was found
|
|
if ($user = $user->first()) {
|
|
$item[$asset_tag][$batch_counter]['checkedout_to'] = $user->id;
|
|
$item[$asset_tag][$batch_counter]['user_id'] = $user->id;
|
|
Actionlog::firstOrCreate(array(
|
|
'item_id' => $asset->id,
|
|
'item_type' => Asset::class,
|
|
'user_id' => Auth::user()->id,
|
|
'note' => 'Checkout imported by '.Auth::user()->present()->fullName().' from history importer',
|
|
'target_id' => $item[$asset_tag][$batch_counter]['user_id'],
|
|
'target_type' => User::class,
|
|
'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'],
|
|
'action_type' => 'checkout',
|
|
));
|
|
$asset->assigned_to = $user->id;
|
|
if ($asset->save()) {
|
|
$status['success'][]['asset'][$asset_tag]['msg'] = 'Asset successfully matched for '.Helper::array_smart_fetch($row, "name").$user_query.' on '.$item[$asset_tag][$batch_counter]['checkout_date'];
|
|
} else {
|
|
$status['error'][]['asset'][$asset_tag]['msg'] = 'Asset and user was matched but could not be saved.';
|
|
}
|
|
} else {
|
|
$item[$asset_tag][$batch_counter]['checkedout_to'] = null;
|
|
$status['error'][]['user'][Helper::array_smart_fetch($row, "name")]['msg'] = 'User does not exist so no checkin log was created.';
|
|
}
|
|
} else {
|
|
$item[$asset_tag][$batch_counter]['asset_id'] = null;
|
|
$status['error'][]['asset'][$asset_tag]['msg'] = 'Asset does not exist so no match was attempted.';
|
|
}
|
|
}
|
|
}
|
|
// Loop through and backfill the checkins
|
|
foreach ($item as $key => $asset_batch) {
|
|
$total_in_batch = count($asset_batch);
|
|
for ($x = 0; $x < $total_in_batch; $x++) {
|
|
$next = $x + 1;
|
|
// Only do this if a matching user was found
|
|
if ((array_key_exists('checkedout_to', $asset_batch[$x])) && ($asset_batch[$x]['checkedout_to']!='')) {
|
|
if (($total_in_batch > 1) && ($x < $total_in_batch) && (array_key_exists($next, $asset_batch))) {
|
|
$checkin_date = Carbon::parse($asset_batch[$next]['checkin_date'])->format('Y-m-d H:i:s');
|
|
$asset_batch[$x]['real_checkin'] = $checkin_date;
|
|
\Log::debug($asset_batch[$next]['checkin_date']);
|
|
\Log::debug($checkin_date);
|
|
Actionlog::firstOrCreate(array(
|
|
'item_id' => $asset_batch[$x]['asset_id'],
|
|
'item_type' => Asset::class,
|
|
'user_id' => Auth::user()->id,
|
|
'note' => 'Checkin imported by ' . Auth::user()->present()->fullName() . ' from history importer',
|
|
'target_id' => null,
|
|
'created_at' => $checkin_date,
|
|
'action_type' => 'checkin'
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return view('hardware/history')->with('status', $status);
|
|
}
|
|
|
|
public function sortByName(array $recordA, array $recordB): int
|
|
{
|
|
return strcmp($recordB['Full Name'], $recordA['Full Name']);
|
|
}
|
|
|
|
/**
|
|
* Retore a deleted asset.
|
|
*
|
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
|
* @param int $assetId
|
|
* @since [v1.0]
|
|
* @return View
|
|
*/
|
|
public function getRestore($assetId = null)
|
|
{
|
|
// Get asset information
|
|
$asset = Asset::withTrashed()->find($assetId);
|
|
$this->authorize('delete', $asset);
|
|
if (isset($asset->id)) {
|
|
// Restore the asset
|
|
Asset::withTrashed()->where('id', $assetId)->restore();
|
|
|
|
$logaction = new Actionlog();
|
|
$logaction->item_type = Asset::class;
|
|
$logaction->item_id = $asset->id;
|
|
$logaction->created_at = date("Y-m-d H:i:s");
|
|
$logaction->user_id = Auth::user()->id;
|
|
$logaction->logaction('restored');
|
|
|
|
return redirect()->route('hardware.index')->with('success', trans('admin/hardware/message.restore.success'));
|
|
}
|
|
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
|
}
|
|
|
|
public function quickScan()
|
|
{
|
|
$this->authorize('audit', Asset::class);
|
|
$dt = Carbon::now()->addMonths(12)->toDateString();
|
|
return view('hardware/quickscan')->with('next_audit_date', $dt);
|
|
}
|
|
|
|
|
|
|
|
public function audit($id)
|
|
{
|
|
$settings = Setting::getSettings();
|
|
$this->authorize('audit', Asset::class);
|
|
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
|
$asset = Asset::findOrFail($id);
|
|
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
|
}
|
|
|
|
public function dueForAudit()
|
|
{
|
|
$this->authorize('audit', Asset::class);
|
|
return view('hardware/audit-due');
|
|
}
|
|
|
|
public function overdueForAudit()
|
|
{
|
|
$this->authorize('audit', Asset::class);
|
|
return view('hardware/audit-overdue');
|
|
}
|
|
|
|
|
|
public function auditStore(Request $request, $id)
|
|
{
|
|
$this->authorize('audit', Asset::class);
|
|
|
|
$rules = array(
|
|
'location_id' => 'exists:locations,id|nullable|numeric',
|
|
'next_audit_date' => 'date|nullable'
|
|
);
|
|
|
|
$validator = \Validator::make($request->all(), $rules);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
|
|
}
|
|
|
|
$asset = Asset::findOrFail($id);
|
|
|
|
// We don't want to log this as a normal update, so let's bypass that
|
|
$asset->unsetEventDispatcher();
|
|
|
|
$asset->next_audit_date = $request->input('next_audit_date');
|
|
$asset->last_audit_date = date('Y-m-d h:i:s');
|
|
|
|
// 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') {
|
|
\Log::debug('update location in audit');
|
|
$asset->location_id = $request->input('location_id');
|
|
}
|
|
|
|
|
|
if ($asset->save()) {
|
|
$file_name = '';
|
|
// Upload an image, if attached
|
|
if ($request->hasFile('image')) {
|
|
$path = 'private_uploads/audits';
|
|
if (!Storage::exists($path)) Storage::makeDirectory($path, 775);
|
|
$upload = $image = $request->file('image');
|
|
$ext = $image->getClientOriginalExtension();
|
|
$file_name = 'audit-'.str_random(18).'.'.$ext;
|
|
Storage::putFileAs($path, $upload, $file_name);
|
|
}
|
|
|
|
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
|
|
return redirect()->to("hardware")->with('success', trans('admin/hardware/message.audit.success'));
|
|
}
|
|
}
|
|
|
|
public function getRequestedIndex($user_id = null)
|
|
{
|
|
$requestedItems = CheckoutRequest::with('user', 'requestedItem')->whereNull('canceled_at')->with('user', 'requestedItem');
|
|
|
|
if ($user_id) {
|
|
$requestedItems->where('user_id', $user_id)->get();
|
|
}
|
|
|
|
$requestedItems = $requestedItems->orderBy('created_at', 'desc')->get();
|
|
|
|
return view('hardware/requested', compact('requestedItems'));
|
|
}
|
|
|
|
}
|