Merge branch 'develop' into min_qty_in_bulk_model_edit
This commit is contained in:
@@ -3307,6 +3307,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ntaylor-86",
|
||||
"name": "Nathan Taylor",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28693782?v=4",
|
||||
"profile": "https://github.com/ntaylor-86",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ DB_USERNAME=snipeit
|
||||
DB_PASSWORD=changeme1234
|
||||
DB_PREFIX=null
|
||||
DB_DUMP_PATH='/usr/bin'
|
||||
DB_DUMP_SKIP_SSL=true
|
||||
DB_CHARSET=utf8mb4
|
||||
DB_COLLATION=utf8mb4_unicode_ci
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ DB_PASSWORD=changeme1234
|
||||
MYSQL_ROOT_PASSWORD=changeme1234
|
||||
DB_PREFIX=null
|
||||
DB_DUMP_PATH='/usr/bin'
|
||||
DB_DUMP_SKIP_SSL=true
|
||||
DB_CHARSET=utf8mb4
|
||||
DB_COLLATION=utf8mb4_unicode_ci
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ DB_USERNAME=null
|
||||
DB_PASSWORD=null
|
||||
DB_PREFIX=null
|
||||
DB_DUMP_PATH='/usr/bin'
|
||||
DB_DUMP_SKIP_SSL=false
|
||||
DB_CHARSET=utf8mb4
|
||||
DB_COLLATION=utf8mb4_unicode_ci
|
||||
DB_SANITIZE_BY_DEFAULT=false
|
||||
|
||||
@@ -54,6 +54,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/167117705?v=4" width="110px;"/><br /><sub>Glukose1</sub>](https://github.com/Glukose1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Glukose1 "Code") | [<img src="https://avatars.githubusercontent.com/u/1197791?v=4" width="110px;"/><br /><sub>Scarzy</sub>](https://github.com/Scarzy)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Scarzy "Code") | [<img src="https://avatars.githubusercontent.com/u/37372069?v=4" width="110px;"/><br /><sub>setpill</sub>](https://github.com/setpill)<br />[💻](https://github.com/snipe/snipe-it/commits?author=setpill "Code") | [<img src="https://avatars.githubusercontent.com/u/3755203?v=4" width="110px;"/><br /><sub>swift2512</sub>](https://github.com/swift2512)<br />[🐛](https://github.com/snipe/snipe-it/issues?q=author%3Aswift2512 "Bug reports") | [<img src="https://avatars.githubusercontent.com/u/6136439?v=4" width="110px;"/><br /><sub>Darren Rainey</sub>](https://darrenraineys.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DarrenRainey "Code") | [<img src="https://avatars.githubusercontent.com/u/133033121?v=4" width="110px;"/><br /><sub>maciej-poleszczyk</sub>](https://github.com/maciej-poleszczyk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=maciej-poleszczyk "Code") | [<img src="https://avatars.githubusercontent.com/u/143394709?v=4" width="110px;"/><br /><sub>Sebastian Groß</sub>](https://github.com/sgross-emlix)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sgross-emlix "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/41107778?v=4" width="110px;"/><br /><sub>Anouar Touati</sub>](https://github.com/AnouarTouati)<br />[💻](https://github.com/snipe/snipe-it/commits?author=AnouarTouati "Code") | [<img src="https://avatars.githubusercontent.com/u/25596663?v=4" width="110px;"/><br /><sub>aHVzY2g</sub>](https://github.com/aHVzY2g)<br />[💻](https://github.com/snipe/snipe-it/commits?author=aHVzY2g "Code") | [<img src="https://avatars.githubusercontent.com/u/13408130?v=4" width="110px;"/><br /><sub>林博仁 Buo-ren Lin</sub>](https://brlin.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=brlin-tw "Code") | [<img src="https://avatars.githubusercontent.com/u/18550946?v=4" width="110px;"/><br /><sub>Adugna Gizaw</sub>](https://orbalia.pythonanywhere.com/)<br />[🌍](#translation-addex12 "Translation") | [<img src="https://avatars.githubusercontent.com/u/760989?v=4" width="110px;"/><br /><sub>Jesse Ostrander</sub>](https://github.com/jostrander)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jostrander "Code") | [<img src="https://avatars.githubusercontent.com/u/31522486?v=4" width="110px;"/><br /><sub>James M</sub>](https://github.com/azmcnutt)<br />[💻](https://github.com/snipe/snipe-it/commits?author=azmcnutt "Code") | [<img src="https://avatars.githubusercontent.com/u/5183146?v=4" width="110px;"/><br /><sub>Fiala06</sub>](https://github.com/Fiala06)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Fiala06 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/28693782?v=4" width="110px;"/><br /><sub>Nathan Taylor</sub>](https://github.com/ntaylor-86)<br />[💻](https://github.com/snipe/snipe-it/commits?author=ntaylor-86 "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
48
app/Actions/CheckoutRequests/CancelCheckoutRequestAction.php
Normal file
48
app/Actions/CheckoutRequests/CancelCheckoutRequestAction.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\CheckoutRequests;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\RequestAssetCancelation;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
|
||||
class CancelCheckoutRequestAction
|
||||
{
|
||||
public static function run(Asset $asset, User $user)
|
||||
{
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
$asset->cancelRequest();
|
||||
|
||||
$asset->decrement('requests_counter', 1);
|
||||
|
||||
$data['item'] = $asset;
|
||||
$data['target'] = $user;
|
||||
$data['item_quantity'] = 1;
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $asset->id;
|
||||
$logaction->item_type = $data['item_type'] = Asset::class;
|
||||
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
|
||||
$logaction->target_id = $data['user_id'] = auth()->id();
|
||||
$logaction->target_type = User::class;
|
||||
$logaction->location_id = $user->location_id ?? null;
|
||||
$logaction->logaction('request canceled');
|
||||
|
||||
try {
|
||||
$settings->notify(new RequestAssetCancelation($data));
|
||||
} catch (\Exception $e) {
|
||||
\Log::warning($e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
54
app/Actions/CheckoutRequests/CreateCheckoutRequestAction.php
Normal file
54
app/Actions/CheckoutRequests/CreateCheckoutRequestAction.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\CheckoutRequests;
|
||||
|
||||
use App\Exceptions\AssetNotRequestable;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\RequestAssetNotification;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Log;
|
||||
|
||||
class CreateCheckoutRequestAction
|
||||
{
|
||||
/**
|
||||
* @throws AssetNotRequestable
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public static function run(Asset $asset, User $user): string
|
||||
{
|
||||
if (is_null(Asset::RequestableAssets()->find($asset->id))) {
|
||||
throw new AssetNotRequestable($asset);
|
||||
}
|
||||
if (!Company::isCurrentUserHasAccess($asset)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
$data['item'] = $asset;
|
||||
$data['target'] = $user;
|
||||
$data['item_quantity'] = 1;
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $asset->id;
|
||||
$logaction->item_type = $data['item_type'] = Asset::class;
|
||||
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
|
||||
$logaction->target_id = $data['user_id'] = auth()->id();
|
||||
$logaction->target_type = User::class;
|
||||
$logaction->location_id = $user->location_id ?? null;
|
||||
$logaction->logaction('requested');
|
||||
|
||||
$asset->request();
|
||||
$asset->increment('requests_counter', 1);
|
||||
try {
|
||||
$settings->notify(new RequestAssetNotification($data));
|
||||
} catch (\Exception $e) {
|
||||
Log::warning($e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
151
app/Console/Commands/FixBulkAccessoryCheckinActionLogEntries.php
Normal file
151
app/Console/Commands/FixBulkAccessoryCheckinActionLogEntries.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class FixBulkAccessoryCheckinActionLogEntries extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:fix-bulk-accessory-action-log-entries {--dry-run : Run the sync process but don\'t update the database} {--skip-backup : Skip pre-execution backup}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This script attempts to fix timestamps and missing created_by values for bulk checkin entries in the log table';
|
||||
|
||||
private bool $dryrun = false;
|
||||
private bool $skipBackup = false;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->skipBackup = $this->option('skip-backup');
|
||||
$this->dryrun = $this->option('dry-run');
|
||||
|
||||
if ($this->dryrun) {
|
||||
$this->info('This is a DRY RUN - no changes will be saved.');
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
$logs = Actionlog::query()
|
||||
// only look for accessory checkin logs
|
||||
->where('item_type', Accessory::class)
|
||||
// that were part of a bulk checkin
|
||||
->where('note', 'Bulk checkin items')
|
||||
// logs that were improperly timestamped should have created_at in the 1970s
|
||||
->whereYear('created_at', '1970')
|
||||
->get();
|
||||
|
||||
if ($logs->isEmpty()) {
|
||||
$this->info('No logs found with incorrect timestamps.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info('Found ' . $logs->count() . ' logs with incorrect timestamps:');
|
||||
|
||||
$this->table(
|
||||
['ID', 'Created By', 'Created At', 'Updated At'],
|
||||
$logs->map(function ($log) {
|
||||
return [
|
||||
$log->id,
|
||||
$log->created_by,
|
||||
$log->created_at,
|
||||
$log->updated_at,
|
||||
];
|
||||
})
|
||||
);
|
||||
|
||||
if (!$this->dryrun && !$this->confirm('Update these logs?')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$this->dryrun && !$this->skipBackup) {
|
||||
$this->info('Backing up the database before making changes...');
|
||||
$this->call('snipeit:backup');
|
||||
}
|
||||
|
||||
if ($this->dryrun) {
|
||||
$this->newLine();
|
||||
$this->info('DRY RUN. NOT ACTUALLY UPDATING LOGS.');
|
||||
}
|
||||
|
||||
foreach ($logs as $log) {
|
||||
$this->newLine();
|
||||
$this->info('Processing log id:' . $log->id);
|
||||
|
||||
// created_by was not being set for accessory bulk checkins
|
||||
// so let's see if there was another bulk checkin log
|
||||
// with the same timestamp and a created_by value we can use.
|
||||
if (is_null($log->created_by)) {
|
||||
$createdByFromSimilarLog = $this->getCreatedByAttributeFromSimilarLog($log);
|
||||
|
||||
if ($createdByFromSimilarLog) {
|
||||
$this->line(vsprintf('Updating log id:%s created_by to %s', [$log->id, $createdByFromSimilarLog]));
|
||||
$log->created_by = $createdByFromSimilarLog;
|
||||
} else {
|
||||
$this->warn(vsprintf('No created_by found for log id:%s', [$log->id]));
|
||||
$this->warn('Skipping updating this log since no similar log was found to update created_by from.');
|
||||
|
||||
// If we can't find a similar log then let's skip updating it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->line(vsprintf('Updating log id:%s from %s to %s', [$log->id, $log->created_at, $log->updated_at]));
|
||||
$log->created_at = $log->updated_at;
|
||||
|
||||
if (!$this->dryrun) {
|
||||
Model::withoutTimestamps(function () use ($log) {
|
||||
$log->saveQuietly();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
|
||||
if ($this->dryrun) {
|
||||
$this->info('DRY RUN. NO CHANGES WERE ACTUALLY MADE.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hopefully the bulk checkin included other items like assets or licenses
|
||||
* so we can use one of those logs to get the correct created_by value.
|
||||
*
|
||||
* This method attempts to find a bulk check in log that was
|
||||
* created at the same time as the log passed in.
|
||||
*/
|
||||
private function getCreatedByAttributeFromSimilarLog(Actionlog $log): null|int
|
||||
{
|
||||
$similarLog = Actionlog::query()
|
||||
->whereNotNull('created_by')
|
||||
->where([
|
||||
'action_type' => 'checkin from',
|
||||
'note' => 'Bulk checkin items',
|
||||
'target_id' => $log->target_id,
|
||||
'target_type' => $log->target_type,
|
||||
'created_at' => $log->updated_at,
|
||||
])
|
||||
->first();
|
||||
|
||||
if ($similarLog) {
|
||||
return $similarLog->created_by;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,7 @@ class PaveIt extends Command
|
||||
}
|
||||
|
||||
// List all the tables in the database so we don't have to worry about missing some as the app grows
|
||||
$tables = DB::connection()->getDoctrineSchemaManager()->listTableNames();
|
||||
|
||||
$tables = Schema::getTables();
|
||||
$except_tables = [
|
||||
'oauth_access_tokens',
|
||||
'oauth_clients',
|
||||
@@ -74,7 +73,8 @@ class PaveIt extends Command
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tables as $table) {
|
||||
foreach ($tables as $table_obj) {
|
||||
$table = $table_obj['name'];
|
||||
if (in_array($table, $except_tables)) {
|
||||
$this->info($table. ' is SKIPPED.');
|
||||
} else {
|
||||
|
||||
@@ -49,6 +49,7 @@ class SendExpirationAlerts extends Command
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))
|
||||
->map(fn($item) => trim($item)) // Trim each email
|
||||
->filter(fn($item) => !empty($item))
|
||||
->all();
|
||||
// Expiring Assets
|
||||
$assets = Asset::getExpiringWarrantee($alert_interval);
|
||||
|
||||
@@ -55,6 +55,7 @@ class SendUpcomingAuditReport extends Command
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
$recipients = collect(explode(',', $settings->alert_email))
|
||||
->map(fn($item) => trim($item))
|
||||
->filter(fn($item) => !empty($item))
|
||||
->all();
|
||||
|
||||
|
||||
|
||||
9
app/Exceptions/AssetNotRequestable.php
Normal file
9
app/Exceptions/AssetNotRequestable.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AssetNotRequestable extends Exception
|
||||
{
|
||||
}
|
||||
10
app/Exceptions/UserDoestExistException.php
Normal file
10
app/Exceptions/UserDoestExistException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UserDoestExistException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -868,7 +868,7 @@ class Helper
|
||||
$filetype = @finfo_file($finfo, $file);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (($filetype == 'image/jpeg') || ($filetype == 'image/jpg') || ($filetype == 'image/png') || ($filetype == 'image/bmp') || ($filetype == 'image/gif') || ($filetype == 'image/avif')) {
|
||||
if (($filetype == 'image/jpeg') || ($filetype == 'image/jpg') || ($filetype == 'image/png') || ($filetype == 'image/bmp') || ($filetype == 'image/gif') || ($filetype == 'image/avif') || ($filetype == 'image/webp')) {
|
||||
return $filetype;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,13 +112,13 @@ class AccessoriesController extends Controller
|
||||
{
|
||||
|
||||
$this->authorize('create', Accessory::class);
|
||||
|
||||
$accessory = clone $accessory;
|
||||
$accessory->id = null;
|
||||
$accessory->location_id = null;
|
||||
$cloned = clone $accessory;
|
||||
$cloned->id = null;
|
||||
$cloned->deleted_at = '';
|
||||
$cloned->location_id = null;
|
||||
|
||||
return view('accessories/edit')
|
||||
->with('item', $accessory);
|
||||
->with('item', $cloned);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -208,9 +208,12 @@ class AcceptanceController extends Controller
|
||||
*/
|
||||
$branding_settings = SettingsController::getPDFBranding();
|
||||
|
||||
if (is_null($branding_settings->logo)){
|
||||
$path_logo = "";
|
||||
} else {
|
||||
$path_logo = "";
|
||||
|
||||
// Check for the PDF logo path and use that, otherwise use the regular logo path
|
||||
if (!is_null($branding_settings->acceptance_pdf_logo)) {
|
||||
$path_logo = public_path() . '/uploads/' . $branding_settings->acceptance_pdf_logo;
|
||||
} elseif (!is_null($branding_settings->logo)) {
|
||||
$path_logo = public_path() . '/uploads/' . $branding_settings->logo;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\View\Label;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
|
||||
/**
|
||||
@@ -491,15 +492,32 @@ class AssetsController extends Controller
|
||||
public function showBySerial(Request $request, $serial): JsonResponse | array
|
||||
{
|
||||
$this->authorize('index', Asset::class);
|
||||
$assets = Asset::where('serial', $serial)->with('assetstatus')->with('assignedTo');
|
||||
$assets = Asset::where('serial', $serial)->with([
|
||||
'assetstatus',
|
||||
'assignedTo',
|
||||
'company',
|
||||
'defaultLoc',
|
||||
'location',
|
||||
'model.category',
|
||||
'model.depreciation',
|
||||
'model.fieldset',
|
||||
'model.manufacturer',
|
||||
'supplier',
|
||||
]);
|
||||
|
||||
// Check if they've passed ?deleted=true
|
||||
if ($request->input('deleted', 'false') == 'true') {
|
||||
$assets = $assets->withTrashed();
|
||||
}
|
||||
|
||||
if (($assets = $assets->get()) && ($assets->count()) > 0) {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
$offset = ($request->input('offset') > $assets->count()) ? $assets->count() : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$total = $assets->count();
|
||||
$assets = $assets->skip($offset)->take($limit)->get();
|
||||
|
||||
if (($assets) && ($assets->count()) > 0) {
|
||||
return (new AssetsTransformer)->transformAssets($assets, $total);
|
||||
}
|
||||
|
||||
// If there are 0 results, return the "no such asset" response
|
||||
@@ -1047,7 +1065,7 @@ class AssetsController extends Controller
|
||||
* @param int $id
|
||||
* @since [v4.0]
|
||||
*/
|
||||
public function audit(Request $request): JsonResponse
|
||||
public function audit(Request $request, Asset $asset): JsonResponse
|
||||
|
||||
{
|
||||
$this->authorize('audit', Asset::class);
|
||||
@@ -1055,36 +1073,15 @@ class AssetsController extends Controller
|
||||
$settings = Setting::getSettings();
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
|
||||
// No tag passed - return an error
|
||||
if (!$request->filled('asset_tag')) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag' => '',
|
||||
'error' => trans('admin/hardware/message.no_tag'),
|
||||
], trans('admin/hardware/message.no_tag')), 200);
|
||||
// Allow the asset tag to be passed in the payload (legacy method)
|
||||
if ($request->filled('asset_tag')) {
|
||||
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
|
||||
}
|
||||
|
||||
|
||||
$asset = Asset::where('asset_tag', '=', $request->input('asset_tag'))->first();
|
||||
|
||||
|
||||
if ($asset) {
|
||||
|
||||
/**
|
||||
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
|
||||
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
|
||||
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
|
||||
* the audit log entry we're creating through this controller.
|
||||
*
|
||||
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
|
||||
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
|
||||
* will bypass normal model-level validation that's usually handled at the observer )
|
||||
*
|
||||
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
|
||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||
*
|
||||
* @see \App\Observers\AssetObserver::updating()
|
||||
*/
|
||||
$asset->unsetEventDispatcher();
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
$asset->next_audit_date = $dt;
|
||||
|
||||
if ($request->filled('next_audit_date')) {
|
||||
@@ -1099,33 +1096,89 @@ class AssetsController extends Controller
|
||||
|
||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||
|
||||
// Set up the payload for re-display in the API response
|
||||
$payload = [
|
||||
'id' => $asset->id,
|
||||
'asset_tag' => $asset->asset_tag,
|
||||
'note' => $request->input('note'),
|
||||
'next_audit_date' => Helper::getFormattedDateObject($asset->next_audit_date),
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 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 (($asset->model) && ($asset->model->fieldset)) {
|
||||
$payload['custom_fields'] = [];
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
if (($field->display_audit=='1') && ($request->has($field->db_column))) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
$payload['custom_fields'][$field->db_column] = $request->input($field->db_column);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Validate custom fields
|
||||
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
|
||||
|
||||
// Validate the rest of the data before we turn off the event dispatcher
|
||||
if ($asset->isInvalid()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $asset->getErrors()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Even though we do a save() further down, we don't want to log this as a "normal" asset update,
|
||||
* which would trigger the Asset Observer and would log an asset *update* log entry (because the
|
||||
* de-normed fields like next_audit_date on the asset itself will change on save()) *in addition* to
|
||||
* the audit log entry we're creating through this controller.
|
||||
*
|
||||
* To prevent this double-logging (one for update and one for audit), we skip the observer and bypass
|
||||
* that de-normed update log entry by using unsetEventDispatcher(), BUT invoking unsetEventDispatcher()
|
||||
* will bypass normal model-level validation that's usually handled at the observer)
|
||||
*
|
||||
* We handle validation on the save() by checking if the asset is valid via the ->isValid() method,
|
||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||
*
|
||||
* @see \App\Observers\AssetObserver::updating()
|
||||
* @see \App\Models\Asset::save()
|
||||
*/
|
||||
|
||||
$asset->unsetEventDispatcher();
|
||||
|
||||
|
||||
/**
|
||||
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
||||
* We have to invoke this manually because of the unsetEventDispatcher() above.)
|
||||
*/
|
||||
if ($asset->isValid() && $asset->save()) {
|
||||
$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')));
|
||||
$asset->logAudit(request('note'), request('location_id'), null, $originalValues);
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('admin/hardware/message.audit.success')));
|
||||
}
|
||||
|
||||
// Asset failed validation or was not able to be saved
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag' => e($asset->asset_tag),
|
||||
'error' => $asset->getErrors()->first(),
|
||||
], trans('admin/hardware/message.audit.error', ['error' => $asset->getErrors()->first()])), 200);
|
||||
}
|
||||
|
||||
|
||||
// No matching asset for the asset tag that was passed.
|
||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||
'asset_tag' => e($request->input('asset_tag')),
|
||||
'error' => trans('admin/hardware/message.audit.error'),
|
||||
], trans('admin/hardware/message.audit.error', ['error' => trans('admin/hardware/message.does_not_exist')])), 200);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/hardware/message.does_not_exist')), 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
44
app/Http/Controllers/Api/CheckoutRequest.php
Normal file
44
app/Http/Controllers/Api/CheckoutRequest.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
|
||||
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
|
||||
use App\Exceptions\AssetNotRequestable;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Exception;
|
||||
|
||||
class CheckoutRequest extends Controller
|
||||
{
|
||||
public function store(Asset $asset): JsonResponse
|
||||
{
|
||||
try {
|
||||
CreateCheckoutRequestAction::run($asset, auth()->user());
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.success')));
|
||||
} catch (AssetNotRequestable $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', 'Asset is not requestable'));
|
||||
} catch (AuthorizationException $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
|
||||
} catch (Exception $e) {
|
||||
report($e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(Asset $asset): JsonResponse
|
||||
{
|
||||
try {
|
||||
CancelCheckoutRequestAction::run($asset, auth()->user());
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/hardware/message.requests.canceled')));
|
||||
} catch (AuthorizationException $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.insufficient_permissions')));
|
||||
} catch (Exception $e) {
|
||||
report($e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,13 +136,13 @@ class LicenseSeatsController extends Controller
|
||||
if ($licenseSeat->save()) {
|
||||
|
||||
if ($is_checkin) {
|
||||
$licenseSeat->logCheckin($target, $request->input('note'));
|
||||
$licenseSeat->logCheckin($target, $request->input('notes'));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||
}
|
||||
|
||||
// in this case, relevant fields are touched but it's not a checkin operation. so it must be a checkout operation.
|
||||
$licenseSeat->logCheckout($request->input('note'), $target);
|
||||
$licenseSeat->logCheckout($request->input('notes'), $target);
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class AssetMaintenancesController extends Controller
|
||||
) {
|
||||
$startDate = Carbon::parse($assetMaintenance->start_date);
|
||||
$completionDate = Carbon::parse($assetMaintenance->completion_date);
|
||||
$assetMaintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
|
||||
$assetMaintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
@@ -210,7 +210,7 @@ class AssetMaintenancesController extends Controller
|
||||
) {
|
||||
$startDate = Carbon::parse($maintenance->start_date);
|
||||
$completionDate = Carbon::parse($maintenance->completion_date);
|
||||
$maintenance->asset_maintenance_time = $completionDate->diffInDays($startDate);
|
||||
$maintenance->asset_maintenance_time = (int) $completionDate->diffInDays($startDate, true);
|
||||
}
|
||||
|
||||
// Was the asset maintenance created?
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Http\Requests\UpdateAssetRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -318,7 +319,7 @@ class AssetsController extends Controller
|
||||
$asset->eol_explicit = false;
|
||||
} elseif ($request->filled('asset_eol_date')) {
|
||||
$asset->asset_eol_date = $request->input('asset_eol_date', null);
|
||||
$months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
|
||||
$months = (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true);
|
||||
if($asset->model->eol) {
|
||||
if($months != $asset->model->eol > 0) {
|
||||
$asset->eol_explicit = true;
|
||||
@@ -390,26 +391,26 @@ class AssetsController extends Controller
|
||||
$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('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
if ($request->has($field->db_column)) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -865,13 +866,6 @@ class AssetsController extends Controller
|
||||
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
|
||||
}
|
||||
|
||||
public function audit(Asset $asset)
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$this->authorize('audit', Asset::class);
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
return view('hardware/audit')->with('asset', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||
}
|
||||
|
||||
public function dueForAudit()
|
||||
{
|
||||
@@ -888,19 +882,59 @@ class AssetsController extends Controller
|
||||
}
|
||||
|
||||
|
||||
public function audit(Asset $asset)
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$this->authorize('audit', Asset::class);
|
||||
$dt = Carbon::now()->addMonths($settings->audit_interval)->toDateString();
|
||||
return view('hardware/audit')->with('asset', $asset)->with('item', $asset)->with('next_audit_date', $dt)->with('locations_list');
|
||||
}
|
||||
|
||||
public function auditStore(UploadFileRequest $request, Asset $asset)
|
||||
{
|
||||
|
||||
$this->authorize('audit', Asset::class);
|
||||
|
||||
$rules = [
|
||||
'location_id' => 'exists:locations,id|nullable|numeric',
|
||||
'next_audit_date' => 'date|nullable',
|
||||
];
|
||||
$originalValues = $asset->getRawOriginal();
|
||||
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
$asset->next_audit_date = $request->input('next_audit_date');
|
||||
$asset->last_audit_date = date('Y-m-d H:i:s');
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $validator->errors()->all()));
|
||||
// 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');
|
||||
}
|
||||
|
||||
// Update custom fields in the database
|
||||
if (($asset->model) && ($asset->model->fieldset)) {
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
if (($field->display_audit=='1') && ($request->has($field->db_column))) {
|
||||
if ($field->field_encrypted == '1') {
|
||||
if (Gate::allows('assets.view.encrypted_custom_fields')) {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column)));
|
||||
} else {
|
||||
$asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_array($request->input($field->db_column))) {
|
||||
$asset->{$field->db_column} = implode(', ', $request->input($field->db_column));
|
||||
} else {
|
||||
$asset->{$field->db_column} = $request->input($field->db_column);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate custom fields
|
||||
Validator::make($asset->toArray(), $asset->customFieldValidationRules())->validate();
|
||||
|
||||
// Validate the rest of the data before we turn off the event dispatcher
|
||||
if ($asset->isInvalid()) {
|
||||
return redirect()->back()->withInput()->withErrors($asset->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -917,18 +951,11 @@ class AssetsController extends Controller
|
||||
* which manually invokes Watson Validating to make sure the asset's model is valid.
|
||||
*
|
||||
* @see \App\Observers\AssetObserver::updating()
|
||||
* @see \App\Models\Asset::save()
|
||||
*/
|
||||
|
||||
$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') {
|
||||
$asset->location_id = $request->input('location_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoke Watson Validating to check the asset itself and check to make sure it saved correctly.
|
||||
@@ -942,7 +969,7 @@ class AssetsController extends Controller
|
||||
$file_name = $request->handleFile('private_uploads/audits/', 'audit-'.$asset->id, $request->file('image'));
|
||||
}
|
||||
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name);
|
||||
$asset->logAudit($request->input('note'), $request->input('location_id'), $file_name, $originalValues);
|
||||
return redirect()->route('assets.audit.due')->with('success', trans('admin/hardware/message.audit.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -570,7 +570,6 @@ class BulkAssetsController extends Controller
|
||||
*/
|
||||
public function storeCheckout(AssetCheckoutRequest $request) : RedirectResponse | ModelNotFoundException
|
||||
{
|
||||
|
||||
$this->authorize('checkout', Asset::class);
|
||||
|
||||
try {
|
||||
@@ -584,6 +583,8 @@ class BulkAssetsController extends Controller
|
||||
|
||||
$asset_ids = array_filter($request->get('selected_assets'));
|
||||
|
||||
$assets = Asset::findOrFail($asset_ids);
|
||||
|
||||
if (request('checkout_to_type') == 'asset') {
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
if ($target->id == $asset_id) {
|
||||
@@ -603,9 +604,8 @@ class BulkAssetsController extends Controller
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $asset_ids, $request) { //NOTE: $errors is passsed by reference!
|
||||
foreach ($asset_ids as $asset_id) {
|
||||
$asset = Asset::findOrFail($asset_id);
|
||||
DB::transaction(function () use ($target, $admin, $checkout_at, $expected_checkin, &$errors, $assets, $request) { //NOTE: $errors is passsed by reference!
|
||||
foreach ($assets as $asset) {
|
||||
$this->authorize('checkout', $asset);
|
||||
|
||||
$checkout_success = $asset->checkOut($target, $admin, $checkout_at, $expected_checkin, e($request->get('note')), $asset->name, null);
|
||||
@@ -632,7 +632,7 @@ class BulkAssetsController extends Controller
|
||||
// Redirect to the asset management page with error
|
||||
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $asset_ids))->withErrors($errors);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return redirect()->route('hardware.bulkcheckout.show')->with('error', $e->getErrors());
|
||||
return redirect()->route('hardware.bulkcheckout.show')->withInput()->with('error', trans_choice('admin/hardware/message.multi-checkout.error', $request->input('selected_assets')));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -284,8 +284,11 @@ class LoginController extends Controller
|
||||
return redirect()->back()->withInput()->withErrors($validator);
|
||||
}
|
||||
|
||||
$this->maxLoginAttempts = config('auth.passwords.users.throttle.max_attempts');
|
||||
$this->lockoutTime = config('auth.passwords.users.throttle.lockout_duration');
|
||||
// Set the custom lockout attempts from the env and sett the custom lockout throttle from the env.
|
||||
// We divide decayMinutes by 60 here to get minutes, since Laravel changed the default from minutes
|
||||
// to seconds, and we don't want to break limits on existing systems
|
||||
$this->maxAttempts = config('auth.passwords.users.throttle.max_attempts');
|
||||
$this->decayMinutes = (config('auth.passwords.users.throttle.lockout_duration') / 60);
|
||||
|
||||
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
|
||||
$this->fireLockoutEvent($request);
|
||||
@@ -355,7 +358,7 @@ class LoginController extends Controller
|
||||
|
||||
// We wouldn't normally see this page if 2FA isn't enforced via the
|
||||
// \App\Http\Middleware\CheckForTwoFactor middleware AND if a device isn't enrolled,
|
||||
// but let's check check anyway in case there's a browser history or back button thing.
|
||||
// but let's check anyway in case there's a browser history or back button thing.
|
||||
// While you can access this page directly, enrolling a device when 2FA isn't enforced
|
||||
// won't cause any harm.
|
||||
|
||||
@@ -521,45 +524,6 @@ class LoginController extends Controller
|
||||
return 'username';
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the user after determining they are locked out.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendLockoutResponse(Request $request)
|
||||
{
|
||||
$seconds = $this->limiter()->availableIn(
|
||||
$this->throttleKey($request)
|
||||
);
|
||||
|
||||
$minutes = round($seconds / 60);
|
||||
|
||||
$message = trans('auth/message.throttle', ['minutes' => $minutes]);
|
||||
|
||||
return redirect()->back()
|
||||
->withInput($request->only($this->username(), 'remember'))
|
||||
->withErrors([$this->username() => $message]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override the lockout time and duration
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasTooManyLoginAttempts(Request $request)
|
||||
{
|
||||
$lockoutTime = config('auth.passwords.users.throttle.lockout_duration');
|
||||
$maxLoginAttempts = config('auth.passwords.users.throttle.max_attempts');
|
||||
|
||||
return $this->limiter()->tooManyAttempts(
|
||||
$this->throttleKey($request),
|
||||
$maxLoginAttempts,
|
||||
$lockoutTime
|
||||
);
|
||||
}
|
||||
|
||||
public function legacyAuthRedirect()
|
||||
{
|
||||
|
||||
@@ -106,6 +106,7 @@ class CustomFieldsController extends Controller
|
||||
"show_in_requestable_list" => $request->get("show_in_requestable_list", 0),
|
||||
"display_checkin" => $request->get("display_checkin", 0),
|
||||
"display_checkout" => $request->get("display_checkout", 0),
|
||||
"display_audit" => $request->get("display_audit", 0),
|
||||
"created_by" => auth()->id()
|
||||
]);
|
||||
|
||||
@@ -250,6 +251,7 @@ class CustomFieldsController extends Controller
|
||||
$field->show_in_requestable_list = $request->get("show_in_requestable_list", 0);
|
||||
$field->display_checkin = $request->get("display_checkin", 0);
|
||||
$field->display_checkout = $request->get("display_checkout", 0);
|
||||
$field->display_audit = $request->get("display_audit", 0);
|
||||
|
||||
if ($request->get('format') == 'CUSTOM REGEX') {
|
||||
$field->format = e($request->get('custom_format'));
|
||||
|
||||
@@ -83,6 +83,10 @@ class GroupsController extends Controller
|
||||
{
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = $group->decodePermissions();
|
||||
|
||||
if ((!is_array($groupPermissions)) || (!$groupPermissions)) {
|
||||
$groupPermissions = [];
|
||||
}
|
||||
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class LabelsController extends Controller
|
||||
$exampleAsset->order_number = '12345';
|
||||
$exampleAsset->purchase_date = '2023-01-01';
|
||||
$exampleAsset->status_id = 1;
|
||||
$exampleAsset->location_id = 1;
|
||||
|
||||
$exampleAsset->company = new Company([
|
||||
'name' => trans('admin/labels/table.example_company'),
|
||||
|
||||
@@ -243,7 +243,7 @@ class ReportsController extends Controller
|
||||
|
||||
$header = [
|
||||
trans('general.date'),
|
||||
trans('general.admin'),
|
||||
trans('general.created_by'),
|
||||
trans('general.action'),
|
||||
trans('general.type'),
|
||||
trans('general.item'),
|
||||
@@ -493,6 +493,17 @@ class ReportsController extends Controller
|
||||
$header[] = trans('admin/hardware/table.eol');
|
||||
}
|
||||
|
||||
if ($request->filled('warranty')) {
|
||||
$header[] = trans('admin/hardware/form.warranty');
|
||||
$header[] = trans('admin/hardware/form.warranty_expires');
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation')) {
|
||||
$header[] = trans('admin/hardware/table.book_value');
|
||||
$header[] = trans('admin/hardware/table.diff');
|
||||
$header[] = trans('admin/hardware/form.fully_depreciated');
|
||||
}
|
||||
|
||||
if ($request->filled('order')) {
|
||||
$header[] = trans('admin/hardware/form.order');
|
||||
}
|
||||
@@ -579,17 +590,6 @@ class ReportsController extends Controller
|
||||
$header[] = trans('general.status');
|
||||
}
|
||||
|
||||
if ($request->filled('warranty')) {
|
||||
$header[] = trans('admin/hardware/form.warranty');
|
||||
$header[] = trans('admin/hardware/form.warranty_expires');
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation')) {
|
||||
$header[] = trans('admin/hardware/table.book_value');
|
||||
$header[] = trans('admin/hardware/table.diff');
|
||||
$header[] = trans('admin/hardware/form.fully_depreciated');
|
||||
}
|
||||
|
||||
if ($request->filled('checkout_date')) {
|
||||
$header[] = trans('admin/hardware/table.checkout_date');
|
||||
}
|
||||
@@ -805,6 +805,19 @@ class ReportsController extends Controller
|
||||
$row[] = ($asset->purchase_date != '') ? $asset->asset_eol_date : '';
|
||||
}
|
||||
|
||||
if ($request->filled('warranty')) {
|
||||
$row[] = ($asset->warranty_months) ? $asset->warranty_months : '';
|
||||
$row[] = $asset->present()->warranty_expires();
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation')) {
|
||||
$depreciation = $asset->getDepreciatedValue();
|
||||
$diff = ($asset->purchase_cost - $depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($diff);
|
||||
$row[] = (($asset->depreciation) && ($asset->depreciated_date())) ? $asset->depreciated_date()->format('Y-m-d') : '';
|
||||
}
|
||||
|
||||
if ($request->filled('order')) {
|
||||
$row[] = ($asset->order_number) ? $asset->order_number : '';
|
||||
}
|
||||
@@ -938,19 +951,6 @@ class ReportsController extends Controller
|
||||
$row[] = ($asset->assetstatus) ? $asset->assetstatus->name.' ('.$asset->present()->statusMeta.')' : '';
|
||||
}
|
||||
|
||||
if ($request->filled('warranty')) {
|
||||
$row[] = ($asset->warranty_months) ? $asset->warranty_months : '';
|
||||
$row[] = $asset->present()->warranty_expires();
|
||||
}
|
||||
|
||||
if ($request->filled('depreciation')) {
|
||||
$depreciation = $asset->getDepreciatedValue();
|
||||
$diff = ($asset->purchase_cost - $depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($depreciation);
|
||||
$row[] = Helper::formatCurrencyOutput($diff);
|
||||
$row[] = (($asset->depreciation) && ($asset->depreciated_date())) ? $asset->depreciated_date()->format('Y-m-d') : '';
|
||||
}
|
||||
|
||||
if ($request->filled('checkout_date')) {
|
||||
$row[] = ($asset->last_checkout) ? $asset->last_checkout : '';
|
||||
}
|
||||
@@ -1081,10 +1081,10 @@ class ReportsController extends Controller
|
||||
$row[] = e($assetMaintenance->start_date);
|
||||
$row[] = e($assetMaintenance->completion_date);
|
||||
if (is_null($assetMaintenance->asset_maintenance_time)) {
|
||||
$improvementTime = intval(Carbon::now()
|
||||
->diffInDays(Carbon::parse($assetMaintenance->start_date)));
|
||||
$improvementTime = (int) Carbon::now()
|
||||
->diffInDays(Carbon::parse($assetMaintenance->start_date), true);
|
||||
} else {
|
||||
$improvementTime = intval($assetMaintenance->asset_maintenance_time);
|
||||
$improvementTime = (int) $assetMaintenance->asset_maintenance_time;
|
||||
}
|
||||
$row[] = $improvementTime;
|
||||
$row[] = trans('general.currency') . Helper::formatCurrencyOutput($assetMaintenance->cost);
|
||||
|
||||
@@ -428,6 +428,13 @@ class SettingsController extends Controller
|
||||
$setting->label_logo = null;
|
||||
}
|
||||
|
||||
// Acceptance PDF upload
|
||||
$setting = $request->handleImages($setting, 600, 'acceptance_pdf_logo', '', 'acceptance_pdf_logo');
|
||||
if ('1' == $request->input('clear_acceptance_pdf_logo')) {
|
||||
$setting = $request->deleteExistingImage($setting, '', 'acceptance_pdf_logo');
|
||||
$setting->acceptance_pdf_logo = null;
|
||||
}
|
||||
|
||||
// Favicon upload
|
||||
$setting = $request->handleImages($setting, 100, 'favicon', '', 'favicon');
|
||||
if ('1' == $request->input('clear_favicon')) {
|
||||
@@ -435,6 +442,7 @@ class SettingsController extends Controller
|
||||
$setting->favicon = null;
|
||||
}
|
||||
|
||||
|
||||
// Default avatar upload
|
||||
$setting = $request->handleImages($setting, 500, 'default_avatar', 'avatars', 'default_avatar');
|
||||
if ($request->input('clear_default_avatar') == '1') {
|
||||
|
||||
@@ -395,13 +395,22 @@ class UsersController extends Controller
|
||||
// Make sure the user can view users at all
|
||||
$this->authorize('view', User::class);
|
||||
|
||||
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find($user->id);
|
||||
$user = User::with([
|
||||
'consumables',
|
||||
'accessories',
|
||||
'licenses',
|
||||
'userloc',
|
||||
])
|
||||
->withTrashed()
|
||||
->find($user->id);
|
||||
|
||||
// Make sure they can view this particular user
|
||||
$this->authorize('view', $user);
|
||||
|
||||
$userlog = $user->userlog->load('item');
|
||||
return view('users/view', compact('user', 'userlog'))->with('settings', Setting::getSettings());
|
||||
return view('users/view', [
|
||||
'user' => $user,
|
||||
'settings' => Setting::getSettings(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,18 +2,21 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
|
||||
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
|
||||
use App\Exceptions\AssetNotRequestable;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\RequestAssetCancelation;
|
||||
use App\Notifications\RequestAssetNotification;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use Log;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to the ability for users
|
||||
@@ -81,7 +84,7 @@ class ViewAssetsController extends Controller
|
||||
return view('account/requestable-assets', compact('assets', 'models'));
|
||||
}
|
||||
|
||||
public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null) : RedirectResponse
|
||||
public function getRequestItem(Request $request, $itemType, $itemId = null, $cancel_by_admin = false, $requestingUser = null): RedirectResponse
|
||||
{
|
||||
$item = null;
|
||||
$fullItemType = 'App\\Models\\'.studly_case($itemType);
|
||||
@@ -144,63 +147,33 @@ class ViewAssetsController extends Controller
|
||||
* Process a specific requested asset
|
||||
* @param null $assetId
|
||||
*/
|
||||
public function getRequestAsset($assetId = null) : RedirectResponse
|
||||
public function store(Asset $asset): RedirectResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
// Check if the asset exists and is requestable
|
||||
if (is_null($asset = Asset::RequestableAssets()->find($assetId))) {
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('error', trans('admin/hardware/message.does_not_exist_or_not_requestable'));
|
||||
}
|
||||
if (! Company::isCurrentUserHasAccess($asset)) {
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('error', trans('general.insufficient_permissions'));
|
||||
}
|
||||
|
||||
$data['item'] = $asset;
|
||||
$data['target'] = auth()->user();
|
||||
$data['item_quantity'] = 1;
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
$logaction = new Actionlog();
|
||||
$logaction->item_id = $data['asset_id'] = $asset->id;
|
||||
$logaction->item_type = $data['item_type'] = Asset::class;
|
||||
$logaction->created_at = $data['requested_date'] = date('Y-m-d H:i:s');
|
||||
|
||||
if ($user->location_id) {
|
||||
$logaction->location_id = $user->location_id;
|
||||
}
|
||||
$logaction->target_id = $data['user_id'] = auth()->id();
|
||||
$logaction->target_type = User::class;
|
||||
|
||||
// If it's already requested, cancel the request.
|
||||
if ($asset->isRequestedBy(auth()->user())) {
|
||||
$asset->cancelRequest();
|
||||
$asset->decrement('requests_counter', 1);
|
||||
|
||||
$logaction->logaction('request canceled');
|
||||
try {
|
||||
$settings->notify(new RequestAssetCancelation($data));
|
||||
} catch (\Exception $e) {
|
||||
Log::warning($e);
|
||||
}
|
||||
return redirect()->route('requestable-assets')
|
||||
->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
||||
}
|
||||
|
||||
$logaction->logaction('requested');
|
||||
$asset->request();
|
||||
$asset->increment('requests_counter', 1);
|
||||
try {
|
||||
$settings->notify(new RequestAssetNotification($data));
|
||||
} catch (\Exception $e) {
|
||||
Log::warning($e);
|
||||
CreateCheckoutRequestAction::run($asset, auth()->user());
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
} catch (AssetNotRequestable $e) {
|
||||
return redirect()->back()->with('error', 'Asset is not requestable');
|
||||
} catch (AuthorizationException $e) {
|
||||
return redirect()->back()->with('error', trans('admin/hardware/message.requests.error'));
|
||||
} catch (Exception $e) {
|
||||
report($e);
|
||||
return redirect()->back()->with('error', trans('general.something_went_wrong'));
|
||||
}
|
||||
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.success'));
|
||||
}
|
||||
|
||||
public function destroy(Asset $asset): RedirectResponse
|
||||
{
|
||||
try {
|
||||
CancelCheckoutRequestAction::run($asset, auth()->user());
|
||||
return redirect()->route('requestable-assets')->with('success')->with('success', trans('admin/hardware/message.requests.canceled'));
|
||||
} catch (Exception $e) {
|
||||
report($e);
|
||||
return redirect()->back()->with('error', trans('general.something_went_wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getRequestedAssets() : View
|
||||
{
|
||||
return view('account/requested');
|
||||
|
||||
@@ -29,6 +29,12 @@ class StoreLabelSettings extends FormRequest
|
||||
return $label->getName();
|
||||
})->values()->toArray();
|
||||
|
||||
if (empty($this->input('label2_template'))) {
|
||||
$this->merge([
|
||||
'label2_template' => 'DefaultLabel',
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'labels_per_page' => 'numeric',
|
||||
'labels_width' => 'numeric',
|
||||
|
||||
@@ -10,19 +10,36 @@ trait MayContainCustomFields
|
||||
// this gets called automatically on a form request
|
||||
public function withValidator($validator)
|
||||
{
|
||||
// find the model
|
||||
if ($this->method() == 'POST') {
|
||||
$asset_model = AssetModel::find($this->model_id);
|
||||
}
|
||||
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
|
||||
$asset_model = $this->asset->model;
|
||||
|
||||
// In case the model is being changed via form
|
||||
if (request()->has('model_id')!='') {
|
||||
|
||||
$asset_model = AssetModel::find(request()->input('model_id'));
|
||||
|
||||
// or if we have it available to route-model-binding
|
||||
} elseif ((request()->route('asset') && (request()->route('asset')->model_id))) {
|
||||
|
||||
$asset_model = AssetModel::find(request()->route('asset')->model_id);
|
||||
|
||||
} else {
|
||||
|
||||
if ($this->method() == 'POST') {
|
||||
$asset_model = AssetModel::find($this->model_id);
|
||||
}
|
||||
|
||||
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
|
||||
$asset_model = $this->asset->model;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// collect the custom fields in the request
|
||||
$validator->after(function ($validator) use ($asset_model) {
|
||||
$request_fields = $this->collect()->keys()->filter(function ($attributes) {
|
||||
return str_starts_with($attributes, '_snipeit_');
|
||||
});
|
||||
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
|
||||
|
||||
// if there are custom fields, find the ones that don't exist on the model's fieldset and add an error to the validator's error bag
|
||||
if (count($request_fields) > 0 && $validator->errors()->isEmpty()) {
|
||||
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
|
||||
->each(function ($request_field_name) use ($request_fields, $validator) {
|
||||
|
||||
@@ -37,8 +37,10 @@ class AccessoriesTransformer
|
||||
'purchase_date' => ($accessory->purchase_date) ? Helper::getFormattedDateObject($accessory->purchase_date, 'date') : null,
|
||||
'purchase_cost' => Helper::formatCurrencyOutput($accessory->purchase_cost),
|
||||
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||
'remaining_qty' => (int) ($accessory->qty - $accessory->checkouts_count),
|
||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null, // Legacy - should phase out - replaced by below, for the bootstrap table formatter
|
||||
'min_amt' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||
'remaining_qty' => (int) ($accessory->qty - $accessory->checkouts_count), // Legacy - should phase out - replaced by below, for the bootstrap table formatter
|
||||
'remaining' => (int) ($accessory->qty - $accessory->checkouts_count),
|
||||
'checkouts_count' => $accessory->checkouts_count,
|
||||
'created_by' => ($accessory->adminuser) ? [
|
||||
'id' => (int) $accessory->adminuser->id,
|
||||
|
||||
@@ -46,8 +46,9 @@ class AssetModelsTransformer
|
||||
'name'=> e($assetmodel->manufacturer->name),
|
||||
] : null,
|
||||
'image' => ($assetmodel->image != '') ? Storage::disk('public')->url('models/'.e($assetmodel->image)) : null,
|
||||
'model_number' => e($assetmodel->model_number),
|
||||
'min_amt' => ($assetmodel->min_amt!='') ? (int) $assetmodel->min_amt : null,
|
||||
'model_number' => ($assetmodel->model_number ? e($assetmodel->model_number): null),
|
||||
'min_amt' => ($assetmodel->min_amt) ? (int) $assetmodel->min_amt : null,
|
||||
'remaining' => (int) ($assetmodel->assets_count - $assetmodel->min_amt),
|
||||
'depreciation' => ($assetmodel->depreciation) ? [
|
||||
'id' => (int) $assetmodel->depreciation->id,
|
||||
'name'=> e($assetmodel->depreciation->name),
|
||||
|
||||
@@ -42,7 +42,7 @@ class AssetsTransformer
|
||||
'requestable' => ($asset->requestable ? true : false),
|
||||
|
||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
||||
'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date).' months' : null,
|
||||
'eol' => (($asset->asset_eol_date != '') && ($asset->purchase_date != '')) ? (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true) . ' months' : null,
|
||||
'asset_eol_date' => ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date') : null,
|
||||
'status_label' => ($asset->assetstatus) ? [
|
||||
'id' => (int) $asset->assetstatus->id,
|
||||
|
||||
@@ -50,6 +50,9 @@ class CustomFieldsTransformer
|
||||
'display_in_user_view' => ($field->display_in_user_view =='1') ? true : false,
|
||||
'auto_add_to_fieldsets' => ($field->auto_add_to_fieldsets == '1') ? true : false,
|
||||
'show_in_listview' => ($field->show_in_listview == '1') ? true : false,
|
||||
'display_checkin' => ($field->display_checkin == '1') ? true : false,
|
||||
'display_checkout' => ($field->display_checkout == '1') ? true : false,
|
||||
'display_audit' => ($field->display_audit == '1') ? true : false,
|
||||
'created_at' => Helper::getFormattedDateObject($field->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($field->updated_at, 'datetime'),
|
||||
];
|
||||
|
||||
@@ -38,6 +38,7 @@ class LicensesTransformer
|
||||
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
||||
'seats' => (int) $license->seats,
|
||||
'free_seats_count' => (int) $license->free_seats_count,
|
||||
'remaining' => (int) $license->free_seats_count,
|
||||
'min_amt' => ($license->min_amt) ? (int) ($license->min_amt) : null,
|
||||
'license_name' => ($license->license_name) ? e($license->license_name) : null,
|
||||
'license_email' => ($license->license_email) ? e($license->license_email) : null,
|
||||
|
||||
@@ -101,11 +101,8 @@ class LocationsTransformer
|
||||
$array = [
|
||||
'id' => $accessory_checkout->id,
|
||||
'assigned_to' => $accessory_checkout->assigned_to,
|
||||
'accessory' => [
|
||||
'id' => $accessory_checkout->accessory->id,
|
||||
'name' => $accessory_checkout->accessory->name,
|
||||
],
|
||||
'image' => ($accessory_checkout->accessory->image) ? Storage::disk('public')->url('accessories/'.e($accessory_checkout->accessory->image)) : null,
|
||||
'accessory' => $this->transformAccessory($accessory_checkout->accessory),
|
||||
'image' => ($accessory_checkout?->accessory?->image) ? Storage::disk('public')->url('accessories/' . e($accessory_checkout->accessory->image)) : null,
|
||||
'note' => $accessory_checkout->note ? e($accessory_checkout->note) : null,
|
||||
'created_by' => $accessory_checkout->adminuser ? [
|
||||
'id' => (int) $accessory_checkout->adminuser->id,
|
||||
@@ -153,4 +150,16 @@ class LocationsTransformer
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function transformAccessory(?Accessory $accessory): ?array
|
||||
{
|
||||
if ($accessory) {
|
||||
return [
|
||||
'id' => $accessory->id,
|
||||
'name' => $accessory->name,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ class ConsumableImporter extends ItemImporter
|
||||
{
|
||||
$consumable = Consumable::where('name', trim($this->item['name']))->first();
|
||||
if ($consumable) {
|
||||
|
||||
if (! $this->updating) {
|
||||
$this->log('A matching Consumable '.$this->item['name'].' already exists. ');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->log('Updating Consumable');
|
||||
@@ -39,12 +39,10 @@ class ConsumableImporter extends ItemImporter
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log('No matching consumable, creating one');
|
||||
$consumable = new Consumable();
|
||||
$consumable->created_by = auth()->id();
|
||||
$this->item['model_number'] = trim($this->findCsvMatch($row, 'model_number'));
|
||||
$this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number'));
|
||||
$this->item['min_amt'] = trim($this->findCsvMatch($row, "min_amt"));
|
||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||
|
||||
// This sets an attribute on the Loggable trait for the action log
|
||||
|
||||
@@ -24,6 +24,12 @@ class ItemImporter extends Importer
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
|
||||
/**
|
||||
* This section adds the most common fields into the $item array so we don't have to manually add them to
|
||||
* things like accessories, consumables, etc.
|
||||
*/
|
||||
|
||||
// Need to reset this between iterations or we'll have stale data.
|
||||
$this->item = [];
|
||||
|
||||
@@ -73,29 +79,20 @@ class ItemImporter extends Importer
|
||||
$this->item['notes'] = $this->findCsvMatch($row, 'notes');
|
||||
$this->item['order_number'] = $this->findCsvMatch($row, 'order_number');
|
||||
$this->item['purchase_cost'] = $this->findCsvMatch($row, 'purchase_cost');
|
||||
$this->item['model_number'] = trim($this->findCsvMatch($row, 'model_number'));
|
||||
$this->item['min_amt'] = $this->findCsvMatch($row, 'min_amt');
|
||||
$this->item['qty'] = $this->findCsvMatch($row, 'quantity');
|
||||
$this->item['requestable'] = $this->findCsvMatch($row, 'requestable');
|
||||
$this->item['created_by'] = auth()->id();
|
||||
$this->item['serial'] = $this->findCsvMatch($row, 'serial');
|
||||
$this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number'));
|
||||
|
||||
|
||||
$this->item['purchase_date'] = null;
|
||||
if ($this->findCsvMatch($row, 'purchase_date') != '') {
|
||||
$this->item['purchase_date'] = date('Y-m-d', strtotime($this->findCsvMatch($row, 'purchase_date')));
|
||||
}
|
||||
|
||||
// $this->item['asset_eol_date'] = null;
|
||||
// if ($this->findCsvMatch($row, 'asset_eol_date') != '') {
|
||||
// $csvMatch = $this->findCsvMatch($row, 'asset_eol_date');
|
||||
// \Log::warning('EOL Date for $csvMatch is '.$csvMatch);
|
||||
// try {
|
||||
// $this->item['asset_eol_date'] = CarbonImmutable::parse($csvMatch)->format('Y-m-d');
|
||||
// } catch (\Exception $e) {
|
||||
// Log::info($e->getMessage());
|
||||
// $this->log('Unable to parse date: '.$csvMatch);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
$this->item['qty'] = $this->findCsvMatch($row, 'quantity');
|
||||
$this->item['requestable'] = $this->findCsvMatch($row, 'requestable');
|
||||
$this->item['created_by'] = auth()->id();
|
||||
$this->item['serial'] = $this->findCsvMatch($row, 'serial');
|
||||
// NO need to call this method if we're running the user import.
|
||||
// TODO: Merge these methods.
|
||||
$this->item['checkout_class'] = $this->findCsvMatch($row, 'checkout_class');
|
||||
@@ -512,7 +509,6 @@ class ItemImporter extends Importer
|
||||
|
||||
if ($supplier->save()) {
|
||||
$this->log('Supplier '.$item_supplier.' was created');
|
||||
|
||||
return $supplier->id;
|
||||
}
|
||||
$this->logError($supplier, 'Supplier');
|
||||
|
||||
@@ -74,7 +74,7 @@ class CheckoutAssetMail extends Mailable
|
||||
{
|
||||
$this->item->load('assetstatus');
|
||||
$eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : '';
|
||||
$req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
|
||||
$req_accept = $this->requiresAcceptance();
|
||||
$fields = [];
|
||||
|
||||
// Check if the item has custom fields associated with it
|
||||
@@ -98,6 +98,7 @@ class CheckoutAssetMail extends Mailable
|
||||
'accept_url' => $accept_url,
|
||||
'last_checkout' => $this->last_checkout,
|
||||
'expected_checkin' => $this->expected_checkin,
|
||||
'introduction_line' => $this->introductionLine(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -120,4 +121,27 @@ class CheckoutAssetMail extends Mailable
|
||||
|
||||
return trans('mail.unaccepted_asset_reminder');
|
||||
}
|
||||
|
||||
private function introductionLine(): string
|
||||
{
|
||||
if ($this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans('mail.new_item_checked_with_acceptance');
|
||||
}
|
||||
|
||||
if ($this->firstTimeSending && !$this->requiresAcceptance()) {
|
||||
return trans('mail.new_item_checked');
|
||||
}
|
||||
|
||||
if (!$this->firstTimeSending && $this->requiresAcceptance()) {
|
||||
return trans('mail.recent_item_checked');
|
||||
}
|
||||
|
||||
// we shouldn't get here but let's send a default message just in case
|
||||
return trans('new_item_checked');
|
||||
}
|
||||
|
||||
private function requiresAcceptance(): int|bool
|
||||
{
|
||||
return method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,9 @@ class Actionlog extends SnipeModel
|
||||
'user_agent',
|
||||
'item_type',
|
||||
'target_type',
|
||||
'action_source'
|
||||
'action_source',
|
||||
'created_at',
|
||||
'action_date',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -69,7 +71,25 @@ class Actionlog extends SnipeModel
|
||||
'company' => ['name'],
|
||||
'adminuser' => ['first_name','last_name','username', 'email'],
|
||||
'user' => ['first_name','last_name','username', 'email'],
|
||||
'assets' => ['asset_tag','name', 'serial'],
|
||||
'assets' => ['asset_tag','name', 'serial', 'order_number', 'notes', 'purchase_date'],
|
||||
'assets.model' => ['name', 'model_number', 'eol', 'notes'],
|
||||
'assets.model.category' => ['name', 'notes'],
|
||||
'assets.model.manufacturer' => ['name', 'notes'],
|
||||
'licenses' => ['name', 'serial', 'notes', 'order_number', 'license_email', 'license_name', 'purchase_order', 'purchase_date'],
|
||||
'licenses.category' => ['name', 'notes'],
|
||||
'licenses.supplier' => ['name'],
|
||||
'consumables' => ['name', 'notes', 'order_number', 'model_number', 'item_no', 'purchase_date'],
|
||||
'consumables.category' => ['name', 'notes'],
|
||||
'consumables.location' => ['name', 'notes'],
|
||||
'consumables.supplier' => ['name', 'notes'],
|
||||
'components' => ['name', 'notes', 'purchase_date'],
|
||||
'components.category' => ['name', 'notes'],
|
||||
'components.location' => ['name', 'notes'],
|
||||
'components.supplier' => ['name', 'notes'],
|
||||
'accessories' => ['name', 'purchase_date'],
|
||||
'accessories.category' => ['name'],
|
||||
'accessories.location' => ['name', 'notes'],
|
||||
'accessories.supplier' => ['name', 'notes'],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -134,6 +154,54 @@ class Actionlog extends SnipeModel
|
||||
return $this->hasMany(\App\Models\Asset::class, 'id', 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the actionlog -> license relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function licenses()
|
||||
{
|
||||
return $this->hasMany(\App\Models\License::class, 'id', 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the actionlog -> consumable relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function consumables()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Consumable::class, 'id', 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the actionlog -> consumable relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function accessories()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Accessory::class, 'id', 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the actionlog -> components relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function components()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Component::class, 'id', 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the actionlog -> item type relationship
|
||||
*
|
||||
@@ -295,13 +363,13 @@ class Actionlog extends SnipeModel
|
||||
$now = Carbon::now();
|
||||
$last_audit_date = $this->created_at; // this is the action log's created at, not the asset itself
|
||||
$next_audit = $last_audit_date->addMonth($monthInterval); // this actually *modifies* the $last_audit_date
|
||||
$next_audit_days = round($now->diffInDays($next_audit, true));
|
||||
$next_audit_days = (int) round($now->diffInDays($next_audit, true));
|
||||
$override_default_next = $next_audit;
|
||||
|
||||
// Override the default setting for interval if the asset has its own next audit date
|
||||
if (($asset) && ($asset->next_audit_date)) {
|
||||
$override_default_next = Carbon::parse($asset->next_audit_date);
|
||||
$next_audit_days = round($override_default_next->diffInDays($now, true));
|
||||
$next_audit_days = (int) round($override_default_next->diffInDays($now, true));
|
||||
}
|
||||
|
||||
// Show as negative number if the next audit date is before the audit date we're looking at
|
||||
|
||||
@@ -213,6 +213,31 @@ class Asset extends Depreciable
|
||||
$this->attributes['expected_checkin'] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function customFieldValidationRules()
|
||||
{
|
||||
|
||||
$customFieldValidationRules = [];
|
||||
|
||||
if (($this->model) && ($this->model->fieldset)) {
|
||||
|
||||
foreach ($this->model->fieldset->fields as $field) {
|
||||
|
||||
if ($field->format == 'BOOLEAN'){
|
||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
}
|
||||
|
||||
$customFieldValidationRules += $this->model->fieldset->validation_rules();
|
||||
}
|
||||
|
||||
return $customFieldValidationRules;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This handles the custom field validation for assets
|
||||
*
|
||||
@@ -220,29 +245,7 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public function save(array $params = [])
|
||||
{
|
||||
if ($this->model_id != '') {
|
||||
$model = AssetModel::find($this->model_id);
|
||||
|
||||
if (($model) && ($model->fieldset)) {
|
||||
|
||||
foreach ($model->fieldset->fields as $field){
|
||||
if($field->format == 'BOOLEAN'){
|
||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
}
|
||||
|
||||
$this->rules += $model->fieldset->validation_rules();
|
||||
|
||||
if ($this->model->fieldset){
|
||||
foreach ($this->model->fieldset->fields as $field){
|
||||
if($field->format == 'BOOLEAN'){
|
||||
$this->{$field->db_column} = filter_var($this->{$field->db_column}, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->rules += $this->customFieldValidationRules();
|
||||
return parent::save($params);
|
||||
}
|
||||
|
||||
@@ -254,7 +257,7 @@ class Asset extends Depreciable
|
||||
|
||||
/**
|
||||
* Returns the warranty expiration date as Carbon object
|
||||
* @return \Carbon|null
|
||||
* @return \Carbon\Carbon|null
|
||||
*/
|
||||
public function getWarrantyExpiresAttribute()
|
||||
{
|
||||
@@ -687,6 +690,21 @@ class Asset extends Depreciable
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of audits for this asset
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function audits()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'audit')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of checkins for this asset
|
||||
*
|
||||
@@ -1456,7 +1474,7 @@ class Asset extends Depreciable
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
|
||||
public function scopeRequestableAssets($query)
|
||||
public function scopeRequestableAssets($query): Builder
|
||||
{
|
||||
$table = $query->getModel()->getTable();
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ final class Company extends SnipeModel
|
||||
'phone',
|
||||
'fax',
|
||||
'email',
|
||||
'created_by'
|
||||
'created_by',
|
||||
'notes',
|
||||
];
|
||||
|
||||
private static function isFullMultipleCompanySupportEnabled()
|
||||
|
||||
@@ -75,6 +75,7 @@ class Consumable extends SnipeModel
|
||||
'item_no',
|
||||
'location_id',
|
||||
'manufacturer_id',
|
||||
'supplier_id',
|
||||
'name',
|
||||
'order_number',
|
||||
'model_number',
|
||||
|
||||
@@ -30,6 +30,7 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
protected $fillable = [
|
||||
'assigned_to',
|
||||
'asset_id',
|
||||
'notes',
|
||||
];
|
||||
|
||||
use Acceptable;
|
||||
|
||||
@@ -220,9 +220,41 @@ trait Loggable
|
||||
* @since [v4.0]
|
||||
* @return \App\Models\Actionlog
|
||||
*/
|
||||
public function logAudit($note, $location_id, $filename = null)
|
||||
public function logAudit($note, $location_id, $filename = null, $originalValues = [])
|
||||
{
|
||||
|
||||
$log = new Actionlog;
|
||||
|
||||
if (static::class == Asset::class) {
|
||||
if ($asset = Asset::find($log->item_id)) {
|
||||
// add the custom fields that were changed
|
||||
if ($asset->model->fieldset) {
|
||||
$fields_array = [];
|
||||
foreach ($asset->model->fieldset->fields as $field) {
|
||||
if ($field->display_audit == 1) {
|
||||
$fields_array[$field->db_column] = $asset->{$field->db_column};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$changed = [];
|
||||
|
||||
unset($originalValues['updated_at'], $originalValues['last_audit_date']);
|
||||
foreach ($originalValues as $key => $value) {
|
||||
|
||||
if ($value != $this->getAttributes()[$key]) {
|
||||
$changed[$key]['old'] = $value;
|
||||
$changed[$key]['new'] = $this->getAttributes()[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($changed)){
|
||||
$log->log_meta = json_encode($changed);
|
||||
}
|
||||
|
||||
|
||||
$location = Location::find($location_id);
|
||||
if (static::class == LicenseSeat::class) {
|
||||
$log->item_type = License::class;
|
||||
@@ -235,6 +267,7 @@ trait Loggable
|
||||
$log->note = $note;
|
||||
$log->created_by = auth()->id();
|
||||
$log->filename = $filename;
|
||||
$log->action_date = date('Y-m-d H:i:s');
|
||||
$log->logaction('audit');
|
||||
|
||||
$params = [
|
||||
@@ -276,6 +309,7 @@ trait Loggable
|
||||
$log->item_id = $this->id;
|
||||
}
|
||||
$log->location_id = null;
|
||||
$log->action_date = date('Y-m-d H:i:s');
|
||||
$log->note = $note;
|
||||
$log->created_by = $created_by;
|
||||
$log->logaction('create');
|
||||
@@ -303,6 +337,7 @@ trait Loggable
|
||||
$log->note = $note;
|
||||
$log->target_id = null;
|
||||
$log->created_at = date('Y-m-d H:i:s');
|
||||
$log->action_date = date('Y-m-d H:i:s');
|
||||
$log->filename = $filename;
|
||||
$log->logaction('uploaded');
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ class AssetObserver
|
||||
// determine if explicit and set eol_explicit to true
|
||||
if (!is_null($asset->asset_eol_date) && !is_null($asset->purchase_date)) {
|
||||
if($asset->model->eol > 0) {
|
||||
$months = Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date);
|
||||
$months = (int) Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date, true);
|
||||
if($months != $asset->model->eol) {
|
||||
$asset->eol_explicit = true;
|
||||
}
|
||||
|
||||
@@ -78,28 +78,36 @@ class AccessoryPresenter extends Presenter
|
||||
'sortable' => true,
|
||||
'title' => trans('general.location'),
|
||||
'formatter' => 'locationsLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'formatter' => 'minAmtFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'qty',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/accessories/general.total'),
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'remaining_qty',
|
||||
'field' => 'remaining',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'visible' => false,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
],[
|
||||
'field' => 'checkouts_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
'title' => trans('general.checked_out'),
|
||||
], [
|
||||
'field' => 'min_qty',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'purchase_date',
|
||||
'searchable' => true,
|
||||
@@ -113,7 +121,7 @@ class AccessoryPresenter extends Presenter
|
||||
'sortable' => true,
|
||||
'title' => trans('general.purchase_cost'),
|
||||
'footerFormatter' => 'sumFormatterQuantity',
|
||||
'class' => 'text-right',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'order_number',
|
||||
'searchable' => true,
|
||||
@@ -221,7 +229,7 @@ class AccessoryPresenter extends Presenter
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.admin'),
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => false,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],
|
||||
|
||||
@@ -102,6 +102,10 @@ class ActionlogPresenter extends Presenter
|
||||
return 'fas fa-sticky-note';
|
||||
}
|
||||
|
||||
if ($this->action_type == 'audit') {
|
||||
return 'fas fa-clipboard-check';
|
||||
}
|
||||
|
||||
return 'fa-solid fa-rotate-right';
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,10 @@ class AssetModelPresenter extends Presenter
|
||||
'switchable' => true,
|
||||
'title' => trans('mail.min_QTY'),
|
||||
'visible' => true,
|
||||
'formatter' => 'minAmtFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
],
|
||||
|
||||
[
|
||||
'field' => 'assets_count',
|
||||
'searchable' => false,
|
||||
@@ -82,6 +85,19 @@ class AssetModelPresenter extends Presenter
|
||||
'switchable' => true,
|
||||
'title' => trans('admin/models/table.numassets'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
],
|
||||
|
||||
[
|
||||
'field' => 'remaining',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.remaining'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
],
|
||||
[
|
||||
'field' => 'depreciation',
|
||||
|
||||
@@ -555,7 +555,7 @@ class AssetPresenter extends Presenter
|
||||
*/
|
||||
public function statusMeta()
|
||||
{
|
||||
if ($this->model->assigned) {
|
||||
if ($this->model->assigned_to) {
|
||||
return 'deployed';
|
||||
}
|
||||
|
||||
|
||||
@@ -79,24 +79,30 @@ class ComponentPresenter extends Presenter
|
||||
'title' => trans('general.manufacturer'),
|
||||
'visible' => false,
|
||||
'formatter' => 'manufacturersLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'formatter' => 'minAmtFormatter',
|
||||
], [
|
||||
'field' => 'qty',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/components/general.total'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'remaining',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('admin/components/general.remaining'),
|
||||
'visible' => true,
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.min_amt'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'location',
|
||||
'searchable' => true,
|
||||
|
||||
@@ -72,25 +72,31 @@ class ConsumablePresenter extends Presenter
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/consumables/general.item_no'),
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('general.min_amt'),
|
||||
'visible' => true,
|
||||
'formatter' => 'minAmtFormatter',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'qty',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/components/general.total'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'remaining',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/components/general.remaining'),
|
||||
'visible' => true,
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.min_amt'),
|
||||
'visible' => true,
|
||||
], [
|
||||
'field' => 'location',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
@@ -123,7 +129,7 @@ class ConsumablePresenter extends Presenter
|
||||
'title' => trans('general.purchase_cost'),
|
||||
'visible' => true,
|
||||
'footerFormatter' => 'sumFormatterQuantity',
|
||||
'class' => 'text-right',
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'notes',
|
||||
'searchable' => true,
|
||||
|
||||
@@ -41,7 +41,7 @@ class LicensePresenter extends Presenter
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/licenses/form.license_key'),
|
||||
'formatter' => 'licensesLinkFormatter',
|
||||
'formatter' => 'licenseKeyFormatter',
|
||||
], [
|
||||
'field' => 'expiration_date',
|
||||
'searchable' => true,
|
||||
@@ -80,24 +80,28 @@ class LicensePresenter extends Presenter
|
||||
'sortable' => true,
|
||||
'title' => trans('general.manufacturer'),
|
||||
'formatter' => 'manufacturersLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'seats',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/accessories/general.total'),
|
||||
], [
|
||||
'field' => 'free_seats_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
],
|
||||
[
|
||||
], [
|
||||
'field' => 'min_amt',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('mail.min_QTY'),
|
||||
'formatter' => 'minAmtFormatter',
|
||||
],[
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
], [
|
||||
'field' => 'seats',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/accessories/general.total'),
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'free_seats_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
'class' => 'text-right text-padding-number-cell',
|
||||
'footerFormatter' => 'qtySumFormatter',
|
||||
], [
|
||||
'field' => 'purchase_date',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
|
||||
@@ -262,7 +262,7 @@ class LocationPresenter extends Presenter
|
||||
'field' => 'created_by',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
'title' => trans('general.admin'),
|
||||
'title' => trans('general.created_by'),
|
||||
'visible' => false,
|
||||
'formatter' => 'usersLinkObjFormatter',
|
||||
],
|
||||
|
||||
@@ -139,6 +139,9 @@ class Label implements View
|
||||
case 'plain_serial_number':
|
||||
$barcode2DTarget = $asset->serial;
|
||||
break;
|
||||
case 'location':
|
||||
$barcode2DTarget = route('locations.show', $asset->location_id);
|
||||
break;
|
||||
case 'hardware_id':
|
||||
default:
|
||||
$barcode2DTarget = route('hardware.show', $asset);
|
||||
|
||||
42
composer.lock
generated
42
composer.lock
generated
@@ -4679,16 +4679,16 @@
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.8.1",
|
||||
"version": "3.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4"
|
||||
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
|
||||
"reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6",
|
||||
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4766,7 +4766,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||
"source": "https://github.com/Seldaek/monolog/tree/3.8.1"
|
||||
"source": "https://github.com/Seldaek/monolog/tree/3.9.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4778,7 +4778,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-05T17:15:07+00:00"
|
||||
"time": "2025-03-24T10:02:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mtdowling/jmespath.php",
|
||||
@@ -7479,22 +7479,22 @@
|
||||
},
|
||||
{
|
||||
"name": "rollbar/rollbar",
|
||||
"version": "v4.0.2",
|
||||
"version": "v4.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rollbar/rollbar-php.git",
|
||||
"reference": "6966a4c97c170298108f5e5b543c2710658f8fe0"
|
||||
"reference": "146216bbfde503632c6969a7c334c8a63c98f50e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/rollbar/rollbar-php/zipball/6966a4c97c170298108f5e5b543c2710658f8fe0",
|
||||
"reference": "6966a4c97c170298108f5e5b543c2710658f8fe0",
|
||||
"url": "https://api.github.com/repos/rollbar/rollbar-php/zipball/146216bbfde503632c6969a7c334c8a63c98f50e",
|
||||
"reference": "146216bbfde503632c6969a7c334c8a63c98f50e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"monolog/monolog": "^2 || ^3",
|
||||
"php": ">=8.0.0 <9.0",
|
||||
"php": ">=8.1.0 <9.0",
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -7536,28 +7536,28 @@
|
||||
"support": {
|
||||
"email": "support@rollbar.com",
|
||||
"issues": "https://github.com/rollbar/rollbar-php/issues",
|
||||
"source": "https://github.com/rollbar/rollbar-php/tree/v4.0.2"
|
||||
"source": "https://github.com/rollbar/rollbar-php/tree/v4.1.1"
|
||||
},
|
||||
"time": "2023-12-22T21:40:31+00:00"
|
||||
"time": "2025-03-21T19:07:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rollbar/rollbar-laravel",
|
||||
"version": "v8.0.1",
|
||||
"version": "v8.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rollbar/rollbar-php-laravel.git",
|
||||
"reference": "69ecc7c19b54b1d88bf8a2bdfa56277500cc41f6"
|
||||
"reference": "fbe96ea28601294aa7dc41cd16db62cddddeb0c2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/rollbar/rollbar-php-laravel/zipball/69ecc7c19b54b1d88bf8a2bdfa56277500cc41f6",
|
||||
"reference": "69ecc7c19b54b1d88bf8a2bdfa56277500cc41f6",
|
||||
"url": "https://api.github.com/repos/rollbar/rollbar-php-laravel/zipball/fbe96ea28601294aa7dc41cd16db62cddddeb0c2",
|
||||
"reference": "fbe96ea28601294aa7dc41cd16db62cddddeb0c2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^10.0|^11.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||
"php": "^8.1",
|
||||
"rollbar/rollbar": "^4.0"
|
||||
"rollbar/rollbar": "v4.1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1",
|
||||
@@ -7608,9 +7608,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/rollbar/rollbar-php-laravel/issues",
|
||||
"source": "https://github.com/rollbar/rollbar-php-laravel/tree/v8.0.1"
|
||||
"source": "https://github.com/rollbar/rollbar-php-laravel/tree/v8.1.2"
|
||||
},
|
||||
"time": "2024-02-13T21:58:17+00:00"
|
||||
"time": "2025-03-21T19:26:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sabberworm/php-css-parser",
|
||||
|
||||
@@ -8,6 +8,22 @@
|
||||
| be modified directly.
|
||||
*/
|
||||
|
||||
|
||||
// This is used by the mysql dump options in spatie backup
|
||||
$dump_options = [
|
||||
'dump_binary_path' => env('DB_DUMP_PATH', '/usr/local/bin'), // only the path, so without 'mysqldump'
|
||||
'use_single_transaction' => false,
|
||||
'timeout' => 60 * 5, // 5 minute timeout
|
||||
//'exclude_tables' => ['table1', 'table2'],
|
||||
//'add_extra_option' => '--optionname=optionvalue',
|
||||
];
|
||||
|
||||
// Some versions of mysql do not support the --skip-ssl option and will fail if it is
|
||||
if (env('DB_DUMP_SKIP_SSL') == 'true') {
|
||||
$dump_options['skip_ssl'] = true;
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
@@ -79,14 +95,7 @@ return [
|
||||
'strict' => false,
|
||||
'engine' => 'InnoDB',
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'dump' => [
|
||||
'dump_binary_path' => env('DB_DUMP_PATH', '/usr/local/bin'), // only the path, so without 'mysqldump'
|
||||
'use_single_transaction' => false,
|
||||
'timeout' => 60 * 5, // 5 minute timeout
|
||||
//'exclude_tables' => ['table1', 'table2'],
|
||||
//'add_extra_option' => '--optionname=optionvalue',
|
||||
],
|
||||
|
||||
'dump' => $dump_options,
|
||||
'dump_command_timeout' => 60 * 5, // 5 minute timeout
|
||||
'dump_using_single_transaction' => true, // perform dump using a single transaction
|
||||
'options' => (env('DB_SSL')) ? ((env('DB_SSL_IS_PAAS')) ? [
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v8.0.4',
|
||||
'full_app_version' => 'v8.0.4 - build 17196-gc29bdbdac',
|
||||
'build_version' => '17196',
|
||||
'full_app_version' => 'v8.0.4 - build 17333-gaf408bb45',
|
||||
'build_version' => '17333',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'gc29bdbdac',
|
||||
'full_hash' => 'v8.0.4-29-gc29bdbdac',
|
||||
'hash_version' => 'gaf408bb45',
|
||||
'full_hash' => 'v8.0.4-135-gaf408bb45',
|
||||
'branch' => 'develop',
|
||||
);
|
||||
@@ -4,6 +4,7 @@ namespace Database\Factories;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Location;
|
||||
use App\Models\Statuslabel;
|
||||
@@ -333,6 +334,15 @@ class AssetFactory extends Factory
|
||||
});
|
||||
}
|
||||
|
||||
public function doesNotRequireAcceptance()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'model_id' => AssetModel::factory()->doesNotRequireAcceptance(),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function deleted()
|
||||
{
|
||||
return $this->state(function () {
|
||||
@@ -347,12 +357,22 @@ class AssetFactory extends Factory
|
||||
|
||||
public function requestable()
|
||||
{
|
||||
return $this->state(['requestable' => true]);
|
||||
$id = Statuslabel::factory()->create([
|
||||
'archived' => false,
|
||||
'deployable' => true,
|
||||
'pending' => true,
|
||||
])->id;
|
||||
return $this->state(['status_id' => $id, 'requestable' => true]);
|
||||
}
|
||||
|
||||
public function nonrequestable()
|
||||
{
|
||||
return $this->state(['requestable' => false]);
|
||||
$id = Statuslabel::factory()->create([
|
||||
'archived' => true,
|
||||
'deployable' => false,
|
||||
'pending' => false,
|
||||
])->id;
|
||||
return $this->state(['status_id' => $id, 'requestable' => false]);
|
||||
}
|
||||
|
||||
public function noPurchaseOrEolDate()
|
||||
|
||||
@@ -448,4 +448,13 @@ class AssetModelFactory extends Factory
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function doesNotRequireAcceptance()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'category_id' => Category::factory()->doesNotRequireAcceptance(),
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,4 +207,11 @@ class CategoryFactory extends Factory
|
||||
'category_type' => 'consumable',
|
||||
]);
|
||||
}
|
||||
|
||||
public function doesNotRequireAcceptance()
|
||||
{
|
||||
return $this->state([
|
||||
'require_acceptance' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,11 @@ class UserFactory extends Factory
|
||||
return $this->appendPermission(['companies.delete' => '1']);
|
||||
}
|
||||
|
||||
public function editCompanies()
|
||||
{
|
||||
return $this->appendPermission(['companies.edit' => '1']);
|
||||
}
|
||||
|
||||
public function viewUsers()
|
||||
{
|
||||
return $this->appendPermission(['users.view' => '1']);
|
||||
@@ -380,6 +385,12 @@ class UserFactory extends Factory
|
||||
return $this->appendPermission(['suppliers.delete' => '1']);
|
||||
}
|
||||
|
||||
public function auditAssets()
|
||||
{
|
||||
return $this->appendPermission(['assets.audit' => '1']);
|
||||
}
|
||||
|
||||
|
||||
private function appendPermission(array $permission)
|
||||
{
|
||||
return $this->state(function ($currentState) use ($permission) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->char('acceptance_pdf_logo')->after('label_logo')->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropColumn('acceptance_pdf_logo');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('custom_fields', function (Blueprint $table) {
|
||||
$table->boolean('display_audit')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('custom_fields', function (Blueprint $table) {
|
||||
$table->dropColumn('display_audit');
|
||||
});
|
||||
}
|
||||
};
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -6,7 +6,7 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"acorn": "^8.12.0",
|
||||
"acorn": "^8.14.1",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
"ajv": "^6.12.6",
|
||||
@@ -15,7 +15,7 @@
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-datepicker": "^1.10.0",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
"bootstrap-table": "1.24.0",
|
||||
"bootstrap-table": "1.24.1",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"chart.js": "^2.9.4",
|
||||
"clipboard": "^2.0.11",
|
||||
@@ -2502,9 +2502,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3702,9 +3702,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bootstrap-table": {
|
||||
"version": "1.24.0",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.24.0.tgz",
|
||||
"integrity": "sha512-dyRf5PQwTgFHj9yjuPXa+GIf4JpuQhsgD1CJrOqhw40qI2gTb3mJfRdoBc7iF2bqzOl+k0RnbAlhSPbGe4VS+w==",
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.24.1.tgz",
|
||||
"integrity": "sha512-que7o2Z6R0I3cfwfm6qg4XnoUK9A/8162HUErFYg3fGtzjk5OjLJx6Ji9p3oKz+IAIVvBCr9sqMqujEG47k3zA==",
|
||||
"peerDependencies": {
|
||||
"jquery": "3"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"acorn": "^8.12.0",
|
||||
"acorn": "^8.14.1",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
"ajv": "^6.12.6",
|
||||
@@ -35,7 +35,7 @@
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-datepicker": "^1.10.0",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
"bootstrap-table": "1.24.0",
|
||||
"bootstrap-table": "1.24.1",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"chart.js": "^2.9.4",
|
||||
"clipboard": "^2.0.11",
|
||||
|
||||
@@ -1175,7 +1175,8 @@ th.css-history > .th-inner::before {
|
||||
padding: 6px 12px;
|
||||
height: 34px;
|
||||
}
|
||||
.form-group.has-error label {
|
||||
.form-group.has-error label,
|
||||
.form-group.has-error .help-block {
|
||||
color: #a94442;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
@@ -1418,4 +1419,20 @@ input[type="radio"]:checked::before {
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: table-row !important;
|
||||
}
|
||||
td.text-right.text-padding-number-cell {
|
||||
padding-right: 30px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
th.text-right.text-padding-number-footer-cell {
|
||||
padding-right: 20px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
code.single-line {
|
||||
white-space: pre-wrap;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
||||
@@ -806,7 +806,8 @@ th.css-history > .th-inner::before {
|
||||
padding: 6px 12px;
|
||||
height: 34px;
|
||||
}
|
||||
.form-group.has-error label {
|
||||
.form-group.has-error label,
|
||||
.form-group.has-error .help-block {
|
||||
color: #a94442;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
@@ -1049,4 +1050,20 @@ input[type="radio"]:checked::before {
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: table-row !important;
|
||||
}
|
||||
td.text-right.text-padding-number-cell {
|
||||
padding-right: 30px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
th.text-right.text-padding-number-footer-cell {
|
||||
padding-right: 20px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
code.single-line {
|
||||
white-space: pre-wrap;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
||||
40
public/css/dist/all.css
vendored
40
public/css/dist/all.css
vendored
@@ -21008,7 +21008,7 @@ hr {
|
||||
@charset "UTF-8";
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* version: 1.24.0
|
||||
* version: 1.24.1
|
||||
* https://github.com/wenzhixin/bootstrap-table/
|
||||
*/
|
||||
/* stylelint-disable annotation-no-unknown, max-line-length */
|
||||
@@ -22510,7 +22510,8 @@ th.css-history > .th-inner::before {
|
||||
padding: 6px 12px;
|
||||
height: 34px;
|
||||
}
|
||||
.form-group.has-error label {
|
||||
.form-group.has-error label,
|
||||
.form-group.has-error .help-block {
|
||||
color: #a94442;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
@@ -22753,6 +22754,22 @@ input[type="radio"]:checked::before {
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: table-row !important;
|
||||
}
|
||||
td.text-right.text-padding-number-cell {
|
||||
padding-right: 30px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
th.text-right.text-padding-number-footer-cell {
|
||||
padding-right: 20px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
code.single-line {
|
||||
white-space: pre-wrap;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
||||
.select2-container {
|
||||
@@ -24045,7 +24062,8 @@ th.css-history > .th-inner::before {
|
||||
padding: 6px 12px;
|
||||
height: 34px;
|
||||
}
|
||||
.form-group.has-error label {
|
||||
.form-group.has-error label,
|
||||
.form-group.has-error .help-block {
|
||||
color: #a94442;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
@@ -24288,4 +24306,20 @@ input[type="radio"]:checked::before {
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: table-row !important;
|
||||
}
|
||||
td.text-right.text-padding-number-cell {
|
||||
padding-right: 30px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
th.text-right.text-padding-number-footer-cell {
|
||||
padding-right: 20px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
code.single-line {
|
||||
white-space: pre-wrap;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
||||
2
public/css/dist/bootstrap-table.css
vendored
2
public/css/dist/bootstrap-table.css
vendored
@@ -1,7 +1,7 @@
|
||||
@charset "UTF-8";
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* version: 1.24.0
|
||||
* version: 1.24.1
|
||||
* https://github.com/wenzhixin/bootstrap-table/
|
||||
*/
|
||||
/* stylelint-disable annotation-no-unknown, max-line-length */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1712
public/js/dist/all.js
vendored
1712
public/js/dist/all.js
vendored
File diff suppressed because it is too large
Load Diff
2
public/js/dist/bootstrap-table-en-US.min.js
vendored
2
public/js/dist/bootstrap-table-en-US.min.js
vendored
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
||||
*
|
||||
* @version v1.24.0
|
||||
* @version v1.24.1
|
||||
* @homepage https://bootstrap-table.com
|
||||
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||
* @license MIT
|
||||
|
||||
File diff suppressed because one or more lines are too long
262
public/js/dist/bootstrap-table.js
vendored
262
public/js/dist/bootstrap-table.js
vendored
@@ -6911,6 +6911,12 @@
|
||||
}
|
||||
return bootstrapVersion;
|
||||
},
|
||||
/**
|
||||
* Returns the prefix for the icons based on the theme.
|
||||
*
|
||||
* @param {string} theme - The theme name (bootstrap3, bootstrap4, bootstrap5, bootstrap-table, bulma, foundation, materialize, semantic).
|
||||
* @returns {string} The icons prefix.
|
||||
*/
|
||||
getIconsPrefix: function getIconsPrefix(theme) {
|
||||
return {
|
||||
bootstrap3: 'glyphicon',
|
||||
@@ -6923,75 +6929,28 @@
|
||||
semantic: 'fa'
|
||||
}[theme] || 'fa';
|
||||
},
|
||||
getIcons: function getIcons(prefix) {
|
||||
return {
|
||||
glyphicon: {
|
||||
clearSearch: 'glyphicon-trash',
|
||||
columns: 'glyphicon-th icon-th',
|
||||
detailClose: 'glyphicon-minus icon-minus',
|
||||
detailOpen: 'glyphicon-plus icon-plus',
|
||||
fullscreen: 'glyphicon-fullscreen',
|
||||
paginationSwitchDown: 'glyphicon-collapse-down icon-chevron-down',
|
||||
paginationSwitchUp: 'glyphicon-collapse-up icon-chevron-up',
|
||||
refresh: 'glyphicon-refresh icon-refresh',
|
||||
search: 'glyphicon-search',
|
||||
toggleOff: 'glyphicon-list-alt icon-list-alt',
|
||||
toggleOn: 'glyphicon-list-alt icon-list-alt'
|
||||
},
|
||||
fa: {
|
||||
clearSearch: 'fa-trash',
|
||||
columns: 'fa-th-list',
|
||||
detailClose: 'fa-minus',
|
||||
detailOpen: 'fa-plus',
|
||||
fullscreen: 'fa-arrows-alt',
|
||||
paginationSwitchDown: 'fa-caret-square-down',
|
||||
paginationSwitchUp: 'fa-caret-square-up',
|
||||
refresh: 'fa-sync',
|
||||
search: 'fa-search',
|
||||
toggleOff: 'fa-toggle-off',
|
||||
toggleOn: 'fa-toggle-on'
|
||||
},
|
||||
bi: {
|
||||
clearSearch: 'bi-trash',
|
||||
columns: 'bi-list-ul',
|
||||
detailClose: 'bi-dash',
|
||||
detailOpen: 'bi-plus',
|
||||
fullscreen: 'bi-arrows-move',
|
||||
paginationSwitchDown: 'bi-caret-down-square',
|
||||
paginationSwitchUp: 'bi-caret-up-square',
|
||||
refresh: 'bi-arrow-clockwise',
|
||||
search: 'bi-search',
|
||||
toggleOff: 'bi-toggle-off',
|
||||
toggleOn: 'bi-toggle-on'
|
||||
},
|
||||
icon: {
|
||||
clearSearch: 'icon-trash-2',
|
||||
columns: 'icon-list',
|
||||
detailClose: 'icon-minus',
|
||||
detailOpen: 'icon-plus',
|
||||
fullscreen: 'icon-maximize',
|
||||
paginationSwitchDown: 'icon-arrow-up-circle',
|
||||
paginationSwitchUp: 'icon-arrow-down-circle',
|
||||
refresh: 'icon-refresh-cw',
|
||||
search: 'icon-search',
|
||||
toggleOff: 'icon-toggle-right',
|
||||
toggleOn: 'icon-toggle-right'
|
||||
},
|
||||
'material-icons': {
|
||||
clearSearch: 'delete',
|
||||
columns: 'view_list',
|
||||
detailClose: 'remove',
|
||||
detailOpen: 'add',
|
||||
fullscreen: 'fullscreen',
|
||||
paginationSwitchDown: 'grid_on',
|
||||
paginationSwitchUp: 'grid_off',
|
||||
refresh: 'refresh',
|
||||
search: 'search',
|
||||
sort: 'sort',
|
||||
toggleOff: 'tablet',
|
||||
toggleOn: 'tablet_android'
|
||||
}
|
||||
}[prefix] || {};
|
||||
/**
|
||||
* Gets the icons for a given prefix.
|
||||
*
|
||||
* @param {Object.<string, Object>} icons - The icons object.
|
||||
* @param {string} prefix - The prefix. For example, 'fa', 'bi', etc.
|
||||
* @return {Object} The icons object for the given prefix.
|
||||
*/
|
||||
getIcons: function getIcons(icons, prefix) {
|
||||
return icons[prefix] || {};
|
||||
},
|
||||
/**
|
||||
* Assigns new icons to icons object.
|
||||
*
|
||||
* @param {Object.<string, Object>} icons - The icons object.
|
||||
* @param {string} icon - The icon name. For example, 'search', 'refresh', etc.
|
||||
* @param {Object.<string, string>} values - The values object.
|
||||
*/
|
||||
assignIcons: function assignIcons(icons, icon, values) {
|
||||
for (var _i = 0, _Object$keys = Object.keys(icons); _i < _Object$keys.length; _i++) {
|
||||
var key = _Object$keys[_i];
|
||||
icons[key][icon] = values[key];
|
||||
}
|
||||
},
|
||||
getSearchInput: function getSearchInput(that) {
|
||||
if (typeof that.options.searchSelector === 'string') {
|
||||
@@ -7145,15 +7104,15 @@
|
||||
flag[i][j] = false;
|
||||
}
|
||||
}
|
||||
for (var _i = 0; _i < columns.length; _i++) {
|
||||
var _iterator3 = _createForOfIteratorHelper(columns[_i]),
|
||||
for (var _i2 = 0; _i2 < columns.length; _i2++) {
|
||||
var _iterator3 = _createForOfIteratorHelper(columns[_i2]),
|
||||
_step3;
|
||||
try {
|
||||
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
||||
var r = _step3.value;
|
||||
var rowspan = +r.rowspan || 1;
|
||||
var colspan = +r.colspan || 1;
|
||||
var index = flag[_i].indexOf(false);
|
||||
var index = flag[_i2].indexOf(false);
|
||||
r.colspanIndex = index;
|
||||
if (colspan === 1) {
|
||||
r.fieldIndex = index;
|
||||
@@ -7166,7 +7125,7 @@
|
||||
}
|
||||
for (var _j = 0; _j < rowspan; _j++) {
|
||||
for (var k = 0; k < colspan; k++) {
|
||||
flag[_i + _j][index + k] = true;
|
||||
flag[_i2 + _j][index + k] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7322,8 +7281,8 @@
|
||||
if (compareLength && aKeys.length !== bKeys.length) {
|
||||
return false;
|
||||
}
|
||||
for (var _i2 = 0, _aKeys = aKeys; _i2 < _aKeys.length; _i2++) {
|
||||
var key = _aKeys[_i2];
|
||||
for (var _i3 = 0, _aKeys = aKeys; _i3 < _aKeys.length; _i3++) {
|
||||
var key = _aKeys[_i3];
|
||||
if (bKeys.includes(key) && objectA[key] !== objectB[key]) {
|
||||
return false;
|
||||
}
|
||||
@@ -7364,8 +7323,8 @@
|
||||
return text.toString().replace(/(<([^>]+)>)/ig, '').replace(/&[#A-Za-z0-9]+;/gi, '').trim();
|
||||
},
|
||||
getRealDataAttr: function getRealDataAttr(dataAttr) {
|
||||
for (var _i3 = 0, _Object$entries = Object.entries(dataAttr); _i3 < _Object$entries.length; _i3++) {
|
||||
var _Object$entries$_i = _slicedToArray(_Object$entries[_i3], 2),
|
||||
for (var _i4 = 0, _Object$entries = Object.entries(dataAttr); _i4 < _Object$entries.length; _i4++) {
|
||||
var _Object$entries$_i = _slicedToArray(_Object$entries[_i4], 2),
|
||||
attr = _Object$entries$_i[0],
|
||||
value = _Object$entries$_i[1];
|
||||
var auxAttr = attr.split(/(?=[A-Z])/).join('-').toLowerCase();
|
||||
@@ -7534,8 +7493,8 @@
|
||||
try {
|
||||
for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
|
||||
var row = _step11.value;
|
||||
for (var _i4 = 0, _Object$keys = Object.keys(row); _i4 < _Object$keys.length; _i4++) {
|
||||
var key = _Object$keys[_i4];
|
||||
for (var _i5 = 0, _Object$keys2 = Object.keys(row); _i5 < _Object$keys2.length; _i5++) {
|
||||
var key = _Object$keys2[_i5];
|
||||
if (key.startsWith('_') && (key.endsWith('_rowspan') || key.endsWith('_colspan'))) {
|
||||
return true;
|
||||
}
|
||||
@@ -7679,8 +7638,8 @@
|
||||
_iterator13.f();
|
||||
}
|
||||
} else if (_typeof(style) === 'object') {
|
||||
for (var _i5 = 0, _Object$entries2 = Object.entries(style); _i5 < _Object$entries2.length; _i5++) {
|
||||
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i5], 2),
|
||||
for (var _i6 = 0, _Object$entries2 = Object.entries(style); _i6 < _Object$entries2.length; _i6++) {
|
||||
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i6], 2),
|
||||
k = _Object$entries2$_i[0],
|
||||
v = _Object$entries2$_i[1];
|
||||
dom.style.setProperty(k, v);
|
||||
@@ -7697,8 +7656,8 @@
|
||||
if (el.tagName === 'A') {
|
||||
el.href = 'javascript:';
|
||||
}
|
||||
for (var _i6 = 0, _Object$entries3 = Object.entries(_attrs); _i6 < _Object$entries3.length; _i6++) {
|
||||
var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i6], 2),
|
||||
for (var _i7 = 0, _Object$entries3 = Object.entries(_attrs); _i7 < _Object$entries3.length; _i7++) {
|
||||
var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i7], 2),
|
||||
k = _Object$entries3$_i[0],
|
||||
v = _Object$entries3$_i[1];
|
||||
if (v === undefined) {
|
||||
@@ -7756,8 +7715,8 @@
|
||||
baseUrl = _hashArray$0$split2[0],
|
||||
search = _hashArray$0$split2[1];
|
||||
var urlParams = new URLSearchParams(search);
|
||||
for (var _i7 = 0, _Object$entries4 = Object.entries(query); _i7 < _Object$entries4.length; _i7++) {
|
||||
var _Object$entries4$_i = _slicedToArray(_Object$entries4[_i7], 2),
|
||||
for (var _i8 = 0, _Object$entries4 = Object.entries(query); _i8 < _Object$entries4.length; _i8++) {
|
||||
var _Object$entries4$_i = _slicedToArray(_Object$entries4[_i8], 2),
|
||||
key = _Object$entries4$_i[0],
|
||||
value = _Object$entries4$_i[1];
|
||||
urlParams.set(key, value);
|
||||
@@ -7766,7 +7725,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
var VERSION = '1.24.0';
|
||||
var VERSION = '1.24.1';
|
||||
var bootstrapVersion = Utils.getBootstrapVersion();
|
||||
var CONSTANTS = {
|
||||
3: {
|
||||
@@ -7870,6 +7829,74 @@
|
||||
}
|
||||
}
|
||||
}[bootstrapVersion];
|
||||
var ICONS = {
|
||||
glyphicon: {
|
||||
clearSearch: 'glyphicon-trash',
|
||||
columns: 'glyphicon-th icon-th',
|
||||
detailClose: 'glyphicon-minus icon-minus',
|
||||
detailOpen: 'glyphicon-plus icon-plus',
|
||||
fullscreen: 'glyphicon-fullscreen',
|
||||
paginationSwitchDown: 'glyphicon-collapse-down icon-chevron-down',
|
||||
paginationSwitchUp: 'glyphicon-collapse-up icon-chevron-up',
|
||||
refresh: 'glyphicon-refresh icon-refresh',
|
||||
search: 'glyphicon-search',
|
||||
toggleOff: 'glyphicon-list-alt icon-list-alt',
|
||||
toggleOn: 'glyphicon-list-alt icon-list-alt'
|
||||
},
|
||||
fa: {
|
||||
clearSearch: 'fa-trash',
|
||||
columns: 'fa-th-list',
|
||||
detailClose: 'fa-minus',
|
||||
detailOpen: 'fa-plus',
|
||||
fullscreen: 'fa-arrows-alt',
|
||||
paginationSwitchDown: 'fa-caret-square-down',
|
||||
paginationSwitchUp: 'fa-caret-square-up',
|
||||
refresh: 'fa-sync',
|
||||
search: 'fa-search',
|
||||
toggleOff: 'fa-toggle-off',
|
||||
toggleOn: 'fa-toggle-on'
|
||||
},
|
||||
bi: {
|
||||
clearSearch: 'bi-trash',
|
||||
columns: 'bi-list-ul',
|
||||
detailClose: 'bi-dash',
|
||||
detailOpen: 'bi-plus',
|
||||
fullscreen: 'bi-arrows-move',
|
||||
paginationSwitchDown: 'bi-caret-down-square',
|
||||
paginationSwitchUp: 'bi-caret-up-square',
|
||||
refresh: 'bi-arrow-clockwise',
|
||||
search: 'bi-search',
|
||||
toggleOff: 'bi-toggle-off',
|
||||
toggleOn: 'bi-toggle-on'
|
||||
},
|
||||
icon: {
|
||||
clearSearch: 'icon-trash-2',
|
||||
columns: 'icon-list',
|
||||
detailClose: 'icon-minus',
|
||||
detailOpen: 'icon-plus',
|
||||
fullscreen: 'icon-maximize',
|
||||
paginationSwitchDown: 'icon-arrow-up-circle',
|
||||
paginationSwitchUp: 'icon-arrow-down-circle',
|
||||
refresh: 'icon-refresh-cw',
|
||||
search: 'icon-search',
|
||||
toggleOff: 'icon-toggle-right',
|
||||
toggleOn: 'icon-toggle-right'
|
||||
},
|
||||
'material-icons': {
|
||||
clearSearch: 'delete',
|
||||
columns: 'view_list',
|
||||
detailClose: 'remove',
|
||||
detailOpen: 'add',
|
||||
fullscreen: 'fullscreen',
|
||||
paginationSwitchDown: 'grid_on',
|
||||
paginationSwitchUp: 'grid_off',
|
||||
refresh: 'refresh',
|
||||
search: 'search',
|
||||
sort: 'sort',
|
||||
toggleOff: 'tablet',
|
||||
toggleOn: 'tablet_android'
|
||||
}
|
||||
};
|
||||
var DEFAULTS = {
|
||||
ajax: undefined,
|
||||
ajaxOptions: {},
|
||||
@@ -8282,6 +8309,7 @@
|
||||
CONSTANTS: CONSTANTS,
|
||||
DEFAULTS: DEFAULTS,
|
||||
EVENTS: EVENTS,
|
||||
ICONS: ICONS,
|
||||
LOCALES: {
|
||||
en: EN,
|
||||
'en-US': EN
|
||||
@@ -8462,7 +8490,7 @@
|
||||
opts.icons = Utils.calculateObjectValue(null, opts.icons);
|
||||
}
|
||||
opts.iconsPrefix = opts.iconsPrefix || $.fn.bootstrapTable.defaults.iconsPrefix || iconsPrefix;
|
||||
opts.icons = Object.assign(Utils.getIcons(opts.iconsPrefix), $.fn.bootstrapTable.defaults.icons, opts.icons);
|
||||
opts.icons = Object.assign(Utils.getIcons(Constants.ICONS, opts.iconsPrefix), $.fn.bootstrapTable.defaults.icons, opts.icons);
|
||||
|
||||
// init buttons class
|
||||
var buttonsPrefix = opts.buttonsPrefix ? "".concat(opts.buttonsPrefix, "-") : '';
|
||||
@@ -9461,7 +9489,10 @@
|
||||
html.push("<div class=\"".concat(this.constants.classes.pull, "-").concat(opts.paginationDetailHAlign, " pagination-detail\">"));
|
||||
}
|
||||
if (this.paginationParts.includes('pageInfo') || this.paginationParts.includes('pageInfoShort')) {
|
||||
var totalRows = this.options.totalRows + (this.options.sidePagination === 'client' && this.options.paginationLoadMore && !this._paginationLoaded && this.totalPages > 1 ? ' +' : '');
|
||||
var totalRows = this.options.totalRows;
|
||||
if (this.options.sidePagination === 'client' && this.options.paginationLoadMore && !this._paginationLoaded && this.totalPages > 1) {
|
||||
totalRows += ' +';
|
||||
}
|
||||
var paginationInfo = this.paginationParts.includes('pageInfoShort') ? opts.formatDetailPagination(totalRows) : opts.formatShowingRows(this.pageFrom, this.pageTo, totalRows, opts.totalNotFiltered);
|
||||
html.push("<span class=\"pagination-info\">\n ".concat(paginationInfo, "\n </span>"));
|
||||
}
|
||||
@@ -11251,7 +11282,7 @@
|
||||
}, {
|
||||
key: "filterBy",
|
||||
value: function filterBy(columns, options) {
|
||||
this.filterOptions = Utils.isEmptyObject(options) ? this.options.filterOptions : Utils.extend(this.options.filterOptions, options);
|
||||
this.filterOptions = Utils.isEmptyObject(options) ? this.options.filterOptions : Utils.extend({}, this.options.filterOptions, options);
|
||||
this.filterColumns = Utils.isEmptyObject(columns) ? {} : columns;
|
||||
this.options.pageNumber = 1;
|
||||
this.initSearch();
|
||||
@@ -11470,6 +11501,7 @@
|
||||
$.fn.bootstrapTable.Constructor = BootstrapTable;
|
||||
$.fn.bootstrapTable.theme = Constants.THEME;
|
||||
$.fn.bootstrapTable.VERSION = Constants.VERSION;
|
||||
$.fn.bootstrapTable.icons = Constants.ICONS;
|
||||
$.fn.bootstrapTable.defaults = BootstrapTable.DEFAULTS;
|
||||
$.fn.bootstrapTable.columnDefaults = BootstrapTable.COLUMN_DEFAULTS;
|
||||
$.fn.bootstrapTable.events = BootstrapTable.EVENTS;
|
||||
@@ -17521,13 +17553,12 @@
|
||||
forceExport: false,
|
||||
forceHide: false
|
||||
});
|
||||
Object.assign($.fn.bootstrapTable.defaults.icons, {
|
||||
export: {
|
||||
bootstrap3: 'glyphicon-export icon-share',
|
||||
bootstrap5: 'bi-download',
|
||||
materialize: 'file_download',
|
||||
'bootstrap-table': 'icon-download'
|
||||
}[$.fn.bootstrapTable.theme] || 'fa-download'
|
||||
Utils.assignIcons($.fn.bootstrapTable.icons, 'export', {
|
||||
glyphicon: 'glyphicon-export icon-share',
|
||||
fa: 'fa-download',
|
||||
bi: 'bi-download',
|
||||
icon: 'icon-download',
|
||||
'material-icons': 'file_download'
|
||||
});
|
||||
Object.assign($.fn.bootstrapTable.locales, {
|
||||
formatExport: function formatExport() {
|
||||
@@ -22124,11 +22155,12 @@
|
||||
}, {
|
||||
key: "configureStorage",
|
||||
value: function configureStorage() {
|
||||
var _this2 = this;
|
||||
this._storage = {};
|
||||
switch (this.options.cookieStorage) {
|
||||
case 'cookieStorage':
|
||||
this._storage.setItem = function (cookieName, cookieValue) {
|
||||
document.cookie = [cookieName, '=', encodeURIComponent(cookieValue), "; expires=".concat(UtilsCookie.calculateExpiration(this.options.cookieExpire)), this.options.cookiePath ? "; path=".concat(this.options.cookiePath) : '', this.options.cookieDomain ? "; domain=".concat(this.options.cookieDomain) : '', this.options.cookieSecure ? '; secure' : '', ";SameSite=".concat(this.options.cookieSameSite)].join('');
|
||||
document.cookie = [cookieName, '=', encodeURIComponent(cookieValue), "; expires=".concat(UtilsCookie.calculateExpiration(_this2.options.cookieExpire)), _this2.options.cookiePath ? "; path=".concat(_this2.options.cookiePath) : '', _this2.options.cookieDomain ? "; domain=".concat(_this2.options.cookieDomain) : '', _this2.options.cookieSecure ? '; secure' : '', ";SameSite=".concat(_this2.options.cookieSameSite)].join('');
|
||||
};
|
||||
this._storage.getItem = function (cookieName) {
|
||||
var value = "; ".concat(document.cookie);
|
||||
@@ -22136,7 +22168,7 @@
|
||||
return parts.length === 2 ? decodeURIComponent(parts.pop().split(';').shift()) : null;
|
||||
};
|
||||
this._storage.removeItem = function (cookieName) {
|
||||
document.cookie = [encodeURIComponent(cookieName), '=', '; expires=Thu, 01 Jan 1970 00:00:00 GMT', this.options.cookiePath ? "; path=".concat(this.options.cookiePath) : '', this.options.cookieDomain ? "; domain=".concat(this.options.cookieDomain) : '', ";SameSite=".concat(this.options.cookieSameSite)].join('');
|
||||
document.cookie = [encodeURIComponent(cookieName), '=', '; expires=Thu, 01 Jan 1970 00:00:00 GMT', _this2.options.cookiePath ? "; path=".concat(_this2.options.cookiePath) : '', _this2.options.cookieDomain ? "; domain=".concat(_this2.options.cookieDomain) : '', ";SameSite=".concat(_this2.options.cookieSameSite)].join('');
|
||||
};
|
||||
break;
|
||||
case 'localStorage':
|
||||
@@ -22166,13 +22198,13 @@
|
||||
throw new Error('The following options must be set while using the customStorage: cookieCustomStorageSet, cookieCustomStorageGet and cookieCustomStorageDelete');
|
||||
}
|
||||
this._storage.setItem = function (cookieName, cookieValue) {
|
||||
Utils.calculateObjectValue(this.options, this.options.cookieCustomStorageSet, [cookieName, cookieValue], '');
|
||||
Utils.calculateObjectValue(_this2.options, _this2.options.cookieCustomStorageSet, [cookieName, cookieValue], '');
|
||||
};
|
||||
this._storage.getItem = function (cookieName) {
|
||||
return Utils.calculateObjectValue(this.options, this.options.cookieCustomStorageGet, [cookieName], '');
|
||||
return Utils.calculateObjectValue(_this2.options, _this2.options.cookieCustomStorageGet, [cookieName], '');
|
||||
};
|
||||
this._storage.removeItem = function (cookieName) {
|
||||
Utils.calculateObjectValue(this.options, this.options.cookieCustomStorageDelete, [cookieName], '');
|
||||
Utils.calculateObjectValue(_this2.options, _this2.options.cookieCustomStorageDelete, [cookieName], '');
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@@ -35674,27 +35706,18 @@ if(xr(e,"index.xml"))throw new Error("Unsupported NUMBERS 09 file");throw new Er
|
||||
var Utils = $.fn.bootstrapTable.utils;
|
||||
var theme = {
|
||||
bootstrap3: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'glyphicon-chevron-down'
|
||||
},
|
||||
classes: {},
|
||||
html: {
|
||||
modal: "\n <div id=\"avdSearchModal_%s\" class=\"modal fade\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n <div class=\"modal-dialog modal-xs\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <button class=\"close toolbar-modal-close\" data-dismiss=\"modal\" aria-label=\"Close\">\n <span aria-hidden=\"true\">×</span>\n </button>\n <h4 class=\"modal-title toolbar-modal-title\"></h4>\n </div>\n <div class=\"modal-body toolbar-modal-body\"></div>\n <div class=\"modal-footer toolbar-modal-footer\">\n <button class=\"btn btn-%s toolbar-modal-close\"></button>\n </div>\n </div>\n </div>\n </div>\n "
|
||||
}
|
||||
},
|
||||
bootstrap4: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
classes: {},
|
||||
html: {
|
||||
modal: "\n <div id=\"avdSearchModal_%s\" class=\"modal fade\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n <div class=\"modal-dialog modal-xs\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h4 class=\"modal-title toolbar-modal-title\"></h4>\n <button class=\"close toolbar-modal-close\" data-dismiss=\"modal\" aria-label=\"Close\">\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n <div class=\"modal-body toolbar-modal-body\"></div>\n <div class=\"modal-footer toolbar-modal-footer\">\n <button class=\"btn btn-%s toolbar-modal-close\"></button>\n </div>\n </div>\n </div>\n </div>\n "
|
||||
}
|
||||
},
|
||||
bootstrap5: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'bi-chevron-down'
|
||||
},
|
||||
classes: {
|
||||
formGroup: 'mb-3'
|
||||
},
|
||||
@@ -35703,36 +35726,24 @@ if(xr(e,"index.xml"))throw new Error("Unsupported NUMBERS 09 file");throw new Er
|
||||
}
|
||||
},
|
||||
bulma: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
classes: {},
|
||||
html: {
|
||||
modal: "\n <div class=\"modal\" id=\"avdSearchModal_%s\">\n <div class=\"modal-background\"></div>\n <div class=\"modal-card\">\n <header class=\"modal-card-head\">\n <p class=\"modal-card-title toolbar-modal-title\"></p>\n <button class=\"delete toolbar-modal-close\"></button>\n </header>\n <section class=\"modal-card-body toolbar-modal-body\"></section>\n <footer class=\"modal-card-foot toolbar-modal-footer\">\n <button class=\"button button-%s toolbar-modal-close\"></button>\n </footer>\n </div>\n </div>\n "
|
||||
}
|
||||
},
|
||||
foundation: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
classes: {},
|
||||
html: {
|
||||
modal: "\n <div class=\"reveal\" id=\"avdSearchModal_%s\" data-reveal>\n <h1 class=\"toolbar-modal-title\"></h1>\n <div class=\"toolbar-modal-body\"></div>\n <button class=\"close-button toolbar-modal-close\" data-close aria-label=\"Close modal\">\n <span aria-hidden=\"true\">×</span>\n </button>\n <div class=\"toolbar-modal-footer\">\n <button class=\"button button-%s toolbar-modal-close\"></button>\n </div>\n </div>\n "
|
||||
}
|
||||
},
|
||||
materialize: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'expand_more'
|
||||
},
|
||||
classes: {},
|
||||
html: {
|
||||
modal: "\n <div id=\"avdSearchModal_%s\" class=\"modal\">\n <div class=\"modal-content\">\n <h4 class=\"toolbar-modal-title\"></h4>\n <div class=\"toolbar-modal-body\"></div>\n </div>\n <div class=\"modal-footer toolbar-modal-footer\">\n <a href=\"javascript:void(0)\" class=\"modal-close waves-effect waves-green btn-flat btn-%s toolbar-modal-close\"></a>\n </div>\n </div>\n "
|
||||
}
|
||||
},
|
||||
semantic: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
classes: {},
|
||||
html: {
|
||||
modal: "\n <div class=\"ui modal\" id=\"avdSearchModal_%s\">\n <i class=\"close icon toolbar-modal-close\"></i>\n <div class=\"header toolbar-modal-title\"\"></div>\n <div class=\"image content ui form toolbar-modal-body\"></div>\n <div class=\"actions toolbar-modal-footer\">\n <div class=\"ui black deny button button-%s toolbar-modal-close\"></div>\n </div>\n </div>\n "
|
||||
@@ -35749,8 +35760,11 @@ if(xr(e,"index.xml"))throw new Error("Unsupported NUMBERS 09 file");throw new Er
|
||||
return false;
|
||||
}
|
||||
});
|
||||
Object.assign($.fn.bootstrapTable.defaults.icons, {
|
||||
advancedSearchIcon: theme.icons.advancedSearchIcon
|
||||
Utils.assignIcons($.fn.bootstrapTable.icons, 'advancedSearchIcon', {
|
||||
glyphicon: 'glyphicon-chevron-down',
|
||||
fa: 'fa-chevron-down',
|
||||
bi: 'bi-chevron-down',
|
||||
'material-icons': 'expand_more'
|
||||
});
|
||||
Object.assign($.fn.bootstrapTable.events, {
|
||||
'column-advanced-search.bs.table': 'onColumnAdvancedSearch'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=65d7af7b9fa7fd0e05737526a0d1d282",
|
||||
"/js/build/app.js": "/js/build/app.js?id=607de09b70b83ef82a427e4b36341682",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=06c13e817cc022028b3f4a33c0ca303a",
|
||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=79aa889a1a6691013be6c342ca7391cd",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=6528155ed5ed8fddf4047de7f0d0298d",
|
||||
"/css/build/app.css": "/css/build/app.css?id=3422f2ca2056b952c3c361adf00c10b8",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=4d62149a0ee9dc139bdf03ff2f83930d",
|
||||
"/css/build/app.css": "/css/build/app.css?id=d47ce0dc14671bb4e462e111001488e5",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=4ea0068716c1bb2434d87a16d51b98c9",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=ea22079836a432d7f46a5d390c445e13",
|
||||
@@ -19,7 +19,7 @@
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=a82b065847bf3cd5d713c04ee8dc86c6",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=6ea836d8126de101081c49abbdb89417",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=18ebb9c284b49dcf6c8e4fdb923ad923",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=7c861c2086473c513fe26c21e3c4d433",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
||||
@@ -90,8 +90,8 @@
|
||||
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=109ad919b74a62a8a223361da1651bbc",
|
||||
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=53c2e50ef821f7b8dd514611d5e0772c",
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=331c85bd61ffa93af09273d1bc2add5a",
|
||||
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=02dfc50d5b951dc6d260bd508968d319",
|
||||
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=1c350eabf064c309f67d6779e5cc4afa",
|
||||
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=5e93ef0a1889bed3f92a705dc1e92c9b",
|
||||
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=c0f21fb7e62d6f0a0153f1cdbf26782a",
|
||||
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=79aa889a1a6691013be6c342ca7391cd",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=06c13e817cc022028b3f4a33c0ca303a",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=76482123f6c70e866d6b971ba91de7bb",
|
||||
@@ -108,8 +108,8 @@
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=44bf834f2110504a793dadec132a5898",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=ea22079836a432d7f46a5d390c445e13",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=7b315b9612b8fde8f9c5b0ddb6bba690",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=ceded08e0cc745a83c13647035b03406",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=54d676a6ea8677dd48f6c4b3041292cf",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=89dffa552c6e3abe3a2aac6c9c7b466b",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=61285c8ac5ea7b46002ea8c451c94e60",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=21e041dec60e0785db6d64961b13a9b0"
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=757648759dcd365f5708e95b985971ff",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=cae553daff19b328b3ba51a62f891442"
|
||||
}
|
||||
|
||||
@@ -886,7 +886,7 @@ th.css-history > .th-inner::before {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.form-group.has-error label {
|
||||
.form-group.has-error label, .form-group.has-error .help-block {
|
||||
color: #a94442;
|
||||
}
|
||||
|
||||
@@ -1163,4 +1163,23 @@ input[type="radio"]:checked::before {
|
||||
/** this is needed to override ekko-lightboxes card view styles **/
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: table-row !important;
|
||||
}
|
||||
}
|
||||
|
||||
td.text-right.text-padding-number-cell {
|
||||
padding-right: 30px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th.text-right.text-padding-number-footer-cell {
|
||||
padding-right: 20px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
code.single-line {
|
||||
white-space: pre-wrap;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
@@ -59,5 +59,6 @@ return [
|
||||
'encrypted_options' => 'This field is encrypted, so some display options will not be available.',
|
||||
'display_checkin' => 'Display in checkin forms',
|
||||
'display_checkout' => 'Display in checkout forms',
|
||||
'display_audit' => 'Display in audit forms',
|
||||
|
||||
];
|
||||
|
||||
@@ -100,9 +100,10 @@ return [
|
||||
],
|
||||
|
||||
'requests' => [
|
||||
'error' => 'Asset was not requested, please try again',
|
||||
'success' => 'Asset requested successfully.',
|
||||
'canceled' => 'Checkout request successfully canceled',
|
||||
'error' => 'Request was not successful, please try again.',
|
||||
'success' => 'Request successfully submitted.',
|
||||
'canceled' => 'Request successfully canceled.',
|
||||
'cancel' => 'Cancel this item request',
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -58,10 +58,8 @@ return [
|
||||
'display_eol' => 'Display EOL in table view',
|
||||
'display_qr' => 'Display 2D barcode',
|
||||
'display_alt_barcode' => 'Display 1D barcode',
|
||||
'email_logo' => 'Email Logo',
|
||||
'barcode_type' => '2D Barcode Type',
|
||||
'alt_barcode_type' => '1D barcode type',
|
||||
'email_logo_size' => 'Square logos in email look best. ',
|
||||
'enabled' => 'Enabled',
|
||||
'eula_settings' => 'EULA Settings',
|
||||
'eula_markdown' => 'This EULA allows <a href="https://help.github.com/articles/github-flavored-markdown/">Github flavored markdown</a>.',
|
||||
@@ -77,7 +75,6 @@ return [
|
||||
'google_workspaces' => 'Google Workspaces',
|
||||
'header_color' => 'Header Color',
|
||||
'info' => 'These settings let you customize certain aspects of your installation.',
|
||||
'label_logo' => 'Label Logo',
|
||||
'label_logo_size' => 'Square logos look best - will be displayed in the top right of each asset label. ',
|
||||
'laravel' => 'Laravel Version',
|
||||
'ldap' => 'LDAP',
|
||||
@@ -389,7 +386,7 @@ return [
|
||||
'test_mail' => 'Test Mail',
|
||||
'profile_edit' => 'Edit Profile',
|
||||
'profile_edit_help' => 'Allow users to edit their own profiles.',
|
||||
'default_avatar' => 'Upload custom default avatar',
|
||||
'default_avatar' => 'Custom Default Avatar',
|
||||
'default_avatar_help' => 'This image will be displayed as a profile if a user does not have a profile photo.',
|
||||
'restore_default_avatar' => 'Restore <a href=":default_avatar" data-toggle="lightbox" data-type="image">original system default avatar</a>',
|
||||
'restore_default_avatar_help' => '',
|
||||
@@ -397,8 +394,32 @@ return [
|
||||
'due_checkin_days_help' => 'How many days before the expected checkin of an asset should it be listed in the "Due for checkin" page?',
|
||||
'no_groups' => 'No groups have been created yet. Visit <code>Admin Settings > Permission Groups</code> to add one.',
|
||||
'text' => 'Text',
|
||||
'firstname_lastname_format' => 'First Name Last Name (jane.smith)',
|
||||
'first_name_format' => 'First Name (jane)',
|
||||
'filastname_format' => 'First Initial Last Name (jsmith)',
|
||||
'lastnamefirstinitial_format' => 'Last Name First Initial (smithj)',
|
||||
'firstname_lastname_underscore_format' => 'First Name Last Name (jane_smith)',
|
||||
'firstinitial.lastname' => 'First Initial Last Name (j.smith)',
|
||||
'lastname_firstinitial' => 'Last Name First Initial (smith_j)',
|
||||
'lastname_dot_firstinitial_format' => 'Last Name First Initial (smith.j)',
|
||||
'firstnamelastname' => 'First Name Last Name (janesmith)',
|
||||
'firstnamelastinitial' => 'First Name Last Initial (janes)',
|
||||
'lastnamefirstname' => 'Last Name.First Name (smith.jane)',
|
||||
|
||||
|
||||
|
||||
'logo_labels' => [
|
||||
'acceptance_pdf_logo' => 'PDF Logo',
|
||||
'email_logo' => 'Email Logo',
|
||||
'label_logo' => 'Label Logo',
|
||||
'logo' => 'Site Logo',
|
||||
'favicon' => 'Favicon',
|
||||
],
|
||||
|
||||
'logo_help' => [
|
||||
'email_logo_size' => 'Square logos in email look best. ',
|
||||
],
|
||||
|
||||
'logo_option_types' => [
|
||||
'text' => 'Text',
|
||||
'logo' => 'Logo',
|
||||
|
||||
@@ -15,6 +15,6 @@ return array(
|
||||
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'password' => 'The provided password is incorrect.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :minutes minute(s).',
|
||||
|
||||
);
|
||||
|
||||
@@ -6,8 +6,6 @@ return array(
|
||||
'account_not_found' => 'The username or password is incorrect.',
|
||||
'account_not_activated' => 'This user account is not activated.',
|
||||
'account_suspended' => 'This user account is suspended.',
|
||||
'account_banned' => 'This user account is banned.',
|
||||
'throttle' => 'Too many failed login attempts. Please try again in :minutes minutes.',
|
||||
|
||||
'two_factor' => array(
|
||||
'already_enrolled' => 'Your device is already enrolled.',
|
||||
|
||||
@@ -31,6 +31,7 @@ return [
|
||||
'accept_assets_menu' => 'Accept Assets',
|
||||
'accept_item' => 'Accept Item',
|
||||
'audit' => 'Audit',
|
||||
'audits' => 'Audits',
|
||||
'audit_report' => 'Audit Log',
|
||||
'assets' => 'Assets',
|
||||
'assets_audited' => 'assets audited',
|
||||
@@ -62,6 +63,8 @@ return [
|
||||
'changepassword' => 'Change Password',
|
||||
'checkin' => 'Checkin',
|
||||
'checkin_from' => 'Checkin from',
|
||||
'checkin_note' => 'Checkin Note',
|
||||
'checkout_note' => 'Checkout Note',
|
||||
'checkout' => 'Checkout',
|
||||
'checkouts_count' => 'Checkouts',
|
||||
'checkins_count' => 'Checkins',
|
||||
|
||||
@@ -66,6 +66,8 @@ return [
|
||||
'min_QTY' => 'Min QTY',
|
||||
'name' => 'Name',
|
||||
'new_item_checked' => 'A new item has been checked out under your name, details are below.',
|
||||
'new_item_checked_with_acceptance' => 'A new item has been checked out under your name that requires acceptance, details are below.',
|
||||
'recent_item_checked' => 'An item was recently checked out under your name that requires acceptance, details are below.',
|
||||
'notes' => 'Notes',
|
||||
'password' => 'Password',
|
||||
'password_reset' => 'Password Reset',
|
||||
@@ -88,7 +90,7 @@ return [
|
||||
'upcoming-audits' => 'There is :count asset that is coming up for audit within :threshold days.|There are :count assets that are coming up for audit within :threshold days.',
|
||||
'user' => 'User',
|
||||
'username' => 'Username',
|
||||
'unaccepted_asset_reminder' => 'You have Unaccepted Assets.',
|
||||
'unaccepted_asset_reminder' => 'Reminder: You have Unaccepted Assets.',
|
||||
'welcome' => 'Welcome :name',
|
||||
'welcome_to' => 'Welcome to :web!',
|
||||
'your_assets' => 'View Your Assets',
|
||||
|
||||
@@ -189,7 +189,7 @@ Form::macro('barcode_types', function ($name = 'barcode_type', $selected = null,
|
||||
return $select;
|
||||
});
|
||||
|
||||
Form::macro('username_format', function ($name = 'username_format', $selected = null, $class = null) {
|
||||
Form::macro('email_format', function ($name = 'email_format', $selected = null, $class = null) {
|
||||
$formats = [
|
||||
'firstname.lastname' => trans('general.firstname_lastname_format'),
|
||||
'firstname' => trans('general.first_name_format'),
|
||||
@@ -214,6 +214,31 @@ Form::macro('username_format', function ($name = 'username_format', $selected =
|
||||
return $select;
|
||||
});
|
||||
|
||||
Form::macro('username_format', function ($name = 'username_format', $selected = null, $class = null) {
|
||||
$formats = [
|
||||
'firstname.lastname' => trans('admin/settings/general.firstname_lastname_format'),
|
||||
'firstname' => trans('admin/settings/general.first_name_format'),
|
||||
'filastname' => trans('admin/settings/general.filastname_format'),
|
||||
'lastnamefirstinitial' => trans('admin/settings/general.lastnamefirstinitial_format'),
|
||||
'firstname_lastname' => trans('admin/settings/general.firstname_lastname_underscore_format'),
|
||||
'firstinitial.lastname' => trans('admin/settings/general.firstinitial.lastname'),
|
||||
'lastname_firstinitial' => trans('admin/settings/general.lastname_firstinitial'),
|
||||
'lastname.firstinitial' => trans('admin/settings/general.lastname_dot_firstinitial_format'),
|
||||
'firstnamelastname' => trans('admin/settings/general.firstnamelastname'),
|
||||
'firstnamelastinitial' => trans('admin/settings/general.firstnamelastinitial'),
|
||||
'lastname.firstname' => trans('admin/settings/general.lastnamefirstname'),
|
||||
];
|
||||
|
||||
$select = '<select name="'.$name.'" class="'.$class.'" style="width: 100%" aria-label="'.$name.'">';
|
||||
foreach ($formats as $format => $label) {
|
||||
$select .= '<option value="'.$format.'"'.($selected == $format ? ' selected="selected" role="option" aria-selected="true"' : ' aria-selected="false"').'>'.$label.'</option> '."\n";
|
||||
}
|
||||
|
||||
$select .= '</select>';
|
||||
|
||||
return $select;
|
||||
});
|
||||
|
||||
Form::macro('two_factor_options', function ($name = 'two_factor_enabled', $selected = null, $class = null) {
|
||||
$formats = [
|
||||
'' => trans('admin/settings/general.two_factor_disabled'),
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
data-show-refresh="true"
|
||||
data-show-footer="true"
|
||||
data-sort-order="asc"
|
||||
data-footer-style="footerStyle"
|
||||
id="accessoriesTable"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{route('api.accessories.index') }}"
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
<!-- history tab pane -->
|
||||
<div class="tab-pane fade" id="history">
|
||||
<div class="table table-responsive">
|
||||
<div class="table-responsive">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table
|
||||
@@ -125,7 +125,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-2" data-visible="false" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.record_created') }}</th>
|
||||
<th class="col-sm-2"data-visible="true" data-sortable="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th class="col-sm-2"data-visible="true" data-sortable="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||
<th class="col-sm-2" data-sortable="true" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
|
||||
@@ -554,21 +554,24 @@
|
||||
}'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ trans('general.name') }}</th>
|
||||
<th>{{ trans('admin/licenses/form.license_key') }}</th>
|
||||
<th>{{ trans('admin/licenses/form.to_name') }}</th>
|
||||
<th>{{ trans('admin/licenses/form.to_email') }}</th>
|
||||
<th>{{ trans('general.category') }}</th>
|
||||
<th class="col-md-2">{{ trans('general.name') }}</th>
|
||||
<th class="col-md-4">{{ trans('admin/licenses/form.license_key') }}</th>
|
||||
<th class="col-md-2">{{ trans('admin/licenses/form.to_name') }}</th>
|
||||
<th class="col-md-2">{{ trans('admin/licenses/form.to_email') }}</th>
|
||||
<th class="col-md-2">{{ trans('general.category') }}</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($user->licenses as $license)
|
||||
<tr>
|
||||
<td>{{ $license->name }}</td>
|
||||
<td>
|
||||
{{ $license->name }}
|
||||
</td>
|
||||
<td>
|
||||
@can('viewKeys', $license)
|
||||
{{ $license->serial }}
|
||||
<code class="single-line"><span class="js-copy-link" data-clipboard-target=".js-copy-key-{{ $license->id }}" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}"><span class="js-copy-key-{{ $license->id }}">{{ $license->serial }}</span></span></code>
|
||||
|
||||
@else
|
||||
------------
|
||||
@endcan
|
||||
|
||||
62
resources/views/blade/input/location-select.blade.php
Normal file
62
resources/views/blade/input/location-select.blade.php
Normal file
@@ -0,0 +1,62 @@
|
||||
@use('App\Models\Location', 'Location')
|
||||
@use('Illuminate\Support\Arr', 'Arr')
|
||||
|
||||
@props([
|
||||
'label',
|
||||
'name',
|
||||
'selected',
|
||||
'required' => false,
|
||||
'multiple' => false,
|
||||
'helpText' => null,
|
||||
'hideNewButton' => false,
|
||||
])
|
||||
|
||||
<div
|
||||
@class([
|
||||
'form-group',
|
||||
'has-error' => $errors->has($name),
|
||||
])
|
||||
>
|
||||
|
||||
<label for="{{ $name }}" class="col-md-3 control-label">{{ $label }}</label>
|
||||
<div class="col-md-7">
|
||||
<select
|
||||
class="js-data-ajax"
|
||||
data-endpoint="locations"
|
||||
data-placeholder="{{ trans('general.select_location') }}"
|
||||
name="{{ $name }}"
|
||||
style="width: 100%"
|
||||
id="{{ $name }}_location_select"
|
||||
aria-label="{{ $name }}"
|
||||
@required($required)
|
||||
@if ($multiple)
|
||||
multiple
|
||||
@endif
|
||||
>
|
||||
@if ($selected)
|
||||
@foreach(Arr::wrap($selected) as $id)
|
||||
<option value="{{ $id }}" selected="selected" role="option" aria-selected="true" role="option">
|
||||
{{ optional(Location::find($id))->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
@endif
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 col-sm-1 text-left">
|
||||
@unless($hideNewButton)
|
||||
@can('create', Location::class)
|
||||
<a href='{{ route('modal.show', 'location') }}' data-toggle="modal" data-target="#createModal" data-select='{{ $name }}_location_select' class="btn btn-sm btn-primary">{{ trans('button.new') }}</a>
|
||||
@endcan
|
||||
@endunless
|
||||
</div>
|
||||
|
||||
{!! $errors->first($name, '<div class="col-md-8 col-md-offset-3"><span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span></div>') !!}
|
||||
|
||||
@if ($helpText)
|
||||
<div class="col-md-7 col-sm-11 col-md-offset-3">
|
||||
<p class="help-block">{{ $helpText }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@@ -25,6 +25,7 @@
|
||||
data-id-table="componentsTable"
|
||||
data-search="true"
|
||||
data-side-pagination="server"
|
||||
data-footer-style="footerStyle"
|
||||
data-show-columns="true"
|
||||
data-show-fullscreen="true"
|
||||
data-show-export="true"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
data-id-table="consumablesTable"
|
||||
data-search="true"
|
||||
data-side-pagination="server"
|
||||
data-footer-style="footerStyle"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
data-show-fullscreen="true"
|
||||
|
||||
@@ -292,8 +292,10 @@
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
|
||||
<span class="js-copy">{{ $consumable->item_no }}</span>
|
||||
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
|
||||
<span class="js-copy-item_no">{{ $consumable->item_no }}</span>
|
||||
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy-item_no"
|
||||
aria-hidden="true" data-tooltip="true" data-placement="top"
|
||||
title="{{ trans('general.copy_to_clipboard') }}">
|
||||
<span class="sr-only">{{ trans('general.copy_to_clipboard') }}</span>
|
||||
</i>
|
||||
|
||||
@@ -308,8 +310,10 @@
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
|
||||
<span class="js-copy">{{ $consumable->model_number }}</span>
|
||||
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy" aria-hidden="true" data-tooltip="true" data-placement="top" title="{{ trans('general.copy_to_clipboard') }}">
|
||||
<span class="js-copy-model_no">{{ $consumable->model_number }}</span>
|
||||
<i class="fa-regular fa-clipboard js-copy-link" data-clipboard-target=".js-copy-model_no"
|
||||
aria-hidden="true" data-tooltip="true" data-placement="top"
|
||||
title="{{ trans('general.copy_to_clipboard') }}">
|
||||
<span class="sr-only">{{ trans('general.copy_to_clipboard') }}</span>
|
||||
</i>
|
||||
|
||||
@@ -329,6 +333,22 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($consumable->adminuser)
|
||||
<!-- created at -->
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
{{ trans('general.created_by') }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
@if ($consumable->adminuser->deleted_at == '')
|
||||
<a href="{{ route('users.show', ['user' => $consumable->adminuser]) }}">{{ $consumable->adminuser->present()->fullName }}</a>
|
||||
@else
|
||||
<del>{{ $consumable->adminuser->present()->fullName }}</del>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($consumable->created_at)
|
||||
<!-- created at -->
|
||||
<div class="row">
|
||||
@@ -353,24 +373,6 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($consumable->admin)
|
||||
<!-- created at -->
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
{{ trans('general.created_by') }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
|
||||
@if ($consumable->admin->deleted_at == '')
|
||||
<a href="{{ route('users.show', ['user' => $consumable->admin]) }}">{{ $consumable->admin->present()->fullName }}</a>
|
||||
@else
|
||||
<del>{{ $consumable->admin->present()->fullName }}</del>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($consumable->notes)
|
||||
<!-- empty -->
|
||||
<div class="row">
|
||||
@@ -419,7 +421,7 @@
|
||||
{{ trans('general.date') }}
|
||||
</th>
|
||||
<th data-searchable="false" data-sortable="false" data-field="note">{{ trans('general.notes') }}</th>
|
||||
<th data-searchable="false" data-sortable="false" data-field="admin">{{ trans('general.admin') }}</th>
|
||||
<th data-searchable="false" data-sortable="false" data-field="admin">{{ trans('general.created_by') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
@@ -470,7 +472,7 @@
|
||||
<tr>
|
||||
<th data-visible="true" data-field="icon" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter">{{ trans('admin/hardware/table.icon') }}</th>
|
||||
<th data-visible="true" data-field="action_date" data-sortable="true" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||
<th data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th class="col-sm-2" data-field="file" data-visible="false" data-formatter="fileUploadNameFormatter">{{ trans('general.file_name') }}</th>
|
||||
<th data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
|
||||
@@ -224,6 +224,14 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Show in Audit Form -->
|
||||
<div class="col-md-9 col-md-offset-3" id="display_audit" style="padding-bottom: 10px;">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" name="display_audit" aria-label="display_audit" value="1" {{ (old('display_audit') || $field->display_audit) ? ' checked="checked"' : '' }}>
|
||||
{{ trans('admin/custom_fields/general.display_audit') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Show in View All Assets profile view -->
|
||||
<div class="col-md-9 col-md-offset-3" id="display_in_user_view">
|
||||
|
||||
@@ -196,6 +196,14 @@
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th data-sortable="true" data-visible="false" data-searchable="false" class="text-center"
|
||||
data-tooltip="{{ trans('admin/custom_fields/general.display_audit') }}">
|
||||
<x-icon type="due" />
|
||||
<span class="sr-only">
|
||||
{{ trans('admin/custom_fields/general.display_audit') }}
|
||||
</span>
|
||||
</th>
|
||||
|
||||
|
||||
<th data-sortable="true" data-searchable="true" class="text-center">{{ trans('admin/custom_fields/general.field_element_short') }}</th>
|
||||
|
||||
@@ -227,6 +235,7 @@
|
||||
<td class="text-center">{!! ($field->is_unique=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||
<td class="text-center">{!! ($field->display_checkin=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||
<td class="text-center">{!! ($field->display_checkout=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||
<td class="text-center">{!! ($field->display_audit=='1') ? '<i class="fas fa-check text-success" aria-hidden="true"><span class="sr-only">'.trans('general.yes').'</span></i>' : '<i class="fas fa-times text-danger" aria-hidden="true"><span class="sr-only">'.trans('general.no').'</span></i>' !!}</td>
|
||||
<td>{{ $field->element }}</td>
|
||||
<td>
|
||||
@foreach($field->fieldset as $fieldset)
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
<tr>
|
||||
<th data-field="icon" data-visible="true" style="width: 40px;" class="hidden-xs" data-formatter="iconFormatter"><span class="sr-only">{{ trans('admin/hardware/table.icon') }}</span></th>
|
||||
<th class="col-sm-3" data-visible="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.date') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.admin') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="admin" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="action_type">{{ trans('general.action') }}</th>
|
||||
<th class="col-sm-3" data-visible="true" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||
<th class="col-sm-2" data-visible="true" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.target') }}</th>
|
||||
|
||||
@@ -115,6 +115,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom fields -->
|
||||
@include("models/custom_fields_form", [
|
||||
'model' => $asset->model,
|
||||
'show_display_checkout_fields' => 'true'
|
||||
])
|
||||
|
||||
|
||||
<!-- Note -->
|
||||
<div class="form-group{{ $errors->has('note') ? ' has-error' : '' }}">
|
||||
|
||||
@@ -95,7 +95,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id', 'help_text' => ($asset->defaultLoc) ? trans('general.checkin_to_diff_location', ['default_location' => $asset->defaultLoc->name]) : null, 'hide_location_radio' => true])
|
||||
<x-input.location-select
|
||||
:label="trans('general.location')"
|
||||
name="location_id"
|
||||
:help_text="($asset->defaultLoc) ? trans('general.checkin_to_diff_location', ['default_location' => $asset->defaultLoc->name]) : null"
|
||||
:selected="old('location_id')"
|
||||
/>
|
||||
|
||||
<!-- Update actual location -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label class="form-control">
|
||||
{{ Form::radio('update_default_location', '1', old('update_default_location'), ['checked'=> 'checked', 'aria-label'=>'update_default_location']) }}
|
||||
{{ trans('admin/hardware/form.asset_location') }}
|
||||
</label>
|
||||
<label class="form-control">
|
||||
{{ Form::radio('update_default_location', '0', old('update_default_location'), ['aria-label'=>'update_default_location']) }}
|
||||
{{ trans('admin/hardware/form.asset_location_update_default_current') }}
|
||||
</label>
|
||||
</div>
|
||||
</div> <!--/form-group-->
|
||||
|
||||
<!-- Checkout/Checkin Date -->
|
||||
<div class="form-group{{ $errors->has('checkin_at') ? ' has-error' : '' }}">
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
var formData = $('#audit-form').serializeArray();
|
||||
|
||||
$.ajax({
|
||||
url: "{{ route('api.asset.audit') }}",
|
||||
url: "{{ route('api.asset.audit.legacy') }}",
|
||||
type : 'POST',
|
||||
headers: {
|
||||
"X-Requested-With": 'XMLHttpRequest',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user