mirror of
https://github.com/snipe/snipe-it.git
synced 2025-01-11 13:57:41 -08:00
Merge branch 'develop' into bug/sc-24812
This commit is contained in:
commit
eb0657c953
|
@ -29,6 +29,7 @@ RUN apk add --no-cache \
|
||||||
php81-sodium \
|
php81-sodium \
|
||||||
php81-redis \
|
php81-redis \
|
||||||
php81-pecl-memcached \
|
php81-pecl-memcached \
|
||||||
|
php81-exif \
|
||||||
curl \
|
curl \
|
||||||
wget \
|
wget \
|
||||||
vim \
|
vim \
|
||||||
|
|
17
TESTING.md
17
TESTING.md
|
@ -45,8 +45,21 @@ DB_PASSWORD={}
|
||||||
|
|
||||||
Now you are ready to run the entire test suite from your terminal:
|
Now you are ready to run the entire test suite from your terminal:
|
||||||
|
|
||||||
`php artisan test`
|
```shell
|
||||||
|
php artisan test
|
||||||
|
````
|
||||||
|
|
||||||
To run individual test files, you can pass the path to the test that you want to run:
|
To run individual test files, you can pass the path to the test that you want to run:
|
||||||
|
|
||||||
`php artisan test tests/Unit/AccessoryTest.php`
|
```shell
|
||||||
|
php artisan test tests/Unit/AccessoryTest.php
|
||||||
|
```
|
||||||
|
|
||||||
|
Some tests, like ones concerning LDAP, are marked with the `@group` annotation. Those groups can be run, or excluded, using the `--group` or `--exclude-group` flags:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
php artisan test --group=ldap
|
||||||
|
|
||||||
|
php artisan test --exclude-group=ldap
|
||||||
|
```
|
||||||
|
This can be helpful if a set of tests are failing because you don't have an extension, like LDAP, installed.
|
||||||
|
|
|
@ -5,6 +5,151 @@ namespace App\Console\Commands;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
|
|
||||||
|
class SQLStreamer {
|
||||||
|
private $input;
|
||||||
|
private $output;
|
||||||
|
// embed the prefix here?
|
||||||
|
public ?string $prefix;
|
||||||
|
|
||||||
|
private bool $reading_beginning_of_line = true;
|
||||||
|
|
||||||
|
public static $buffer_size = 1024 * 1024; // use a 1MB buffer, ought to work fine for most cases?
|
||||||
|
|
||||||
|
public array $tablenames = [];
|
||||||
|
private bool $should_guess = false;
|
||||||
|
private bool $statement_is_permitted = false;
|
||||||
|
|
||||||
|
public function __construct($input, $output, string $prefix = null)
|
||||||
|
{
|
||||||
|
$this->input = $input;
|
||||||
|
$this->output = $output;
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse_sql(string $line): string {
|
||||||
|
// take into account the 'start of line or not' setting as an instance variable?
|
||||||
|
// 'continuation' lines for a permitted statement are PERMITTED.
|
||||||
|
if($this->statement_is_permitted && $line[0] === ' ') {
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table_regex = '`?([a-zA-Z0-9_]+)`?';
|
||||||
|
$allowed_statements = [
|
||||||
|
"/^(DROP TABLE (?:IF EXISTS )?)`$table_regex(.*)$/" => false,
|
||||||
|
"/^(CREATE TABLE )$table_regex(.*)$/" => true, //sets up 'continuation'
|
||||||
|
"/^(LOCK TABLES )$table_regex(.*)$/" => false,
|
||||||
|
"/^(INSERT INTO )$table_regex(.*)$/" => false,
|
||||||
|
"/^UNLOCK TABLES/" => false,
|
||||||
|
// "/^\\) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;/" => false, // FIXME not sure what to do here?
|
||||||
|
"/^\\)[a-zA-Z0-9_= ]*;$/" => false
|
||||||
|
// ^^^^^^ that bit should *exit* the 'perimitted' black
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($allowed_statements as $statement => $statechange) {
|
||||||
|
// $this->info("Checking regex: $statement...\n");
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match($statement,$line,$matches)) {
|
||||||
|
$this->statement_is_permitted = $statechange;
|
||||||
|
// matches are: 1 => first part of the statement, 2 => tablename, 3 => rest of statement
|
||||||
|
// (with of course 0 being "the whole match")
|
||||||
|
if (@$matches[2]) {
|
||||||
|
// print "Found a tablename! It's: ".$matches[2]."\n";
|
||||||
|
if ($this->should_guess) {
|
||||||
|
@$this->tablenames[$matches[2]] += 1;
|
||||||
|
continue; //oh? FIXME
|
||||||
|
} else {
|
||||||
|
$cleaned_tablename = \DB::getTablePrefix().preg_replace('/^'.$this->prefix.'/','',$matches[2]);
|
||||||
|
$line = preg_replace($statement,'$1`'.$cleaned_tablename.'`$3' , $line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no explicit tablename in this one, leave the line alone
|
||||||
|
}
|
||||||
|
//how do we *replace* the tablename?
|
||||||
|
// print "RETURNING LINE: $line";
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all that is not allowed is denied.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//this is used in exactly *TWO* places, and in both cases should return a prefix I think?
|
||||||
|
// first - if you do the --sanitize-only one (which is mostly for testing/development)
|
||||||
|
// next - when you run *without* a guessed prefix, this is run first to figure out the prefix
|
||||||
|
// I think we have to *duplicate* the call to be able to run it again?
|
||||||
|
public static function guess_prefix($input):string
|
||||||
|
{
|
||||||
|
$parser = new self($input, null);
|
||||||
|
$parser->should_guess = true;
|
||||||
|
$parser->line_aware_piping(); // <----- THIS is doing the heavy lifting!
|
||||||
|
|
||||||
|
$check_tables = ['settings' => null, 'migrations' => null /* 'assets' => null */]; //TODO - move to statics?
|
||||||
|
//can't use 'users' because the 'accessories_users' table?
|
||||||
|
// can't use 'assets' because 'ver1_components_assets'
|
||||||
|
foreach($check_tables as $check_table => $_ignore) {
|
||||||
|
foreach ($parser->tablenames as $tablename => $_count) {
|
||||||
|
// print "Comparing $tablename to $check_table\n";
|
||||||
|
if (str_ends_with($tablename,$check_table)) {
|
||||||
|
// print "Found one!\n";
|
||||||
|
$check_tables[$check_table] = substr($tablename,0,-strlen($check_table));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$guessed_prefix = null;
|
||||||
|
foreach ($check_tables as $clean_table => $prefix_guess) {
|
||||||
|
if(is_null($prefix_guess)) {
|
||||||
|
print("Couldn't find table $clean_table\n");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
if(is_null($guessed_prefix)) {
|
||||||
|
$guessed_prefix = $prefix_guess;
|
||||||
|
} else {
|
||||||
|
if ($guessed_prefix != $prefix_guess) {
|
||||||
|
print("Prefix mismatch! Had guessed $guessed_prefix but got $prefix_guess\n");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $guessed_prefix;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function line_aware_piping(): int
|
||||||
|
{
|
||||||
|
$bytes_read = 0;
|
||||||
|
if (! $this->input) {
|
||||||
|
throw new \Exception("No Input available for line_aware_piping");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (($buffer = fgets($this->input, SQLStreamer::$buffer_size)) !== false) {
|
||||||
|
$bytes_read += strlen($buffer);
|
||||||
|
if ($this->reading_beginning_of_line) {
|
||||||
|
// \Log::debug("Buffer is: '$buffer'");
|
||||||
|
$cleaned_buffer = $this->parse_sql($buffer);
|
||||||
|
if ($this->output) {
|
||||||
|
$bytes_written = fwrite($this->output, $cleaned_buffer);
|
||||||
|
|
||||||
|
if ($bytes_written === false) {
|
||||||
|
throw new \Exception("Unable to write to pipe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we got a newline at the end of this, then the _next_ read is the beginning of a line
|
||||||
|
if($buffer[strlen($buffer)-1] === "\n") {
|
||||||
|
$this->reading_beginning_of_line = true;
|
||||||
|
} else {
|
||||||
|
$this->reading_beginning_of_line = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return $bytes_read;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RestoreFromBackup extends Command
|
class RestoreFromBackup extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -12,10 +157,13 @@ class RestoreFromBackup extends Command
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
// FIXME - , stripping prefixes and nonstandard SQL statements. Without --prefix, guess and return the correct prefix to strip
|
||||||
protected $signature = 'snipeit:restore
|
protected $signature = 'snipeit:restore
|
||||||
{--force : Skip the danger prompt; assuming you enter "y"}
|
{--force : Skip the danger prompt; assuming you enter "y"}
|
||||||
{filename : The zip file to be migrated}
|
{filename : The zip file to be migrated}
|
||||||
{--no-progress : Don\'t show a progress bar}';
|
{--no-progress : Don\'t show a progress bar}
|
||||||
|
{--sanitize-guess-prefix : Guess and output the table-prefix needed to "sanitize" the SQL}
|
||||||
|
{--sanitize-with-prefix= : "Sanitize" the SQL, using the passed-in table prefix (can be learned from --sanitize-guess-prefix). Pass as just \'--sanitize-with-prefix=\' to use no prefix}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
|
@ -34,8 +182,6 @@ class RestoreFromBackup extends Command
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static $buffer_size = 1024 * 1024; // use a 1MB buffer, ought to work fine for most cases?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
|
@ -55,7 +201,7 @@ class RestoreFromBackup extends Command
|
||||||
return $this->error('Missing required filename');
|
return $this->error('Missing required filename');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $this->option('force') && ! $this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
|
if (! $this->option('force') && ! $this->option('sanitize-guess-prefix') && ! $this->confirm('Are you sure you wish to restore from the given backup file? This can lead to MASSIVE DATA LOSS!')) {
|
||||||
return $this->error('Data loss not confirmed');
|
return $this->error('Data loss not confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,11 +304,11 @@ class RestoreFromBackup extends Command
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (array_merge($private_dirs, $public_dirs) as $dir) {
|
foreach (array_merge($private_dirs, $public_dirs) as $dir) {
|
||||||
$last_pos = strrpos($raw_path, $dir.'/');
|
$last_pos = strrpos($raw_path, $dir . '/');
|
||||||
if ($last_pos !== false) {
|
if ($last_pos !== false) {
|
||||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||||
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
|
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
|
||||||
$interesting_files[$raw_path] = ['dest' =>$dir, 'index' => $i];
|
$interesting_files[$raw_path] = ['dest' => $dir, 'index' => $i];
|
||||||
continue 2;
|
continue 2;
|
||||||
if ($last_pos + strlen($dir) + 1 == strlen($raw_path)) {
|
if ($last_pos + strlen($dir) + 1 == strlen($raw_path)) {
|
||||||
// we don't care about that; we just want files with the appropriate prefix
|
// we don't care about that; we just want files with the appropriate prefix
|
||||||
|
@ -171,7 +317,7 @@ class RestoreFromBackup extends Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
|
$good_extensions = ['png', 'gif', 'jpg', 'svg', 'jpeg', 'doc', 'docx', 'pdf', 'txt',
|
||||||
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico', ];
|
'zip', 'rar', 'xls', 'xlsx', 'lic', 'xml', 'rtf', 'webp', 'key', 'ico',];
|
||||||
foreach (array_merge($private_files, $public_files) as $file) {
|
foreach (array_merge($private_files, $public_files) as $file) {
|
||||||
$has_wildcard = (strpos($file, '*') !== false);
|
$has_wildcard = (strpos($file, '*') !== false);
|
||||||
if ($has_wildcard) {
|
if ($has_wildcard) {
|
||||||
|
@ -180,8 +326,8 @@ class RestoreFromBackup extends Command
|
||||||
$last_pos = strrpos($raw_path, $file); // no trailing slash!
|
$last_pos = strrpos($raw_path, $file); // no trailing slash!
|
||||||
if ($last_pos !== false) {
|
if ($last_pos !== false) {
|
||||||
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
|
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
|
||||||
if (! in_array($extension, $good_extensions)) {
|
if (!in_array($extension, $good_extensions)) {
|
||||||
$this->warn('Potentially unsafe file '.$raw_path.' is being skipped');
|
$this->warn('Potentially unsafe file ' . $raw_path . ' is being skipped');
|
||||||
$boring_files[] = $raw_path;
|
$boring_files[] = $raw_path;
|
||||||
continue 2;
|
continue 2;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +342,6 @@ class RestoreFromBackup extends Command
|
||||||
}
|
}
|
||||||
$boring_files[] = $raw_path; //if we've gotten to here and haven't continue'ed our way into the next iteration, we don't want this file
|
$boring_files[] = $raw_path; //if we've gotten to here and haven't continue'ed our way into the next iteration, we don't want this file
|
||||||
} // end of pre-processing the ZIP file for-loop
|
} // end of pre-processing the ZIP file for-loop
|
||||||
|
|
||||||
// print_r($interesting_files);exit(-1);
|
// print_r($interesting_files);exit(-1);
|
||||||
|
|
||||||
if (count($sqlfiles) != 1) {
|
if (count($sqlfiles) != 1) {
|
||||||
|
@ -208,6 +353,17 @@ class RestoreFromBackup extends Command
|
||||||
//older Snipe-IT installs don't have the db-dumps subdirectory component
|
//older Snipe-IT installs don't have the db-dumps subdirectory component
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sql_stat = $za->statIndex($sqlfile_indices[0]);
|
||||||
|
//$this->info("SQL Stat is: ".print_r($sql_stat,true));
|
||||||
|
$sql_contents = $za->getStream($sql_stat['name']); // maybe copy *THIS* thing?
|
||||||
|
|
||||||
|
// OKAY, now that we *found* the sql file if we're doing just the guess-prefix thing, we can do that *HERE* I think?
|
||||||
|
if ($this->option('sanitize-guess-prefix')) {
|
||||||
|
$prefix = SQLStreamer::guess_prefix($sql_contents);
|
||||||
|
$this->line($prefix);
|
||||||
|
return $this->info("Re-run this command with '--sanitize-with-prefix=".$prefix."' to see an attempt to sanitze your SQL.");
|
||||||
|
}
|
||||||
|
|
||||||
//how to invoke the restore?
|
//how to invoke the restore?
|
||||||
$pipes = [];
|
$pipes = [];
|
||||||
|
|
||||||
|
@ -228,6 +384,7 @@ class RestoreFromBackup extends Command
|
||||||
return $this->error('Unable to invoke mysql via CLI');
|
return $this->error('Unable to invoke mysql via CLI');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I'm not sure about these?
|
||||||
stream_set_blocking($pipes[1], false); // use non-blocking reads for stdout
|
stream_set_blocking($pipes[1], false); // use non-blocking reads for stdout
|
||||||
stream_set_blocking($pipes[2], false); // use non-blocking reads for stderr
|
stream_set_blocking($pipes[2], false); // use non-blocking reads for stderr
|
||||||
|
|
||||||
|
@ -238,9 +395,9 @@ class RestoreFromBackup extends Command
|
||||||
|
|
||||||
//$sql_contents = fopen($sqlfiles[0], "r"); //NOPE! This isn't a real file yet, silly-billy!
|
//$sql_contents = fopen($sqlfiles[0], "r"); //NOPE! This isn't a real file yet, silly-billy!
|
||||||
|
|
||||||
$sql_stat = $za->statIndex($sqlfile_indices[0]);
|
// FIXME - this feels like it wants to go somewhere else?
|
||||||
//$this->info("SQL Stat is: ".print_r($sql_stat,true));
|
// and it doesn't seem 'right' - if you can't get a stream to the .sql file,
|
||||||
$sql_contents = $za->getStream($sql_stat['name']);
|
// why do we care what's happening with pipes and stdout and stderr?!
|
||||||
if ($sql_contents === false) {
|
if ($sql_contents === false) {
|
||||||
$stdout = fgets($pipes[1]);
|
$stdout = fgets($pipes[1]);
|
||||||
$this->info($stdout);
|
$this->info($stdout);
|
||||||
|
@ -249,20 +406,27 @@ class RestoreFromBackup extends Command
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$bytes_read = 0;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (($buffer = fgets($sql_contents, self::$buffer_size)) !== false) {
|
if ( $this->option('sanitize-with-prefix') === null) {
|
||||||
$bytes_read += strlen($buffer);
|
// "Legacy" direct-piping
|
||||||
// \Log::debug("Buffer is: '$buffer'");
|
$bytes_read = 0;
|
||||||
|
while (($buffer = fgets($sql_contents, SQLStreamer::$buffer_size)) !== false) {
|
||||||
|
$bytes_read += strlen($buffer);
|
||||||
|
// \Log::debug("Buffer is: '$buffer'");
|
||||||
$bytes_written = fwrite($pipes[0], $buffer);
|
$bytes_written = fwrite($pipes[0], $buffer);
|
||||||
|
|
||||||
if ($bytes_written === false) {
|
if ($bytes_written === false) {
|
||||||
throw new Exception("Unable to write to pipe");
|
throw new Exception("Unable to write to pipe");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$sql_importer = new SQLStreamer($sql_contents, $pipes[0], $this->option('sanitize-with-prefix'));
|
||||||
|
$bytes_read = $sql_importer->line_aware_piping();
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
\Log::error("Error during restore!!!! ".$e->getMessage());
|
\Log::error("Error during restore!!!! ".$e->getMessage());
|
||||||
|
// FIXME - put these back and/or put them in the right places?!
|
||||||
$err_out = fgets($pipes[1]);
|
$err_out = fgets($pipes[1]);
|
||||||
$err_err = fgets($pipes[2]);
|
$err_err = fgets($pipes[2]);
|
||||||
\Log::error("Error OUTPUT: ".$err_out);
|
\Log::error("Error OUTPUT: ".$err_out);
|
||||||
|
@ -271,7 +435,6 @@ class RestoreFromBackup extends Command
|
||||||
$this->error($err_err);
|
$this->error($err_err);
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!feof($sql_contents) || $bytes_read == 0) {
|
if (!feof($sql_contents) || $bytes_read == 0) {
|
||||||
return $this->error("Not at end of file for sql file, or zero bytes read. aborting!");
|
return $this->error("Not at end of file for sql file, or zero bytes read. aborting!");
|
||||||
}
|
}
|
||||||
|
@ -303,7 +466,7 @@ class RestoreFromBackup extends Command
|
||||||
$fp = $za->getStream($ugly_file_name);
|
$fp = $za->getStream($ugly_file_name);
|
||||||
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
||||||
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
|
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
|
||||||
while (($buffer = fgets($fp, self::$buffer_size)) !== false) {
|
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
|
||||||
fwrite($migrated_file, $buffer);
|
fwrite($migrated_file, $buffer);
|
||||||
}
|
}
|
||||||
fclose($migrated_file);
|
fclose($migrated_file);
|
||||||
|
|
|
@ -11,6 +11,7 @@ use App\Models\CustomFieldset;
|
||||||
use App\Models\Depreciation;
|
use App\Models\Depreciation;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use App\Models\Statuslabel;
|
use App\Models\Statuslabel;
|
||||||
|
use App\Models\License;
|
||||||
use Crypt;
|
use Crypt;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
use Image;
|
use Image;
|
||||||
|
@ -715,18 +716,19 @@ class Helper
|
||||||
*/
|
*/
|
||||||
public static function checkLowInventory()
|
public static function checkLowInventory()
|
||||||
{
|
{
|
||||||
|
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
||||||
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
$consumables = Consumable::withCount('consumableAssignments as consumable_assignments_count')->whereNotNull('min_amt')->get();
|
||||||
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
|
$accessories = Accessory::withCount('users as users_count')->whereNotNull('min_amt')->get();
|
||||||
$components = Component::whereNotNull('min_amt')->get();
|
$components = Component::whereNotNull('min_amt')->get();
|
||||||
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
||||||
|
$licenses = License::where('min_amt', '>', 0)->get();
|
||||||
|
|
||||||
$avail_consumables = 0;
|
|
||||||
$items_array = [];
|
$items_array = [];
|
||||||
$all_count = 0;
|
$all_count = 0;
|
||||||
|
|
||||||
foreach ($consumables as $consumable) {
|
foreach ($consumables as $consumable) {
|
||||||
$avail = $consumable->numRemaining();
|
$avail = $consumable->numRemaining();
|
||||||
if ($avail < ($consumable->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
if ($avail < ($consumable->min_amt) + $alert_threshold) {
|
||||||
if ($consumable->qty > 0) {
|
if ($consumable->qty > 0) {
|
||||||
$percent = number_format((($avail / $consumable->qty) * 100), 0);
|
$percent = number_format((($avail / $consumable->qty) * 100), 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -745,7 +747,7 @@ class Helper
|
||||||
|
|
||||||
foreach ($accessories as $accessory) {
|
foreach ($accessories as $accessory) {
|
||||||
$avail = $accessory->qty - $accessory->users_count;
|
$avail = $accessory->qty - $accessory->users_count;
|
||||||
if ($avail < ($accessory->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
if ($avail < ($accessory->min_amt) + $alert_threshold) {
|
||||||
if ($accessory->qty > 0) {
|
if ($accessory->qty > 0) {
|
||||||
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -764,7 +766,7 @@ class Helper
|
||||||
|
|
||||||
foreach ($components as $component) {
|
foreach ($components as $component) {
|
||||||
$avail = $component->numRemaining();
|
$avail = $component->numRemaining();
|
||||||
if ($avail < ($component->min_amt) + \App\Models\Setting::getSettings()->alert_threshold) {
|
if ($avail < ($component->min_amt) + $alert_threshold) {
|
||||||
if ($component->qty > 0) {
|
if ($component->qty > 0) {
|
||||||
$percent = number_format((($avail / $component->qty) * 100), 0);
|
$percent = number_format((($avail / $component->qty) * 100), 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -787,7 +789,7 @@ class Helper
|
||||||
$total_owned = $asset->where('model_id', '=', $asset_model->id)->count();
|
$total_owned = $asset->where('model_id', '=', $asset_model->id)->count();
|
||||||
$avail = $asset->where('model_id', '=', $asset_model->id)->whereNull('assigned_to')->count();
|
$avail = $asset->where('model_id', '=', $asset_model->id)->whereNull('assigned_to')->count();
|
||||||
|
|
||||||
if ($avail < ($asset_model->min_amt)+ \App\Models\Setting::getSettings()->alert_threshold) {
|
if ($avail < ($asset_model->min_amt) + $alert_threshold) {
|
||||||
if ($avail > 0) {
|
if ($avail > 0) {
|
||||||
$percent = number_format((($avail / $total_owned) * 100), 0);
|
$percent = number_format((($avail / $total_owned) * 100), 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -803,6 +805,26 @@ class Helper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($licenses as $license){
|
||||||
|
$avail = $license->remaincount();
|
||||||
|
if ($avail < ($license->min_amt) + $alert_threshold) {
|
||||||
|
if ($avail > 0) {
|
||||||
|
$percent = number_format((($avail / $license->min_amt) * 100), 0);
|
||||||
|
} else {
|
||||||
|
$percent = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
$items_array[$all_count]['id'] = $license->id;
|
||||||
|
$items_array[$all_count]['name'] = $license->name;
|
||||||
|
$items_array[$all_count]['type'] = 'licenses';
|
||||||
|
$items_array[$all_count]['percent'] = $percent;
|
||||||
|
$items_array[$all_count]['remaining'] = $avail;
|
||||||
|
$items_array[$all_count]['min_amt'] = $license->min_amt;
|
||||||
|
$all_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return $items_array;
|
return $items_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,8 @@ class AssetMaintenancesController extends Controller
|
||||||
{
|
{
|
||||||
$this->authorize('view', Asset::class);
|
$this->authorize('view', Asset::class);
|
||||||
|
|
||||||
$maintenances = AssetMaintenance::select('asset_maintenances.*')->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'admin');
|
$maintenances = AssetMaintenance::select('asset_maintenances.*')
|
||||||
|
->with('asset', 'asset.model', 'asset.location', 'asset.defaultLoc', 'supplier', 'asset.company', 'asset.assetstatus', 'admin');
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$maintenances = $maintenances->TextSearch($request->input('search'));
|
$maintenances = $maintenances->TextSearch($request->input('search'));
|
||||||
|
@ -47,7 +48,7 @@ class AssetMaintenancesController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->filled('supplier_id')) {
|
if ($request->filled('supplier_id')) {
|
||||||
$maintenances->where('supplier_id', '=', $request->input('supplier_id'));
|
$maintenances->where('asset_maintenances.supplier_id', '=', $request->input('supplier_id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->filled('asset_maintenance_type')) {
|
if ($request->filled('asset_maintenance_type')) {
|
||||||
|
@ -70,10 +71,13 @@ class AssetMaintenancesController extends Controller
|
||||||
'notes',
|
'notes',
|
||||||
'asset_tag',
|
'asset_tag',
|
||||||
'asset_name',
|
'asset_name',
|
||||||
|
'serial',
|
||||||
'user_id',
|
'user_id',
|
||||||
'supplier',
|
'supplier',
|
||||||
'is_warranty',
|
'is_warranty',
|
||||||
|
'status_label',
|
||||||
];
|
];
|
||||||
|
|
||||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||||
|
|
||||||
|
@ -90,6 +94,12 @@ class AssetMaintenancesController extends Controller
|
||||||
case 'asset_name':
|
case 'asset_name':
|
||||||
$maintenances = $maintenances->OrderByAssetName($order);
|
$maintenances = $maintenances->OrderByAssetName($order);
|
||||||
break;
|
break;
|
||||||
|
case 'serial':
|
||||||
|
$maintenances = $maintenances->OrderByAssetSerial($order);
|
||||||
|
break;
|
||||||
|
case 'status_label':
|
||||||
|
$maintenances = $maintenances->OrderStatusName($order);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$maintenances = $maintenances->orderBy($sort, $order);
|
$maintenances = $maintenances->orderBy($sort, $order);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -4,6 +4,10 @@ namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Events\CheckoutableCheckedIn;
|
use App\Events\CheckoutableCheckedIn;
|
||||||
use App\Http\Requests\StoreAssetRequest;
|
use App\Http\Requests\StoreAssetRequest;
|
||||||
|
use App\Http\Traits\MigratesLegacyAssetLocations;
|
||||||
|
use App\Models\CheckoutAcceptance;
|
||||||
|
use App\Models\LicenseSeat;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
@ -45,6 +49,8 @@ use Route;
|
||||||
*/
|
*/
|
||||||
class AssetsController extends Controller
|
class AssetsController extends Controller
|
||||||
{
|
{
|
||||||
|
use MigratesLegacyAssetLocations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns JSON listing of all assets
|
* Returns JSON listing of all assets
|
||||||
*
|
*
|
||||||
|
@ -105,6 +111,7 @@ class AssetsController extends Controller
|
||||||
'requests_counter',
|
'requests_counter',
|
||||||
'byod',
|
'byod',
|
||||||
'asset_eol_date',
|
'asset_eol_date',
|
||||||
|
'requestable',
|
||||||
];
|
];
|
||||||
|
|
||||||
$filter = [];
|
$filter = [];
|
||||||
|
@ -864,11 +871,9 @@ class AssetsController extends Controller
|
||||||
*/
|
*/
|
||||||
public function checkin(Request $request, $asset_id)
|
public function checkin(Request $request, $asset_id)
|
||||||
{
|
{
|
||||||
$this->authorize('checkin', Asset::class);
|
|
||||||
$asset = Asset::with('model')->findOrFail($asset_id);
|
$asset = Asset::with('model')->findOrFail($asset_id);
|
||||||
$this->authorize('checkin', $asset);
|
$this->authorize('checkin', $asset);
|
||||||
|
|
||||||
|
|
||||||
$target = $asset->assignedTo;
|
$target = $asset->assignedTo;
|
||||||
if (is_null($target)) {
|
if (is_null($target)) {
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', [
|
return response()->json(Helper::formatStandardApiResponse('error', [
|
||||||
|
@ -881,7 +886,6 @@ class AssetsController extends Controller
|
||||||
$asset->expected_checkin = null;
|
$asset->expected_checkin = null;
|
||||||
//$asset->last_checkout = null;
|
//$asset->last_checkout = null;
|
||||||
$asset->last_checkin = now();
|
$asset->last_checkin = now();
|
||||||
$asset->assigned_to = null;
|
|
||||||
$asset->assignedTo()->disassociate($asset);
|
$asset->assignedTo()->disassociate($asset);
|
||||||
$asset->accepted = null;
|
$asset->accepted = null;
|
||||||
|
|
||||||
|
@ -889,10 +893,16 @@ class AssetsController extends Controller
|
||||||
$asset->name = $request->input('name');
|
$asset->name = $request->input('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->migrateLegacyLocations($asset);
|
||||||
|
|
||||||
$asset->location_id = $asset->rtd_location_id;
|
$asset->location_id = $asset->rtd_location_id;
|
||||||
|
|
||||||
if ($request->filled('location_id')) {
|
if ($request->filled('location_id')) {
|
||||||
$asset->location_id = $request->input('location_id');
|
$asset->location_id = $request->input('location_id');
|
||||||
|
|
||||||
|
if ($request->input('update_default_location')){
|
||||||
|
$asset->rtd_location_id = $request->input('location_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->has('status_id')) {
|
if ($request->has('status_id')) {
|
||||||
|
@ -906,6 +916,23 @@ class AssetsController extends Controller
|
||||||
$originalValues['action_date'] = $checkin_at;
|
$originalValues['action_date'] = $checkin_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$asset->licenseseats->each(function (LicenseSeat $seat) {
|
||||||
|
$seat->update(['assigned_to' => null]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get all pending Acceptances for this asset and delete them
|
||||||
|
CheckoutAcceptance::pending()
|
||||||
|
->whereHasMorph(
|
||||||
|
'checkoutable',
|
||||||
|
[Asset::class],
|
||||||
|
function (Builder $query) use ($asset) {
|
||||||
|
$query->where('id', $asset->id);
|
||||||
|
})
|
||||||
|
->get()
|
||||||
|
->map(function ($acceptance) {
|
||||||
|
$acceptance->delete();
|
||||||
|
});
|
||||||
|
|
||||||
if ($asset->save()) {
|
if ($asset->save()) {
|
||||||
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
|
event(new CheckoutableCheckedIn($asset, $target, Auth::user(), $request->input('note'), $checkin_at, $originalValues));
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
|
||||||
use App\Http\Transformers\GroupsTransformer;
|
use App\Http\Transformers\GroupsTransformer;
|
||||||
use App\Models\Group;
|
use App\Models\Group;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Auth;
|
||||||
|
|
||||||
|
|
||||||
class GroupsController extends Controller
|
class GroupsController extends Controller
|
||||||
|
@ -25,7 +26,7 @@ class GroupsController extends Controller
|
||||||
$this->authorize('view', Group::class);
|
$this->authorize('view', Group::class);
|
||||||
$allowed_columns = ['id', 'name', 'created_at', 'users_count'];
|
$allowed_columns = ['id', 'name', 'created_at', 'users_count'];
|
||||||
|
|
||||||
$groups = Group::select('id', 'name', 'permissions', 'created_at', 'updated_at')->withCount('users as users_count');
|
$groups = Group::select('id', 'name', 'permissions', 'created_at', 'updated_at', 'created_by')->with('admin')->withCount('users as users_count');
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$groups = $groups->TextSearch($request->input('search'));
|
$groups = $groups->TextSearch($request->input('search'));
|
||||||
|
@ -63,6 +64,7 @@ class GroupsController extends Controller
|
||||||
$group = new Group;
|
$group = new Group;
|
||||||
|
|
||||||
$group->name = $request->input('name');
|
$group->name = $request->input('name');
|
||||||
|
$group->created_by = Auth::user()->id;
|
||||||
$group->permissions = json_encode($request->input('permissions')); // Todo - some JSON validation stuff here
|
$group->permissions = json_encode($request->input('permissions')); // Todo - some JSON validation stuff here
|
||||||
|
|
||||||
if ($group->save()) {
|
if ($group->save()) {
|
||||||
|
|
|
@ -136,6 +136,7 @@ class LicensesController extends Controller
|
||||||
'seats',
|
'seats',
|
||||||
'termination_date',
|
'termination_date',
|
||||||
'depreciation_id',
|
'depreciation_id',
|
||||||
|
'min_amt',
|
||||||
];
|
];
|
||||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||||
$licenses = $licenses->orderBy($sort, $order);
|
$licenses = $licenses->orderBy($sort, $order);
|
||||||
|
|
|
@ -25,9 +25,27 @@ class LocationsController extends Controller
|
||||||
{
|
{
|
||||||
$this->authorize('view', Location::class);
|
$this->authorize('view', Location::class);
|
||||||
$allowed_columns = [
|
$allowed_columns = [
|
||||||
'id', 'name', 'address', 'address2', 'city', 'state', 'country', 'zip', 'created_at',
|
'id',
|
||||||
'updated_at', 'manager_id', 'image',
|
'name',
|
||||||
'assigned_assets_count', 'users_count', 'assets_count','assigned_assets_count', 'assets_count', 'rtd_assets_count', 'currency', 'ldap_ou', ];
|
'address',
|
||||||
|
'address2',
|
||||||
|
'city',
|
||||||
|
'state',
|
||||||
|
'country',
|
||||||
|
'zip',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
'manager_id',
|
||||||
|
'image',
|
||||||
|
'assigned_assets_count',
|
||||||
|
'users_count',
|
||||||
|
'assets_count',
|
||||||
|
'assigned_assets_count',
|
||||||
|
'assets_count',
|
||||||
|
'rtd_assets_count',
|
||||||
|
'currency',
|
||||||
|
'ldap_ou',
|
||||||
|
];
|
||||||
|
|
||||||
$locations = Location::with('parent', 'manager', 'children')->select([
|
$locations = Location::with('parent', 'manager', 'children')->select([
|
||||||
'locations.id',
|
'locations.id',
|
||||||
|
@ -50,6 +68,7 @@ class LocationsController extends Controller
|
||||||
])->withCount('assignedAssets as assigned_assets_count')
|
])->withCount('assignedAssets as assigned_assets_count')
|
||||||
->withCount('assets as assets_count')
|
->withCount('assets as assets_count')
|
||||||
->withCount('rtd_assets as rtd_assets_count')
|
->withCount('rtd_assets as rtd_assets_count')
|
||||||
|
->withCount('children as children_count')
|
||||||
->withCount('users as users_count');
|
->withCount('users as users_count');
|
||||||
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
|
@ -80,6 +99,10 @@ class LocationsController extends Controller
|
||||||
$locations->where('locations.country', '=', $request->input('country'));
|
$locations->where('locations.country', '=', $request->input('country'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($request->filled('manager_id')) {
|
||||||
|
$locations->where('locations.manager_id', '=', $request->input('manager_id'));
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the offset and limit are actually integers and do not exceed system limits
|
// Make sure the offset and limit are actually integers and do not exceed system limits
|
||||||
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : app('api_offset_value');
|
$offset = ($request->input('offset') > $locations->count()) ? $locations->count() : app('api_offset_value');
|
||||||
$limit = app('api_limit_value');
|
$limit = app('api_limit_value');
|
||||||
|
|
|
@ -33,7 +33,12 @@ class ReportsController extends Controller
|
||||||
|
|
||||||
if (($request->filled('item_type')) && ($request->filled('item_id'))) {
|
if (($request->filled('item_type')) && ($request->filled('item_id'))) {
|
||||||
$actionlogs = $actionlogs->where('item_id', '=', $request->input('item_id'))
|
$actionlogs = $actionlogs->where('item_id', '=', $request->input('item_id'))
|
||||||
->where('item_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')));
|
->where('item_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')))
|
||||||
|
->orWhere(function($query) use ($request)
|
||||||
|
{
|
||||||
|
$query->where('target_id', '=', $request->input('item_id'))
|
||||||
|
->where('target_type', '=', 'App\\Models\\'.ucwords($request->input('item_type')));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->filled('action_type')) {
|
if ($request->filled('action_type')) {
|
||||||
|
|
|
@ -148,7 +148,7 @@ class SettingsController extends Controller
|
||||||
*
|
*
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
* @since [v3.0]
|
* @since [v3.0]
|
||||||
* @return Redirect
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function ajaxTestEmail()
|
public function ajaxTestEmail()
|
||||||
{
|
{
|
||||||
|
@ -170,7 +170,7 @@ class SettingsController extends Controller
|
||||||
*
|
*
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
* @since [v5.0.0]
|
* @since [v5.0.0]
|
||||||
* @return Response
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function purgeBarcodes()
|
public function purgeBarcodes()
|
||||||
{
|
{
|
||||||
|
@ -211,7 +211,7 @@ class SettingsController extends Controller
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
* @since [v5.0.0]
|
* @since [v5.0.0]
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @return array
|
* @return array | JsonResponse
|
||||||
*/
|
*/
|
||||||
public function showLoginAttempts(Request $request)
|
public function showLoginAttempts(Request $request)
|
||||||
{
|
{
|
||||||
|
@ -229,6 +229,12 @@ class SettingsController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists backup files
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto]
|
||||||
|
* @return array | JsonResponse
|
||||||
|
*/
|
||||||
public function listBackups() {
|
public function listBackups() {
|
||||||
$settings = Setting::getSettings();
|
$settings = Setting::getSettings();
|
||||||
$path = 'app/backups';
|
$path = 'app/backups';
|
||||||
|
@ -249,12 +255,12 @@ class SettingsController extends Controller
|
||||||
'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])),
|
'filesize' => Setting::fileSizeConvert(Storage::size($backup_files[$f])),
|
||||||
'modified_value' => $file_timestamp,
|
'modified_value' => $file_timestamp,
|
||||||
'modified_display' => date($settings->date_display_format.' '.$settings->time_display_format, $file_timestamp),
|
'modified_display' => date($settings->date_display_format.' '.$settings->time_display_format, $file_timestamp),
|
||||||
|
'backup_url' => config('app.url').'/settings/backups/download/'.basename($backup_files[$f]),
|
||||||
|
|
||||||
];
|
];
|
||||||
$count++;
|
$count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,15 +270,56 @@ class SettingsController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a backup file.
|
||||||
|
* We use response()->download() here instead of Storage::download() because Storage::download()
|
||||||
|
* exhausts memory on larger files.
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto]
|
||||||
|
* @return JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||||
|
*/
|
||||||
public function downloadBackup($file) {
|
public function downloadBackup($file) {
|
||||||
|
|
||||||
$path = 'app/backups';
|
$path = storage_path('app/backups');
|
||||||
if (Storage::exists($path.'/'.$file)) {
|
|
||||||
|
if (Storage::exists('app/backups/'.$file)) {
|
||||||
$headers = ['ContentType' => 'application/zip'];
|
$headers = ['ContentType' => 'application/zip'];
|
||||||
return Storage::download($path.'/'.$file, $file, $headers);
|
return response()->download($path.'/'.$file, $file, $headers);
|
||||||
} else {
|
} else {
|
||||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_not_found')));
|
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_not_found')), 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines and downloads the latest backup
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto]
|
||||||
|
* @since [v6.3.1]
|
||||||
|
* @return JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||||
|
*/
|
||||||
|
public function downloadLatestBackup() {
|
||||||
|
|
||||||
|
$fileData = collect();
|
||||||
|
foreach (Storage::files('app/backups') as $file) {
|
||||||
|
if (pathinfo($file, PATHINFO_EXTENSION) == 'zip') {
|
||||||
|
$fileData->push([
|
||||||
|
'file' => $file,
|
||||||
|
'date' => Storage::lastModified($file)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$newest = $fileData->sortByDesc('date')->first();
|
||||||
|
if (Storage::exists($newest['file'])) {
|
||||||
|
$headers = ['ContentType' => 'application/zip'];
|
||||||
|
return response()->download(storage_path($newest['file']), basename($newest['file']), $headers);
|
||||||
|
} else {
|
||||||
|
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.file_not_found')), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -73,6 +73,7 @@ class UsersController extends Controller
|
||||||
'users.end_date',
|
'users.end_date',
|
||||||
'users.vip',
|
'users.vip',
|
||||||
'users.autoassign_licenses',
|
'users.autoassign_licenses',
|
||||||
|
'users.website',
|
||||||
|
|
||||||
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',)
|
])->with('manager', 'groups', 'userloc', 'company', 'department', 'assets', 'licenses', 'accessories', 'consumables', 'createdBy',)
|
||||||
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count');
|
->withCount('assets as assets_count', 'licenses as licenses_count', 'accessories as accessories_count', 'consumables as consumables_count');
|
||||||
|
@ -122,6 +123,10 @@ class UsersController extends Controller
|
||||||
$users = $users->where('users.country', '=', $request->input('country'));
|
$users = $users->where('users.country', '=', $request->input('country'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($request->filled('website')) {
|
||||||
|
$users = $users->where('users.website', '=', $request->input('website'));
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->filled('zip')) {
|
if ($request->filled('zip')) {
|
||||||
$users = $users->where('users.zip', '=', $request->input('zip'));
|
$users = $users->where('users.zip', '=', $request->input('zip'));
|
||||||
}
|
}
|
||||||
|
@ -254,6 +259,7 @@ class UsersController extends Controller
|
||||||
'start_date',
|
'start_date',
|
||||||
'end_date',
|
'end_date',
|
||||||
'autoassign_licenses',
|
'autoassign_licenses',
|
||||||
|
'website',
|
||||||
];
|
];
|
||||||
|
|
||||||
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
$sort = in_array($request->get('sort'), $allowed_columns) ? $request->get('sort') : 'first_name';
|
||||||
|
@ -274,11 +280,6 @@ class UsersController extends Controller
|
||||||
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
|
$offset = ($request->input('offset') > $users->count()) ? $users->count() : app('api_offset_value');
|
||||||
$limit = app('api_limit_value');
|
$limit = app('api_limit_value');
|
||||||
|
|
||||||
\Log::debug('Requested offset: '. $request->input('offset'));
|
|
||||||
\Log::debug('App offset: '. app('api_offset_value'));
|
|
||||||
\Log::debug('Actual offset: '. $offset);
|
|
||||||
\Log::debug('Limit: '. $limit);
|
|
||||||
|
|
||||||
$total = $users->count();
|
$total = $users->count();
|
||||||
$users = $users->skip($offset)->take($limit)->get();
|
$users = $users->skip($offset)->take($limit)->get();
|
||||||
|
|
||||||
|
|
|
@ -148,30 +148,20 @@ class AssetMaintenancesController extends Controller
|
||||||
*/
|
*/
|
||||||
public function edit($assetMaintenanceId = null)
|
public function edit($assetMaintenanceId = null)
|
||||||
{
|
{
|
||||||
|
$this->authorize('update', Asset::class);
|
||||||
|
// Check if the asset maintenance exists
|
||||||
$this->authorize('update', Asset::class);
|
$this->authorize('update', Asset::class);
|
||||||
// Check if the asset maintenance exists
|
// Check if the asset maintenance exists
|
||||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||||
// Redirect to the improvement management page
|
// Redirect to the asset maintenance management page
|
||||||
return redirect()->route('maintenances.index')
|
return redirect()->route('maintenances.index')->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
} elseif ((!$assetMaintenance->asset) || ($assetMaintenance->asset->deleted_at!='')) {
|
||||||
} elseif (! $assetMaintenance->asset) {
|
// Redirect to the asset maintenance management page
|
||||||
return redirect()->route('maintenances.index')
|
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||||
->with('error', 'The asset associated with this maintenance does not exist.');
|
|
||||||
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||||
return static::getInsufficientPermissionsRedirect();
|
return static::getInsufficientPermissionsRedirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($assetMaintenance->completion_date == '0000-00-00') {
|
|
||||||
$assetMaintenance->completion_date = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($assetMaintenance->start_date == '0000-00-00') {
|
|
||||||
$assetMaintenance->start_date = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($assetMaintenance->cost == '0.00') {
|
|
||||||
$assetMaintenance->cost = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare Improvement Type List
|
// Prepare Improvement Type List
|
||||||
$assetMaintenanceType = [
|
$assetMaintenanceType = [
|
||||||
|
@ -203,8 +193,10 @@ class AssetMaintenancesController extends Controller
|
||||||
// Check if the asset maintenance exists
|
// Check if the asset maintenance exists
|
||||||
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
if (is_null($assetMaintenance = AssetMaintenance::find($assetMaintenanceId))) {
|
||||||
// Redirect to the asset maintenance management page
|
// Redirect to the asset maintenance management page
|
||||||
return redirect()->route('maintenances.index')
|
return redirect()->route('maintenances.index')->with('error', trans('admin/asset_maintenances/message.not_found'));
|
||||||
->with('error', trans('admin/asset_maintenances/message.not_found'));
|
} elseif ((!$assetMaintenance->asset) || ($assetMaintenance->asset->deleted_at!='')) {
|
||||||
|
// Redirect to the asset maintenance management page
|
||||||
|
return redirect()->route('maintenances.index')->with('error', 'asset does not exist');
|
||||||
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
} elseif (! Company::isCurrentUserHasAccess($assetMaintenance->asset)) {
|
||||||
return static::getInsufficientPermissionsRedirect();
|
return static::getInsufficientPermissionsRedirect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -442,7 +442,6 @@ class AssetModelsController extends Controller
|
||||||
$del_count = 0;
|
$del_count = 0;
|
||||||
|
|
||||||
foreach ($models as $model) {
|
foreach ($models as $model) {
|
||||||
\Log::debug($model->id);
|
|
||||||
|
|
||||||
if ($model->assets_count > 0) {
|
if ($model->assets_count > 0) {
|
||||||
$del_error_count++;
|
$del_error_count++;
|
||||||
|
@ -452,8 +451,6 @@ class AssetModelsController extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
\Log::debug($del_count);
|
|
||||||
\Log::debug($del_error_count);
|
|
||||||
|
|
||||||
if ($del_error_count == 0) {
|
if ($del_error_count == 0) {
|
||||||
return redirect()->route('models.index')
|
return redirect()->route('models.index')
|
||||||
|
|
|
@ -6,8 +6,10 @@ use App\Events\CheckoutableCheckedIn;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\AssetCheckinRequest;
|
use App\Http\Requests\AssetCheckinRequest;
|
||||||
|
use App\Http\Traits\MigratesLegacyAssetLocations;
|
||||||
use App\Models\Asset;
|
use App\Models\Asset;
|
||||||
use App\Models\CheckoutAcceptance;
|
use App\Models\CheckoutAcceptance;
|
||||||
|
use App\Models\LicenseSeat;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Redirect;
|
use Illuminate\Support\Facades\Redirect;
|
||||||
|
@ -15,6 +17,8 @@ use Illuminate\Support\Facades\View;
|
||||||
|
|
||||||
class AssetCheckinController extends Controller
|
class AssetCheckinController extends Controller
|
||||||
{
|
{
|
||||||
|
use MigratesLegacyAssetLocations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a view that presents a form to check an asset back into inventory.
|
* Returns a view that presents a form to check an asset back into inventory.
|
||||||
*
|
*
|
||||||
|
@ -69,9 +73,7 @@ class AssetCheckinController extends Controller
|
||||||
$asset->expected_checkin = null;
|
$asset->expected_checkin = null;
|
||||||
//$asset->last_checkout = null;
|
//$asset->last_checkout = null;
|
||||||
$asset->last_checkin = now();
|
$asset->last_checkin = now();
|
||||||
$asset->assigned_to = null;
|
|
||||||
$asset->assignedTo()->disassociate($asset);
|
$asset->assignedTo()->disassociate($asset);
|
||||||
$asset->assigned_type = null;
|
|
||||||
$asset->accepted = null;
|
$asset->accepted = null;
|
||||||
$asset->name = $request->get('name');
|
$asset->name = $request->get('name');
|
||||||
|
|
||||||
|
@ -79,24 +81,7 @@ class AssetCheckinController extends Controller
|
||||||
$asset->status_id = e($request->get('status_id'));
|
$asset->status_id = e($request->get('status_id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is just meant to correct legacy issues where some user data would have 0
|
$this->migrateLegacyLocations($asset);
|
||||||
// as a location ID, which isn't valid. Later versions of Snipe-IT have stricter validation
|
|
||||||
// rules, so it's necessary to fix this for long-time users. It's kinda gross, but will help
|
|
||||||
// people (and their data) in the long run
|
|
||||||
|
|
||||||
if ($asset->rtd_location_id == '0') {
|
|
||||||
\Log::debug('Manually override the RTD location IDs');
|
|
||||||
\Log::debug('Original RTD Location ID: '.$asset->rtd_location_id);
|
|
||||||
$asset->rtd_location_id = '';
|
|
||||||
\Log::debug('New RTD Location ID: '.$asset->rtd_location_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($asset->location_id == '0') {
|
|
||||||
\Log::debug('Manually override the location IDs');
|
|
||||||
\Log::debug('Original Location ID: '.$asset->location_id);
|
|
||||||
$asset->location_id = '';
|
|
||||||
\Log::debug('New Location ID: '.$asset->location_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$asset->location_id = $asset->rtd_location_id;
|
$asset->location_id = $asset->rtd_location_id;
|
||||||
|
|
||||||
|
@ -117,12 +102,9 @@ class AssetCheckinController extends Controller
|
||||||
$checkin_at = $request->get('checkin_at');
|
$checkin_at = $request->get('checkin_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!empty($asset->licenseseats->all())){
|
$asset->licenseseats->each(function (LicenseSeat $seat) {
|
||||||
foreach ($asset->licenseseats as $seat){
|
$seat->update(['assigned_to' => null]);
|
||||||
$seat->assigned_to = null;
|
});
|
||||||
$seat->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all pending Acceptances for this asset and delete them
|
// Get all pending Acceptances for this asset and delete them
|
||||||
$acceptances = CheckoutAcceptance::pending()->whereHasMorph('checkoutable',
|
$acceptances = CheckoutAcceptance::pending()->whereHasMorph('checkoutable',
|
||||||
|
|
|
@ -737,11 +737,11 @@ class AssetsController extends Controller
|
||||||
|
|
||||||
if ($isCheckinHeaderExplicit) {
|
if ($isCheckinHeaderExplicit) {
|
||||||
|
|
||||||
//if checkin date header exists, assume that empty or future date is still checked out
|
// if checkin date header exists, assume that empty or future date is still checked out
|
||||||
//if checkin is before todays date, assume it's checked in and do not assign user ID, if checkin date is in the future or blank, this is the expected checkin date, items is checked out
|
// if checkin is before today's date, assume it's checked in and do not assign user ID, if checkin date is in the future or blank, this is the expected checkin date, items are checked out
|
||||||
|
|
||||||
if ((strtotime($checkin_date) > strtotime(Carbon::now())) || (empty($checkin_date))
|
if ((strtotime($checkin_date) > strtotime(Carbon::now())) || (empty($checkin_date)))
|
||||||
) {
|
{
|
||||||
//only do this if item is checked out
|
//only do this if item is checked out
|
||||||
$asset->assigned_to = $user->id;
|
$asset->assigned_to = $user->id;
|
||||||
$asset->assigned_type = User::class;
|
$asset->assigned_type = User::class;
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
use App\Models\Group;
|
use App\Models\Group;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Auth;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This controller handles all actions related to User Groups for
|
* This controller handles all actions related to User Groups for
|
||||||
|
@ -63,6 +64,7 @@ class GroupsController extends Controller
|
||||||
$group = new Group();
|
$group = new Group();
|
||||||
$group->name = $request->input('name');
|
$group->name = $request->input('name');
|
||||||
$group->permissions = json_encode($request->input('permission'));
|
$group->permissions = json_encode($request->input('permission'));
|
||||||
|
$group->created_by = Auth::user()->id;
|
||||||
|
|
||||||
if ($group->save()) {
|
if ($group->save()) {
|
||||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
|
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
|
||||||
|
|
|
@ -6,6 +6,7 @@ use App\Models\Asset;
|
||||||
use App\Models\AssetModel;
|
use App\Models\AssetModel;
|
||||||
use App\Models\Category;
|
use App\Models\Category;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
|
use App\Models\CustomField;
|
||||||
use App\Models\Labels\Label;
|
use App\Models\Labels\Label;
|
||||||
use App\Models\Location;
|
use App\Models\Location;
|
||||||
use App\Models\Manufacturer;
|
use App\Models\Manufacturer;
|
||||||
|
@ -65,6 +66,18 @@ class LabelsController extends Controller
|
||||||
$exampleAsset->model->category->id = 999999;
|
$exampleAsset->model->category->id = 999999;
|
||||||
$exampleAsset->model->category->name = trans('admin/labels/table.example_category');
|
$exampleAsset->model->category->name = trans('admin/labels/table.example_category');
|
||||||
|
|
||||||
|
$customFieldColumns = CustomField::all()->pluck('db_column');
|
||||||
|
|
||||||
|
collect(explode(';', Setting::getSettings()->label2_fields))
|
||||||
|
->filter()
|
||||||
|
->each(function ($item) use ($customFieldColumns, $exampleAsset) {
|
||||||
|
$pair = explode('=', $item);
|
||||||
|
|
||||||
|
if ($customFieldColumns->contains($pair[1])) {
|
||||||
|
$exampleAsset->{$pair[1]} = "{{$pair[0]}}";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$settings = Setting::getSettings();
|
$settings = Setting::getSettings();
|
||||||
if (request()->has('settings')) {
|
if (request()->has('settings')) {
|
||||||
$overrides = request()->get('settings');
|
$overrides = request()->get('settings');
|
||||||
|
|
|
@ -99,6 +99,7 @@ class LicensesController extends Controller
|
||||||
$license->category_id = $request->input('category_id');
|
$license->category_id = $request->input('category_id');
|
||||||
$license->termination_date = $request->input('termination_date');
|
$license->termination_date = $request->input('termination_date');
|
||||||
$license->user_id = Auth::id();
|
$license->user_id = Auth::id();
|
||||||
|
$license->min_amt = $request->input('min_amt');
|
||||||
|
|
||||||
if ($license->save()) {
|
if ($license->save()) {
|
||||||
return redirect()->route('licenses.index')->with('success', trans('admin/licenses/message.create.success'));
|
return redirect()->route('licenses.index')->with('success', trans('admin/licenses/message.create.success'));
|
||||||
|
@ -176,6 +177,7 @@ class LicensesController extends Controller
|
||||||
$license->manufacturer_id = $request->input('manufacturer_id');
|
$license->manufacturer_id = $request->input('manufacturer_id');
|
||||||
$license->supplier_id = $request->input('supplier_id');
|
$license->supplier_id = $request->input('supplier_id');
|
||||||
$license->category_id = $request->input('category_id');
|
$license->category_id = $request->input('category_id');
|
||||||
|
$license->min_amt = $request->input('min_amt');
|
||||||
|
|
||||||
if ($license->save()) {
|
if ($license->save()) {
|
||||||
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
|
return redirect()->route('licenses.show', ['license' => $licenseId])->with('success', trans('admin/licenses/message.update.success'));
|
||||||
|
@ -245,12 +247,6 @@ class LicensesController extends Controller
|
||||||
$available_seats_count = $license->availCount()->count();
|
$available_seats_count = $license->availCount()->count();
|
||||||
$checkedout_seats_count = ($total_seats_count - $available_seats_count);
|
$checkedout_seats_count = ($total_seats_count - $available_seats_count);
|
||||||
|
|
||||||
\Log::debug('Total: '.$total_seats_count);
|
|
||||||
\Log::debug('Users: '.$users_count);
|
|
||||||
\Log::debug('Available: '.$available_seats_count);
|
|
||||||
\Log::debug('Checkedout: '.$checkedout_seats_count);
|
|
||||||
|
|
||||||
|
|
||||||
$this->authorize('view', $license);
|
$this->authorize('view', $license);
|
||||||
return view('licenses.view', compact('license'))
|
return view('licenses.view', compact('license'))
|
||||||
->with('users_count', $users_count)
|
->with('users_count', $users_count)
|
||||||
|
|
|
@ -8,6 +8,7 @@ use App\Models\Location;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This controller handles all actions related to Locations for
|
* This controller handles all actions related to Locations for
|
||||||
|
@ -168,7 +169,7 @@ class LocationsController extends Controller
|
||||||
{
|
{
|
||||||
$this->authorize('delete', Location::class);
|
$this->authorize('delete', Location::class);
|
||||||
if (is_null($location = Location::find($locationId))) {
|
if (is_null($location = Location::find($locationId))) {
|
||||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.not_found'));
|
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($location->users()->count() > 0) {
|
if ($location->users()->count() > 0) {
|
||||||
|
@ -238,7 +239,7 @@ class LocationsController extends Controller
|
||||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
* @param int $locationId
|
* @param int $locationId
|
||||||
* @since [v6.0.14]
|
* @since [v6.0.14]
|
||||||
* @return View
|
* @return \Illuminate\Contracts\View\View
|
||||||
*/
|
*/
|
||||||
public function getClone($locationId = null)
|
public function getClone($locationId = null)
|
||||||
{
|
{
|
||||||
|
@ -272,8 +273,97 @@ class LocationsController extends Controller
|
||||||
|
|
||||||
}
|
}
|
||||||
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
return redirect()->route('locations.index')->with('error', trans('admin/locations/message.does_not_exist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a view that allows the user to bulk delete locations
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v6.3.1]
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function postBulkDelete(Request $request)
|
||||||
|
{
|
||||||
|
$locations_raw_array = $request->input('ids');
|
||||||
|
|
||||||
|
// Make sure some IDs have been selected
|
||||||
|
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {
|
||||||
|
$locations = Location::whereIn('id', $locations_raw_array)
|
||||||
|
->withCount('assignedAssets as assigned_assets_count')
|
||||||
|
->withCount('assets as assets_count')
|
||||||
|
->withCount('rtd_assets as rtd_assets_count')
|
||||||
|
->withCount('children as children_count')
|
||||||
|
->withCount('users as users_count')->get();
|
||||||
|
|
||||||
|
$valid_count = 0;
|
||||||
|
foreach ($locations as $location) {
|
||||||
|
if ($location->isDeletable()) {
|
||||||
|
$valid_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view('locations/bulk-delete', compact('locations'))->with('valid_count', $valid_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('models.index')
|
||||||
|
->with('error', 'You must select at least one model to edit.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that locations can be deleted and deletes them if they can
|
||||||
|
*
|
||||||
|
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||||
|
* @since [v6.3.1]
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function postBulkDeleteStore(Request $request) {
|
||||||
|
$locations_raw_array = $request->input('ids');
|
||||||
|
|
||||||
|
if ((is_array($locations_raw_array)) && (count($locations_raw_array) > 0)) {
|
||||||
|
$locations = Location::whereIn('id', $locations_raw_array)->get();
|
||||||
|
|
||||||
|
$success_count = 0;
|
||||||
|
$error_count = 0;
|
||||||
|
|
||||||
|
foreach ($locations as $location) {
|
||||||
|
|
||||||
|
// Can we delete this location?
|
||||||
|
if ($location->isDeletable()) {
|
||||||
|
$location->delete();
|
||||||
|
$success_count++;
|
||||||
|
} else {
|
||||||
|
$error_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\Log::debug('Success count: '.$success_count);
|
||||||
|
\Log::debug('Error count: '.$error_count);
|
||||||
|
// Complete success
|
||||||
|
if ($success_count == count($locations_raw_array)) {
|
||||||
|
return redirect()
|
||||||
|
->route('locations.index')
|
||||||
|
->with('success', trans_choice('general.bulk.delete.success', $success_count,
|
||||||
|
['object_type' => trans_choice('general.location_plural', $success_count), 'count' => $success_count]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial success
|
||||||
|
if ($error_count > 0) {
|
||||||
|
return redirect()
|
||||||
|
->route('locations.index')
|
||||||
|
->with('warning', trans('general.bulk.partial_success',
|
||||||
|
['success' => $success_count, 'error' => $error_count, 'object_type' => trans('general.locations')]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Nothing was selected - return to the index
|
||||||
|
return redirect()
|
||||||
|
->route('locations.index')
|
||||||
|
->with('error', trans('general.bulk.nothing_selected',
|
||||||
|
['object_type' => trans('general.locations')]
|
||||||
|
));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,8 +296,8 @@ class ReportsController extends Controller
|
||||||
e($actionlog->itemType()),
|
e($actionlog->itemType()),
|
||||||
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
|
($actionlog->itemType() == 'user') ? $actionlog->filename : $item_name,
|
||||||
($actionlog->item) ? $actionlog->item->serial : null,
|
($actionlog->item) ? $actionlog->item->serial : null,
|
||||||
($actionlog->item->model) ? htmlspecialchars($actionlog->item->model->name, ENT_NOQUOTES) : null,
|
(($actionlog->item) && ($actionlog->item->model)) ? htmlspecialchars($actionlog->item->model->name, ENT_NOQUOTES) : null,
|
||||||
($actionlog->item->model) ? $actionlog->item->model->model_number : null,
|
(($actionlog->item) && ($actionlog->item->model)) ? $actionlog->item->model->model_number : null,
|
||||||
$target_name,
|
$target_name,
|
||||||
($actionlog->note) ? e($actionlog->note) : '',
|
($actionlog->note) ? e($actionlog->note) : '',
|
||||||
$actionlog->log_meta,
|
$actionlog->log_meta,
|
||||||
|
@ -686,11 +686,17 @@ class ReportsController extends Controller
|
||||||
|
|
||||||
$assets->whereBetween('assets.created_at', [$created_start, $created_end]);
|
$assets->whereBetween('assets.created_at', [$created_start, $created_end]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($request->filled('checkout_date_start')) && ($request->filled('checkout_date_end'))) {
|
if (($request->filled('checkout_date_start')) && ($request->filled('checkout_date_end'))) {
|
||||||
$checkout_start = \Carbon::parse($request->input('checkout_date_start'))->startOfDay();
|
$checkout_start = \Carbon::parse($request->input('checkout_date_start'))->startOfDay();
|
||||||
$checkout_end = \Carbon::parse($request->input('checkout_date_end'))->endOfDay();
|
$checkout_end = \Carbon::parse($request->input('checkout_date_end',now()))->endOfDay();
|
||||||
|
|
||||||
$assets->whereBetween('assets.last_checkout', [$checkout_start, $checkout_end]);
|
$actionlogassets = Actionlog::where('action_type','=', 'checkout')
|
||||||
|
->where('item_type', 'LIKE', '%Asset%',)
|
||||||
|
->whereBetween('action_date',[$checkout_start, $checkout_end])
|
||||||
|
->pluck('item_id');
|
||||||
|
|
||||||
|
$assets->whereIn('id',$actionlogassets);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($request->filled('checkin_date_start'))) {
|
if (($request->filled('checkin_date_start'))) {
|
||||||
|
@ -1157,16 +1163,20 @@ class ReportsController extends Controller
|
||||||
$logItem = $logItem_res[0];
|
$logItem = $logItem_res[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$assetItem->assignedTo->locale){
|
// Only send notification if assigned
|
||||||
Notification::locale(Setting::getSettings()->locale)->send(
|
if ($assetItem->assignedTo) {
|
||||||
$assetItem->assignedTo,
|
|
||||||
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
if (!$assetItem->assignedTo->locale) {
|
||||||
);
|
Notification::locale(Setting::getSettings()->locale)->send(
|
||||||
} else {
|
$assetItem->assignedTo,
|
||||||
Notification::send(
|
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
||||||
$assetItem->assignedTo,
|
);
|
||||||
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
} else {
|
||||||
);
|
Notification::send(
|
||||||
|
$assetItem->assignedTo,
|
||||||
|
new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.reminder_sent'));
|
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.reminder_sent'));
|
||||||
|
|
|
@ -425,65 +425,64 @@ class SettingsController extends Controller
|
||||||
if (! config('app.lock_passwords')) {
|
if (! config('app.lock_passwords')) {
|
||||||
$setting->site_name = $request->input('site_name');
|
$setting->site_name = $request->input('site_name');
|
||||||
$setting->custom_css = $request->input('custom_css');
|
$setting->custom_css = $request->input('custom_css');
|
||||||
}
|
$setting = $request->handleImages($setting, 600, 'logo', '', 'logo');
|
||||||
|
|
||||||
$setting = $request->handleImages($setting, 600, 'logo', '', 'logo');
|
if ('1' == $request->input('clear_logo')) {
|
||||||
|
|
||||||
if ('1' == $request->input('clear_logo')) {
|
|
||||||
Storage::disk('public')->delete($setting->logo);
|
Storage::disk('public')->delete($setting->logo);
|
||||||
$setting->logo = null;
|
$setting->logo = null;
|
||||||
$setting->brand = 1;
|
$setting->brand = 1;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$setting = $request->handleImages($setting, 600, 'email_logo', '', 'email_logo');
|
|
||||||
|
|
||||||
|
|
||||||
if ('1' == $request->input('clear_email_logo')) {
|
|
||||||
Storage::disk('public')->delete($setting->email_logo);
|
|
||||||
$setting->email_logo = null;
|
|
||||||
// If they are uploading an image, validate it and upload it
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$setting = $request->handleImages($setting, 600, 'label_logo', '', 'label_logo');
|
|
||||||
|
|
||||||
|
|
||||||
if ('1' == $request->input('clear_label_logo')) {
|
|
||||||
Storage::disk('public')->delete($setting->label_logo);
|
|
||||||
$setting->label_logo = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// If the user wants to clear the favicon...
|
|
||||||
if ($request->hasFile('favicon')) {
|
|
||||||
$favicon_image = $favicon_upload = $request->file('favicon');
|
|
||||||
$favicon_ext = $favicon_image->getClientOriginalExtension();
|
|
||||||
$setting->favicon = $favicon_file_name = 'favicon-uploaded.'.$favicon_ext;
|
|
||||||
|
|
||||||
if (($favicon_image->getClientOriginalExtension() != 'ico') && ($favicon_image->getClientOriginalExtension() != 'svg')) {
|
|
||||||
$favicon_upload = Image::make($favicon_image->getRealPath())->resize(null, 36, function ($constraint) {
|
|
||||||
$constraint->aspectRatio();
|
|
||||||
$constraint->upsize();
|
|
||||||
});
|
|
||||||
|
|
||||||
// This requires a string instead of an object, so we use ($string)
|
|
||||||
Storage::disk('public')->put($favicon_file_name, (string) $favicon_upload->encode());
|
|
||||||
} else {
|
|
||||||
Storage::disk('public')->put($favicon_file_name, file_get_contents($request->file('favicon')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Remove Current image if exists
|
$setting = $request->handleImages($setting, 600, 'email_logo', '', 'email_logo');
|
||||||
if (($setting->favicon) && (file_exists($favicon_file_name))) {
|
|
||||||
Storage::disk('public')->delete($favicon_file_name);
|
|
||||||
}
|
|
||||||
} elseif ('1' == $request->input('clear_favicon')) {
|
|
||||||
Storage::disk('public')->delete($setting->clear_favicon);
|
|
||||||
$setting->favicon = null;
|
|
||||||
|
|
||||||
// If they are uploading an image, validate it and upload it
|
|
||||||
}
|
if ('1' == $request->input('clear_email_logo')) {
|
||||||
|
Storage::disk('public')->delete($setting->email_logo);
|
||||||
|
$setting->email_logo = null;
|
||||||
|
// If they are uploading an image, validate it and upload it
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$setting = $request->handleImages($setting, 600, 'label_logo', '', 'label_logo');
|
||||||
|
|
||||||
|
if ('1' == $request->input('clear_label_logo')) {
|
||||||
|
Storage::disk('public')->delete($setting->label_logo);
|
||||||
|
$setting->label_logo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If the user wants to clear the favicon...
|
||||||
|
if ($request->hasFile('favicon')) {
|
||||||
|
$favicon_image = $favicon_upload = $request->file('favicon');
|
||||||
|
$favicon_ext = $favicon_image->getClientOriginalExtension();
|
||||||
|
$setting->favicon = $favicon_file_name = 'favicon-uploaded.'.$favicon_ext;
|
||||||
|
|
||||||
|
if (($favicon_image->getClientOriginalExtension() != 'ico') && ($favicon_image->getClientOriginalExtension() != 'svg')) {
|
||||||
|
$favicon_upload = Image::make($favicon_image->getRealPath())->resize(null, 36, function ($constraint) {
|
||||||
|
$constraint->aspectRatio();
|
||||||
|
$constraint->upsize();
|
||||||
|
});
|
||||||
|
|
||||||
|
// This requires a string instead of an object, so we use ($string)
|
||||||
|
Storage::disk('public')->put($favicon_file_name, (string) $favicon_upload->encode());
|
||||||
|
} else {
|
||||||
|
Storage::disk('public')->put($favicon_file_name, file_get_contents($request->file('favicon')));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Remove Current image if exists
|
||||||
|
if (($setting->favicon) && (file_exists($favicon_file_name))) {
|
||||||
|
Storage::disk('public')->delete($favicon_file_name);
|
||||||
|
}
|
||||||
|
} elseif ('1' == $request->input('clear_favicon')) {
|
||||||
|
Storage::disk('public')->delete($setting->clear_favicon);
|
||||||
|
$setting->favicon = null;
|
||||||
|
|
||||||
|
// If they are uploading an image, validate it and upload it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($setting->save()) {
|
if ($setting->save()) {
|
||||||
return redirect()->route('settings.index')
|
return redirect()->route('settings.index')
|
||||||
|
|
|
@ -59,12 +59,21 @@ class Importer extends Component
|
||||||
'field_map' => 'array'
|
'field_map' => 'array'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used in resources/views/livewire/importer.blade.php, and we kinda shouldn't need to check for
|
||||||
|
* activeFile here, but there's some UI goofiness that allows this to crash out on some imports.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function generate_field_map()
|
public function generate_field_map()
|
||||||
{
|
{
|
||||||
\Log::debug("header row is: ".print_r($this->activeFile->header_row,true));
|
$tmp = array();
|
||||||
\Log::debug("Field map is: ".print_r($this->field_map,true));
|
if ($this->activeFile) {
|
||||||
$tmp = array_combine($this->activeFile->header_row, $this->field_map);
|
$tmp = array_combine($this->activeFile->header_row, $this->field_map);
|
||||||
return json_encode(array_filter($tmp));
|
$tmp = array_filter($tmp);
|
||||||
|
}
|
||||||
|
return json_encode($tmp);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ class ImageUploadRequest extends Request
|
||||||
|
|
||||||
if (!config('app.lock_passwords')) {
|
if (!config('app.lock_passwords')) {
|
||||||
|
|
||||||
$ext = $image->getClientOriginalExtension();
|
$ext = $image->guessExtension();
|
||||||
$file_name = $type.'-'.$form_fieldname.'-'.$item->id.'-'.str_random(10).'.'.$ext;
|
$file_name = $type.'-'.$form_fieldname.'-'.$item->id.'-'.str_random(10).'.'.$ext;
|
||||||
|
|
||||||
\Log::info('File name will be: '.$file_name);
|
\Log::info('File name will be: '.$file_name);
|
||||||
|
|
33
app/Http/Traits/MigratesLegacyAssetLocations.php
Normal file
33
app/Http/Traits/MigratesLegacyAssetLocations.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Traits;
|
||||||
|
|
||||||
|
use App\Models\Asset;
|
||||||
|
|
||||||
|
trait MigratesLegacyAssetLocations
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is just meant to correct legacy issues where some user data would have 0
|
||||||
|
* as a location ID, which isn't valid. Later versions of Snipe-IT have stricter validation
|
||||||
|
* rules, so it's necessary to fix this for long-time users. It's kinda gross, but will help
|
||||||
|
* people (and their data) in the long run
|
||||||
|
* @param Asset $asset
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function migrateLegacyLocations(Asset $asset): void
|
||||||
|
{
|
||||||
|
if ($asset->rtd_location_id == '0') {
|
||||||
|
\Log::debug('Manually override the RTD location IDs');
|
||||||
|
\Log::debug('Original RTD Location ID: ' . $asset->rtd_location_id);
|
||||||
|
$asset->rtd_location_id = '';
|
||||||
|
\Log::debug('New RTD Location ID: ' . $asset->rtd_location_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($asset->location_id == '0') {
|
||||||
|
\Log::debug('Manually override the location IDs');
|
||||||
|
\Log::debug('Original Location ID: ' . $asset->location_id);
|
||||||
|
$asset->location_id = '';
|
||||||
|
\Log::debug('New Location ID: ' . $asset->location_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,12 +28,20 @@ class AssetMaintenancesTransformer
|
||||||
'id' => (int) $assetmaintenance->asset->id,
|
'id' => (int) $assetmaintenance->asset->id,
|
||||||
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
|
'name'=> ($assetmaintenance->asset->name) ? e($assetmaintenance->asset->name) : null,
|
||||||
'asset_tag'=> e($assetmaintenance->asset->asset_tag),
|
'asset_tag'=> e($assetmaintenance->asset->asset_tag),
|
||||||
|
'serial'=> e($assetmaintenance->asset->serial),
|
||||||
|
'deleted_at'=> e($assetmaintenance->asset->deleted_at),
|
||||||
|
'created_at'=> e($assetmaintenance->asset->created_at),
|
||||||
] : null,
|
] : null,
|
||||||
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
|
'model' => (($assetmaintenance->asset) && ($assetmaintenance->asset->model)) ? [
|
||||||
'id' => (int) $assetmaintenance->asset->model->id,
|
'id' => (int) $assetmaintenance->asset->model->id,
|
||||||
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
|
'name'=> ($assetmaintenance->asset->model->name) ? e($assetmaintenance->asset->model->name).' '.e($assetmaintenance->asset->model->model_number) : null,
|
||||||
] : null,
|
] : null,
|
||||||
|
'status_label' => ($assetmaintenance->asset->assetstatus) ? [
|
||||||
|
'id' => (int) $assetmaintenance->asset->assetstatus->id,
|
||||||
|
'name'=> e($assetmaintenance->asset->assetstatus->name),
|
||||||
|
'status_type'=> e($assetmaintenance->asset->assetstatus->getStatuslabelType()),
|
||||||
|
'status_meta' => e($assetmaintenance->asset->present()->statusMeta),
|
||||||
|
] : null,
|
||||||
'company' => (($assetmaintenance->asset) && ($assetmaintenance->asset->company)) ? [
|
'company' => (($assetmaintenance->asset) && ($assetmaintenance->asset->company)) ? [
|
||||||
'id' => (int) $assetmaintenance->asset->company->id,
|
'id' => (int) $assetmaintenance->asset->company->id,
|
||||||
'name'=> ($assetmaintenance->asset->company->name) ? e($assetmaintenance->asset->company->name) : null,
|
'name'=> ($assetmaintenance->asset->company->name) ? e($assetmaintenance->asset->company->name) : null,
|
||||||
|
@ -64,7 +72,7 @@ class AssetMaintenancesTransformer
|
||||||
];
|
];
|
||||||
|
|
||||||
$permissions_array['available_actions'] = [
|
$permissions_array['available_actions'] = [
|
||||||
'update' => Gate::allows('update', Asset::class),
|
'update' => (Gate::allows('update', Asset::class) && ($assetmaintenance->asset->deleted_at=='')) ? true : false,
|
||||||
'delete' => Gate::allows('delete', Asset::class),
|
'delete' => Gate::allows('delete', Asset::class),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ class AssetsTransformer
|
||||||
'name'=> e($asset->model->name),
|
'name'=> e($asset->model->name),
|
||||||
] : null,
|
] : null,
|
||||||
'byod' => ($asset->byod ? true : false),
|
'byod' => ($asset->byod ? true : false),
|
||||||
|
'requestable' => ($asset->requestable ? true : false),
|
||||||
|
|
||||||
'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
|
'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 != '')) ? Carbon::parse($asset->asset_eol_date)->diffInMonths($asset->purchase_date).' months' : null,
|
||||||
|
|
|
@ -26,6 +26,7 @@ class GroupsTransformer
|
||||||
'name' => e($group->name),
|
'name' => e($group->name),
|
||||||
'permissions' => json_decode($group->permissions),
|
'permissions' => json_decode($group->permissions),
|
||||||
'users_count' => (int) $group->users_count,
|
'users_count' => (int) $group->users_count,
|
||||||
|
'created_by' => ($group->admin) ? e($group->admin->present()->fullName) : null,
|
||||||
'created_at' => Helper::getFormattedDateObject($group->created_at, 'datetime'),
|
'created_at' => Helper::getFormattedDateObject($group->created_at, 'datetime'),
|
||||||
'updated_at' => Helper::getFormattedDateObject($group->updated_at, 'datetime'),
|
'updated_at' => Helper::getFormattedDateObject($group->updated_at, 'datetime'),
|
||||||
];
|
];
|
||||||
|
|
|
@ -27,8 +27,8 @@ class LicensesTransformer
|
||||||
'company' => ($license->company) ? ['id' => (int) $license->company->id, 'name'=> e($license->company->name)] : null,
|
'company' => ($license->company) ? ['id' => (int) $license->company->id, 'name'=> e($license->company->name)] : null,
|
||||||
'manufacturer' => ($license->manufacturer) ? ['id' => (int) $license->manufacturer->id, 'name'=> e($license->manufacturer->name)] : null,
|
'manufacturer' => ($license->manufacturer) ? ['id' => (int) $license->manufacturer->id, 'name'=> e($license->manufacturer->name)] : null,
|
||||||
'product_key' => (Gate::allows('viewKeys', License::class)) ? e($license->serial) : '------------',
|
'product_key' => (Gate::allows('viewKeys', License::class)) ? e($license->serial) : '------------',
|
||||||
'order_number' => e($license->order_number),
|
'order_number' => ($license->order_number) ? e($license->order_number) : null,
|
||||||
'purchase_order' => e($license->purchase_order),
|
'purchase_order' => ($license->purchase_order) ? e($license->purchase_order) : null,
|
||||||
'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'),
|
'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'),
|
||||||
'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'),
|
'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'),
|
||||||
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
|
'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null,
|
||||||
|
@ -38,8 +38,9 @@ class LicensesTransformer
|
||||||
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
||||||
'seats' => (int) $license->seats,
|
'seats' => (int) $license->seats,
|
||||||
'free_seats_count' => (int) $license->free_seats_count,
|
'free_seats_count' => (int) $license->free_seats_count,
|
||||||
'license_name' => e($license->license_name),
|
'min_amt' => ($license->min_amt) ? (int) ($license->min_amt) : null,
|
||||||
'license_email' => e($license->license_email),
|
'license_name' => ($license->license_name) ? e($license->license_name) : null,
|
||||||
|
'license_email' => ($license->license_email) ? e($license->license_email) : null,
|
||||||
'reassignable' => ($license->reassignable == 1) ? true : false,
|
'reassignable' => ($license->reassignable == 1) ? true : false,
|
||||||
'maintained' => ($license->maintained == 1) ? true : false,
|
'maintained' => ($license->maintained == 1) ? true : false,
|
||||||
'supplier' => ($license->supplier) ? ['id' => (int) $license->supplier->id, 'name'=> e($license->supplier->name)] : null,
|
'supplier' => ($license->supplier) ? ['id' => (int) $license->supplier->id, 'name'=> e($license->supplier->name)] : null,
|
||||||
|
|
|
@ -65,6 +65,9 @@ class LocationsTransformer
|
||||||
$permissions_array['available_actions'] = [
|
$permissions_array['available_actions'] = [
|
||||||
'update' => Gate::allows('update', Location::class) ? true : false,
|
'update' => Gate::allows('update', Location::class) ? true : false,
|
||||||
'delete' => $location->isDeletable(),
|
'delete' => $location->isDeletable(),
|
||||||
|
'bulk_selectable' => [
|
||||||
|
'delete' => $location->isDeletable()
|
||||||
|
],
|
||||||
'clone' => (Gate::allows('create', Location::class) && ($location->deleted_at == '')),
|
'clone' => (Gate::allows('create', Location::class) && ($location->deleted_at == '')),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,9 @@ class AccessoryImporter extends ItemImporter
|
||||||
$this->item['min_amt'] = $this->findCsvMatch($row, "min_amt");
|
$this->item['min_amt'] = $this->findCsvMatch($row, "min_amt");
|
||||||
$accessory->fill($this->sanitizeItemForStoring($accessory));
|
$accessory->fill($this->sanitizeItemForStoring($accessory));
|
||||||
|
|
||||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
// This sets an attribute on the Loggable trait for the action log
|
||||||
// $accessory->unsetEventDispatcher();
|
$accessory->setImported(true);
|
||||||
if ($accessory->save()) {
|
if ($accessory->save()) {
|
||||||
$accessory->logCreate('Imported using CSV Importer');
|
|
||||||
$this->log('Accessory '.$this->item['name'].' was created');
|
$this->log('Accessory '.$this->item['name'].' was created');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -135,10 +135,10 @@ class AssetImporter extends ItemImporter
|
||||||
$asset->{$custom_field} = $val;
|
$asset->{$custom_field} = $val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This sets an attribute on the Loggable trait for the action log
|
||||||
|
$asset->setImported(true);
|
||||||
if ($asset->save()) {
|
if ($asset->save()) {
|
||||||
|
|
||||||
$asset->logCreate(trans('general.importer.import_note'));
|
|
||||||
$this->log('Asset '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
$this->log('Asset '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
||||||
|
|
||||||
// If we have a target to checkout to, lets do so.
|
// If we have a target to checkout to, lets do so.
|
||||||
|
|
|
@ -48,10 +48,10 @@ class ComponentImporter extends ItemImporter
|
||||||
$this->log('No matching component, creating one');
|
$this->log('No matching component, creating one');
|
||||||
$component = new Component;
|
$component = new Component;
|
||||||
$component->fill($this->sanitizeItemForStoring($component));
|
$component->fill($this->sanitizeItemForStoring($component));
|
||||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
|
||||||
$component->unsetEventDispatcher();
|
// This sets an attribute on the Loggable trait for the action log
|
||||||
|
$component->setImported(true);
|
||||||
if ($component->save()) {
|
if ($component->save()) {
|
||||||
$component->logCreate('Imported using CSV Importer');
|
|
||||||
$this->log('Component '.$this->item['name'].' was created');
|
$this->log('Component '.$this->item['name'].' was created');
|
||||||
|
|
||||||
// If we have an asset tag, checkout to that asset.
|
// If we have an asset tag, checkout to that asset.
|
||||||
|
|
|
@ -45,10 +45,10 @@ class ConsumableImporter extends ItemImporter
|
||||||
$this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number'));
|
$this->item['item_no'] = trim($this->findCsvMatch($row, 'item_number'));
|
||||||
$this->item['min_amt'] = trim($this->findCsvMatch($row, "min_amt"));
|
$this->item['min_amt'] = trim($this->findCsvMatch($row, "min_amt"));
|
||||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
|
||||||
$consumable->unsetEventDispatcher();
|
// This sets an attribute on the Loggable trait for the action log
|
||||||
|
$consumable->setImported(true);
|
||||||
if ($consumable->save()) {
|
if ($consumable->save()) {
|
||||||
$consumable->logCreate('Imported using CSV Importer');
|
|
||||||
$this->log('Consumable '.$this->item['name'].' was created');
|
$this->log('Consumable '.$this->item['name'].' was created');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -85,10 +85,10 @@ class LicenseImporter extends ItemImporter
|
||||||
} else {
|
} else {
|
||||||
$license->fill($this->sanitizeItemForStoring($license));
|
$license->fill($this->sanitizeItemForStoring($license));
|
||||||
}
|
}
|
||||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
|
||||||
// $license->unsetEventDispatcher();
|
// This sets an attribute on the Loggable trait for the action log
|
||||||
|
$license->setImported(true);
|
||||||
if ($license->save()) {
|
if ($license->save()) {
|
||||||
$license->logCreate('Imported using csv importer');
|
|
||||||
$this->log('License '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
$this->log('License '.$this->item['name'].' with serial number '.$this->item['serial'].' was created');
|
||||||
|
|
||||||
// Lets try to checkout seats if the fields exist and we have seats.
|
// Lets try to checkout seats if the fields exist and we have seats.
|
||||||
|
|
|
@ -69,9 +69,9 @@ class CheckoutableListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ClientException $e) {
|
} catch (ClientException $e) {
|
||||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error("Exception caught during checkout notification: " . $e->getMessage());
|
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class CheckoutableListener
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//slack doesn't include the url in its messaging format so this is needed to hit the endpoint
|
//slack doesn't include the url in its messaging format so this is needed to hit the endpoint
|
||||||
if(Setting::getSettings()->webhook_selected =='slack') {
|
if(Setting::getSettings()->webhook_selected =='slack' || Setting::getSettings()->webhook_selected =='general') {
|
||||||
|
|
||||||
if ($this->shouldSendWebhookNotification()) {
|
if ($this->shouldSendWebhookNotification()) {
|
||||||
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
Notification::route('slack', Setting::getSettings()->webhook_endpoint)
|
||||||
|
@ -124,9 +124,9 @@ class CheckoutableListener
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (ClientException $e) {
|
} catch (ClientException $e) {
|
||||||
Log::debug("Exception caught during checkout notification: " . $e->getMessage());
|
Log::warning("Exception caught during checkout notification: " . $e->getMessage());
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error("Exception caught during checkin notification: " . $e->getMessage());
|
Log::warning("Exception caught during checkin notification: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ class Actionlog extends SnipeModel
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
|
// This is to manually set the source (via setActionSource()) for determineActionSource()
|
||||||
|
protected ?string $source = null;
|
||||||
|
|
||||||
protected $presenter = \App\Presenters\ActionlogPresenter::class;
|
protected $presenter = \App\Presenters\ActionlogPresenter::class;
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use Presentable;
|
use Presentable;
|
||||||
|
@ -341,7 +344,12 @@ class Actionlog extends SnipeModel
|
||||||
* @since v6.3.0
|
* @since v6.3.0
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function determineActionSource() {
|
public function determineActionSource(): string
|
||||||
|
{
|
||||||
|
// This is a manually set source
|
||||||
|
if($this->source) {
|
||||||
|
return $this->source;
|
||||||
|
}
|
||||||
|
|
||||||
// This is an API call
|
// This is an API call
|
||||||
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
|
if (((request()->header('content-type') && (request()->header('accept'))=='application/json'))
|
||||||
|
@ -358,4 +366,10 @@ class Actionlog extends SnipeModel
|
||||||
return 'cli/unknown';
|
return 'cli/unknown';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manually sets $this->source for determineActionSource()
|
||||||
|
public function setActionSource($source = null): void
|
||||||
|
{
|
||||||
|
$this->source = $source;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1560,7 +1560,7 @@ class Asset extends Depreciable
|
||||||
*
|
*
|
||||||
* In short, this set of statements tells the query builder to ONLY query against an
|
* In short, this set of statements tells the query builder to ONLY query against an
|
||||||
* actual field that's being passed if it doesn't meet known relational fields. This
|
* actual field that's being passed if it doesn't meet known relational fields. This
|
||||||
* allows us to query custom fields directly in the assetsv table
|
* allows us to query custom fields directly in the assets table
|
||||||
* (regardless of their name) and *skip* any fields that we already know can only be
|
* (regardless of their name) and *skip* any fields that we already know can only be
|
||||||
* searched through relational searches that we do earlier in this method.
|
* searched through relational searches that we do earlier in this method.
|
||||||
*
|
*
|
||||||
|
|
|
@ -62,7 +62,15 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $searchableAttributes = ['title', 'notes', 'asset_maintenance_type', 'cost', 'start_date', 'completion_date'];
|
protected $searchableAttributes =
|
||||||
|
[
|
||||||
|
'title',
|
||||||
|
'notes',
|
||||||
|
'asset_maintenance_type',
|
||||||
|
'cost',
|
||||||
|
'start_date',
|
||||||
|
'completion_date'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The relations and their attributes that should be included when searching the model.
|
* The relations and their attributes that should be included when searching the model.
|
||||||
|
@ -70,9 +78,10 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $searchableRelations = [
|
protected $searchableRelations = [
|
||||||
'asset' => ['name', 'asset_tag'],
|
'asset' => ['name', 'asset_tag', 'serial'],
|
||||||
'asset.model' => ['name', 'model_number'],
|
'asset.model' => ['name', 'model_number'],
|
||||||
'asset.supplier' => ['name'],
|
'asset.supplier' => ['name'],
|
||||||
|
'asset.assetstatus' => ['name'],
|
||||||
'supplier' => ['name'],
|
'supplier' => ['name'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -197,6 +206,7 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||||
->orderBy('suppliers_maintenances.name', $order);
|
->orderBy('suppliers_maintenances.name', $order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query builder scope to order on admin user
|
* Query builder scope to order on admin user
|
||||||
*
|
*
|
||||||
|
@ -239,4 +249,33 @@ class AssetMaintenance extends Model implements ICompanyableChild
|
||||||
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
|
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
|
||||||
->orderBy('assets.name', $order);
|
->orderBy('assets.name', $order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query builder scope to order on serial
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||||
|
* @param string $order Order
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||||
|
*/
|
||||||
|
public function scopeOrderByAssetSerial($query, $order)
|
||||||
|
{
|
||||||
|
return $query->leftJoin('assets', 'asset_maintenances.asset_id', '=', 'assets.id')
|
||||||
|
->orderBy('assets.serial', $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query builder scope to order on status label name
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||||
|
* @param text $order Order
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||||
|
*/
|
||||||
|
public function scopeOrderStatusName($query, $order)
|
||||||
|
{
|
||||||
|
return $query->join('assets as maintained_asset', 'asset_maintenances.asset_id', '=', 'maintained_asset.id')
|
||||||
|
->leftjoin('status_labels as maintained_asset_status', 'maintained_asset_status.id', '=', 'maintained_asset.status_id')
|
||||||
|
->orderBy('maintained_asset_status.name', $order);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,18 @@ class Group extends SnipeModel
|
||||||
return $this->belongsToMany(\App\Models\User::class, 'users_groups');
|
return $this->belongsToMany(\App\Models\User::class, 'users_groups');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user that created the group
|
||||||
|
*
|
||||||
|
* @author A. Gianotto <snipe@snipe.net>
|
||||||
|
* @since [v6.3.0]
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
|
*/
|
||||||
|
public function admin()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(\App\Models\User::class, 'created_by');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode JSON permissions into array
|
* Decode JSON permissions into array
|
||||||
*
|
*
|
||||||
|
|
|
@ -53,6 +53,7 @@ class License extends Depreciable
|
||||||
'purchase_date' => 'date_format:Y-m-d|nullable|max:10',
|
'purchase_date' => 'date_format:Y-m-d|nullable|max:10',
|
||||||
'expiration_date' => 'date_format:Y-m-d|nullable|max:10',
|
'expiration_date' => 'date_format:Y-m-d|nullable|max:10',
|
||||||
'termination_date' => 'date_format:Y-m-d|nullable|max:10',
|
'termination_date' => 'date_format:Y-m-d|nullable|max:10',
|
||||||
|
'min_amt' => 'numeric|nullable|gte:0',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +82,7 @@ class License extends Depreciable
|
||||||
'supplier_id',
|
'supplier_id',
|
||||||
'termination_date',
|
'termination_date',
|
||||||
'user_id',
|
'user_id',
|
||||||
|
'min_amt',
|
||||||
];
|
];
|
||||||
|
|
||||||
use Searchable;
|
use Searchable;
|
||||||
|
|
|
@ -95,7 +95,10 @@ class Location extends SnipeModel
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether or not this location can be deleted
|
* Determine whether or not this location can be deleted.
|
||||||
|
*
|
||||||
|
* This method requires the eager loading of the relationships in order to determine whether
|
||||||
|
* it can be deleted. It's tempting to load those here, but that increases the query load considerably.
|
||||||
*
|
*
|
||||||
* @author A. Gianotto <snipe@snipe.net>
|
* @author A. Gianotto <snipe@snipe.net>
|
||||||
* @since [v3.0]
|
* @since [v3.0]
|
||||||
|
@ -104,9 +107,10 @@ class Location extends SnipeModel
|
||||||
public function isDeletable()
|
public function isDeletable()
|
||||||
{
|
{
|
||||||
return Gate::allows('delete', $this)
|
return Gate::allows('delete', $this)
|
||||||
&& ($this->assignedAssets()->count() === 0)
|
&& ($this->assets_count === 0)
|
||||||
&& ($this->assets()->count() === 0)
|
&& ($this->assigned_assets_count === 0)
|
||||||
&& ($this->users()->count() === 0);
|
&& ($this->children_count === 0)
|
||||||
|
&& ($this->users_count === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +8,9 @@ use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
trait Loggable
|
trait Loggable
|
||||||
{
|
{
|
||||||
|
// an attribute for setting whether or not the item was imported
|
||||||
|
public ?bool $imported = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
||||||
* @since [v3.4]
|
* @since [v3.4]
|
||||||
|
@ -18,6 +21,11 @@ trait Loggable
|
||||||
return $this->morphMany(Actionlog::class, 'item');
|
return $this->morphMany(Actionlog::class, 'item');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setImported(bool $bool): void
|
||||||
|
{
|
||||||
|
$this->imported = $bool;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
* @author Daniel Meltzer <dmeltzer.devel@gmail.com>
|
||||||
* @since [v3.4]
|
* @since [v3.4]
|
||||||
|
|
|
@ -67,6 +67,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||||
'gravatar',
|
'gravatar',
|
||||||
'vip',
|
'vip',
|
||||||
'autoassign_licenses',
|
'autoassign_licenses',
|
||||||
|
'website',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
@ -120,6 +121,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||||
'phone',
|
'phone',
|
||||||
'jobtitle',
|
'jobtitle',
|
||||||
'employee_num',
|
'employee_num',
|
||||||
|
'website',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -63,34 +63,8 @@ class CheckinAccessoryNotification extends Notification
|
||||||
if ($this->target instanceof User && $this->target->email != '') {
|
if ($this->target instanceof User && $this->target->email != '') {
|
||||||
\Log::debug('The target is a user');
|
\Log::debug('The target is a user');
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an email if the asset requires acceptance,
|
|
||||||
* so the user can accept or decline the asset
|
|
||||||
*/
|
|
||||||
if (($this->item->requireAcceptance()) || ($this->item->getEula()) || ($this->item->checkin_email())) {
|
|
||||||
$notifyBy[] = 'mail';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an email if the asset requires acceptance,
|
|
||||||
* so the user can accept or decline the asset
|
|
||||||
*/
|
|
||||||
if ($this->item->requireAcceptance()) {
|
|
||||||
\Log::debug('This accessory requires acceptance');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an email if the item has a EULA, since the user should always receive it
|
|
||||||
*/
|
|
||||||
if ($this->item->getEula()) {
|
|
||||||
\Log::debug('This accessory has a EULA');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an email if an email should be sent at checkin/checkout
|
|
||||||
*/
|
|
||||||
if ($this->item->checkin_email()) {
|
if ($this->item->checkin_email()) {
|
||||||
\Log::debug('This accessory has a checkin_email()');
|
$notifyBy[] = 'mail';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ class AccessoryObserver
|
||||||
$logAction->item_id = $accessory->id;
|
$logAction->item_id = $accessory->id;
|
||||||
$logAction->created_at = date('Y-m-d H:i:s');
|
$logAction->created_at = date('Y-m-d H:i:s');
|
||||||
$logAction->user_id = Auth::id();
|
$logAction->user_id = Auth::id();
|
||||||
|
if($accessory->imported) {
|
||||||
|
$logAction->setActionSource('importer');
|
||||||
|
}
|
||||||
$logAction->logaction('create');
|
$logAction->logaction('create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,9 @@ class AssetObserver
|
||||||
$logAction->item_id = $asset->id;
|
$logAction->item_id = $asset->id;
|
||||||
$logAction->created_at = date('Y-m-d H:i:s');
|
$logAction->created_at = date('Y-m-d H:i:s');
|
||||||
$logAction->user_id = Auth::id();
|
$logAction->user_id = Auth::id();
|
||||||
|
if($asset->imported) {
|
||||||
|
$logAction->setActionSource('importer');
|
||||||
|
}
|
||||||
$logAction->logaction('create');
|
$logAction->logaction('create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ class ComponentObserver
|
||||||
$logAction->item_id = $component->id;
|
$logAction->item_id = $component->id;
|
||||||
$logAction->created_at = date('Y-m-d H:i:s');
|
$logAction->created_at = date('Y-m-d H:i:s');
|
||||||
$logAction->user_id = Auth::id();
|
$logAction->user_id = Auth::id();
|
||||||
|
if($component->imported) {
|
||||||
|
$logAction->setActionSource('importer');
|
||||||
|
}
|
||||||
$logAction->logaction('create');
|
$logAction->logaction('create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ class ConsumableObserver
|
||||||
$logAction->item_id = $consumable->id;
|
$logAction->item_id = $consumable->id;
|
||||||
$logAction->created_at = date('Y-m-d H:i:s');
|
$logAction->created_at = date('Y-m-d H:i:s');
|
||||||
$logAction->user_id = Auth::id();
|
$logAction->user_id = Auth::id();
|
||||||
|
if($consumable->imported) {
|
||||||
|
$logAction->setActionSource('importer');
|
||||||
|
}
|
||||||
$logAction->logaction('create');
|
$logAction->logaction('create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ class LicenseObserver
|
||||||
$logAction->item_id = $license->id;
|
$logAction->item_id = $license->id;
|
||||||
$logAction->created_at = date('Y-m-d H:i:s');
|
$logAction->created_at = date('Y-m-d H:i:s');
|
||||||
$logAction->user_id = Auth::id();
|
$logAction->user_id = Auth::id();
|
||||||
|
if($license->imported) {
|
||||||
|
$logAction->setActionSource('importer');
|
||||||
|
}
|
||||||
$logAction->logaction('create');
|
$logAction->logaction('create');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,19 @@ class AssetMaintenancesPresenter extends Presenter
|
||||||
'sortable' => true,
|
'sortable' => true,
|
||||||
'title' => trans('admin/hardware/table.asset_tag'),
|
'title' => trans('admin/hardware/table.asset_tag'),
|
||||||
'formatter' => 'assetTagLinkFormatter',
|
'formatter' => 'assetTagLinkFormatter',
|
||||||
|
], [
|
||||||
|
'field' => 'serial',
|
||||||
|
'searchable' => true,
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => trans('admin/hardware/table.serial'),
|
||||||
|
'formatter' => 'assetSerialLinkFormatter',
|
||||||
|
], [
|
||||||
|
'field' => 'status_label',
|
||||||
|
'searchable' => true,
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => trans('admin/hardware/table.status'),
|
||||||
|
'visible' => true,
|
||||||
|
'formatter' => 'statuslabelsLinkObjFormatter',
|
||||||
], [
|
], [
|
||||||
'field' => 'model',
|
'field' => 'model',
|
||||||
'searchable' => true,
|
'searchable' => true,
|
||||||
|
|
|
@ -195,6 +195,14 @@ class AssetPresenter extends Presenter
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
'title' => trans('admin/hardware/form.warranty_expires'),
|
'title' => trans('admin/hardware/form.warranty_expires'),
|
||||||
'formatter' => 'dateDisplayFormatter',
|
'formatter' => 'dateDisplayFormatter',
|
||||||
|
], [
|
||||||
|
'field' => 'requestable',
|
||||||
|
'searchable' => false,
|
||||||
|
'sortable' => true,
|
||||||
|
'visible' => false,
|
||||||
|
'title' => trans('admin/hardware/general.requestable'),
|
||||||
|
'formatter' => 'trueFalseFormatter',
|
||||||
|
|
||||||
], [
|
], [
|
||||||
'field' => 'notes',
|
'field' => 'notes',
|
||||||
'searchable' => true,
|
'searchable' => true,
|
||||||
|
|
|
@ -89,7 +89,14 @@ class LicensePresenter extends Presenter
|
||||||
'searchable' => false,
|
'searchable' => false,
|
||||||
'sortable' => true,
|
'sortable' => true,
|
||||||
'title' => trans('admin/accessories/general.remaining'),
|
'title' => trans('admin/accessories/general.remaining'),
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
|
'field' => 'min_amt',
|
||||||
|
'searchable' => false,
|
||||||
|
'sortable' => true,
|
||||||
|
'title' => trans('mail.min_QTY'),
|
||||||
|
'formatter' => 'minAmtFormatter',
|
||||||
|
],[
|
||||||
'field' => 'purchase_date',
|
'field' => 'purchase_date',
|
||||||
'searchable' => true,
|
'searchable' => true,
|
||||||
'sortable' => true,
|
'sortable' => true,
|
||||||
|
|
|
@ -14,7 +14,11 @@ class LocationPresenter extends Presenter
|
||||||
public static function dataTableLayout()
|
public static function dataTableLayout()
|
||||||
{
|
{
|
||||||
$layout = [
|
$layout = [
|
||||||
|
[
|
||||||
|
'field' => 'bulk_selectable',
|
||||||
|
'checkbox' => true,
|
||||||
|
'formatter' => 'checkboxEnabledFormatter',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'field' => 'id',
|
'field' => 'id',
|
||||||
'searchable' => false,
|
'searchable' => false,
|
||||||
|
|
|
@ -122,6 +122,15 @@ class UserPresenter extends Presenter
|
||||||
'visible' => true,
|
'visible' => true,
|
||||||
'formatter' => 'phoneFormatter',
|
'formatter' => 'phoneFormatter',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'field' => 'website',
|
||||||
|
'searchable' => true,
|
||||||
|
'sortable' => true,
|
||||||
|
'switchable' => true,
|
||||||
|
'title' => trans('general.website'),
|
||||||
|
'visible' => false,
|
||||||
|
'formatter' => 'externalLinkFormatter',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'field' => 'address',
|
'field' => 'address',
|
||||||
'searchable' => true,
|
'searchable' => true,
|
||||||
|
|
|
@ -39,24 +39,12 @@ class SettingsServiceProvider extends ServiceProvider
|
||||||
$limit = abs($int_limit);
|
$limit = abs($int_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// \Log::debug('Max in env: '.config('app.max_results'));
|
|
||||||
// \Log::debug('Original requested limit: '.request('limit'));
|
|
||||||
// \Log::debug('Int limit: '.$int_limit);
|
|
||||||
// \Log::debug('Modified limit: '.$limit);
|
|
||||||
// \Log::debug('------------------------------');
|
|
||||||
|
|
||||||
|
|
||||||
return $limit;
|
return $limit;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure the offset is actually set and is an integer
|
// Make sure the offset is actually set and is an integer
|
||||||
\App::singleton('api_offset_value', function () {
|
\App::singleton('api_offset_value', function () {
|
||||||
$offset = intval(request('offset'));
|
$offset = intval(request('offset'));
|
||||||
// \Log::debug('Original requested offset: '.request('offset'));
|
|
||||||
// \Log::debug('Modified offset: '.$offset);
|
|
||||||
// \Log::debug('------------------------------');
|
|
||||||
|
|
||||||
|
|
||||||
return $offset;
|
return $offset;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -90,13 +90,9 @@ class Label implements View
|
||||||
$assetData->put('id', $asset->id);
|
$assetData->put('id', $asset->id);
|
||||||
$assetData->put('tag', $asset->asset_tag);
|
$assetData->put('tag', $asset->asset_tag);
|
||||||
|
|
||||||
if ($template->getSupportTitle()) {
|
if ($template->getSupportTitle() && !empty($settings->label2_title)) {
|
||||||
|
$title = str_replace('{COMPANY}', data_get($asset, 'company.name'), $settings->label2_title);
|
||||||
if ($asset->company && !empty($settings->label2_title)) {
|
$assetData->put('title', $title);
|
||||||
$title = str_replace('{COMPANY}', $asset->company->name, $settings->label2_title);
|
|
||||||
$settings->qr_text;
|
|
||||||
$assetData->put('title', $title);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($template->getSupportLogo()) {
|
if ($template->getSupportLogo()) {
|
||||||
|
@ -216,4 +212,4 @@ class Label implements View
|
||||||
return self::NAME;
|
return self::NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,8 @@
|
||||||
"watson/validating": "^6.1"
|
"watson/validating": "^6.1"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-ldap": "*"
|
"ext-ldap": "*",
|
||||||
|
"ext-zip": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"brianium/paratest": "^6.6",
|
"brianium/paratest": "^6.6",
|
||||||
|
|
56
composer.lock
generated
56
composer.lock
generated
|
@ -4804,16 +4804,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/oauth2-server",
|
"name": "league/oauth2-server",
|
||||||
"version": "8.3.5",
|
"version": "8.4.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/oauth2-server.git",
|
"url": "https://github.com/thephpleague/oauth2-server.git",
|
||||||
"reference": "7aeb7c42b463b1a6fe4d084d3145e2fa22436876"
|
"reference": "007dc5f6c0151a73b133fec36c9686cc956209d3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/7aeb7c42b463b1a6fe4d084d3145e2fa22436876",
|
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/007dc5f6c0151a73b133fec36c9686cc956209d3",
|
||||||
"reference": "7aeb7c42b463b1a6fe4d084d3145e2fa22436876",
|
"reference": "007dc5f6c0151a73b133fec36c9686cc956209d3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4880,7 +4880,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/thephpleague/oauth2-server/issues",
|
"issues": "https://github.com/thephpleague/oauth2-server/issues",
|
||||||
"source": "https://github.com/thephpleague/oauth2-server/tree/8.3.5"
|
"source": "https://github.com/thephpleague/oauth2-server/tree/8.4.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -4888,7 +4888,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-03T21:21:28+00:00"
|
"time": "2023-08-02T22:54:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/uri",
|
"name": "league/uri",
|
||||||
|
@ -6701,16 +6701,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phenx/php-svg-lib",
|
"name": "phenx/php-svg-lib",
|
||||||
"version": "0.5.1",
|
"version": "0.5.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/dompdf/php-svg-lib.git",
|
"url": "https://github.com/dompdf/php-svg-lib.git",
|
||||||
"reference": "8a8a1ebcf6aea861ef30197999f096f7bd4b4456"
|
"reference": "732faa9fb4309221e2bd9b2fda5de44f947133aa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/8a8a1ebcf6aea861ef30197999f096f7bd4b4456",
|
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/732faa9fb4309221e2bd9b2fda5de44f947133aa",
|
||||||
"reference": "8a8a1ebcf6aea861ef30197999f096f7bd4b4456",
|
"reference": "732faa9fb4309221e2bd9b2fda5de44f947133aa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -6741,9 +6741,9 @@
|
||||||
"homepage": "https://github.com/PhenX/php-svg-lib",
|
"homepage": "https://github.com/PhenX/php-svg-lib",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/dompdf/php-svg-lib/issues",
|
"issues": "https://github.com/dompdf/php-svg-lib/issues",
|
||||||
"source": "https://github.com/dompdf/php-svg-lib/tree/0.5.1"
|
"source": "https://github.com/dompdf/php-svg-lib/tree/0.5.2"
|
||||||
},
|
},
|
||||||
"time": "2023-12-11T20:56:08+00:00"
|
"time": "2024-02-07T12:49:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "php-http/message-factory",
|
"name": "php-http/message-factory",
|
||||||
|
@ -9669,16 +9669,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-kernel",
|
"name": "symfony/http-kernel",
|
||||||
"version": "v5.4.10",
|
"version": "v5.4.20",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-kernel.git",
|
"url": "https://github.com/symfony/http-kernel.git",
|
||||||
"reference": "255ae3b0a488d78fbb34da23d3e0c059874b5948"
|
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/255ae3b0a488d78fbb34da23d3e0c059874b5948",
|
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
|
||||||
"reference": "255ae3b0a488d78fbb34da23d3e0c059874b5948",
|
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -9761,7 +9761,7 @@
|
||||||
"description": "Provides a structured process for converting a Request into a Response",
|
"description": "Provides a structured process for converting a Request into a Response",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-kernel/tree/v5.4.10"
|
"source": "https://github.com/symfony/http-kernel/tree/v5.4.20"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -9777,7 +9777,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-06-26T16:57:59+00:00"
|
"time": "2023-02-01T08:18:48+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/mime",
|
"name": "symfony/mime",
|
||||||
|
@ -12647,16 +12647,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/composer",
|
"name": "composer/composer",
|
||||||
"version": "2.6.6",
|
"version": "2.7.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/composer.git",
|
"url": "https://github.com/composer/composer.git",
|
||||||
"reference": "683557bd2466072777309d039534bb1332d0dda5"
|
"reference": "aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/composer/zipball/683557bd2466072777309d039534bb1332d0dda5",
|
"url": "https://api.github.com/repos/composer/composer/zipball/aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc",
|
||||||
"reference": "683557bd2466072777309d039534bb1332d0dda5",
|
"reference": "aaf6ed5ccd27c23f79a545e351b4d7842a99d0bc",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -12674,7 +12674,7 @@
|
||||||
"seld/jsonlint": "^1.4",
|
"seld/jsonlint": "^1.4",
|
||||||
"seld/phar-utils": "^1.2",
|
"seld/phar-utils": "^1.2",
|
||||||
"seld/signal-handler": "^2.0",
|
"seld/signal-handler": "^2.0",
|
||||||
"symfony/console": "^5.4.11 || ^6.0.11",
|
"symfony/console": "^5.4.11 || ^6.0.11 || ^7",
|
||||||
"symfony/filesystem": "^5.4 || ^6.0 || ^7",
|
"symfony/filesystem": "^5.4 || ^6.0 || ^7",
|
||||||
"symfony/finder": "^5.4 || ^6.0 || ^7",
|
"symfony/finder": "^5.4 || ^6.0 || ^7",
|
||||||
"symfony/polyfill-php73": "^1.24",
|
"symfony/polyfill-php73": "^1.24",
|
||||||
|
@ -12688,7 +12688,7 @@
|
||||||
"phpstan/phpstan-phpunit": "^1.0",
|
"phpstan/phpstan-phpunit": "^1.0",
|
||||||
"phpstan/phpstan-strict-rules": "^1",
|
"phpstan/phpstan-strict-rules": "^1",
|
||||||
"phpstan/phpstan-symfony": "^1.2.10",
|
"phpstan/phpstan-symfony": "^1.2.10",
|
||||||
"symfony/phpunit-bridge": "^6.0 || ^7"
|
"symfony/phpunit-bridge": "^6.4.1 || ^7.0.1"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
|
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
|
||||||
|
@ -12701,7 +12701,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "2.6-dev"
|
"dev-main": "2.7-dev"
|
||||||
},
|
},
|
||||||
"phpstan": {
|
"phpstan": {
|
||||||
"includes": [
|
"includes": [
|
||||||
|
@ -12741,7 +12741,7 @@
|
||||||
"irc": "ircs://irc.libera.chat:6697/composer",
|
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||||
"issues": "https://github.com/composer/composer/issues",
|
"issues": "https://github.com/composer/composer/issues",
|
||||||
"security": "https://github.com/composer/composer/security/policy",
|
"security": "https://github.com/composer/composer/security/policy",
|
||||||
"source": "https://github.com/composer/composer/tree/2.6.6"
|
"source": "https://github.com/composer/composer/tree/2.7.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -12757,7 +12757,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-12-08T17:32:26+00:00"
|
"time": "2024-02-09T14:26:28+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/metadata-minifier",
|
"name": "composer/metadata-minifier",
|
||||||
|
@ -17088,5 +17088,5 @@
|
||||||
"ext-pdo": "*"
|
"ext-pdo": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.3.0"
|
"plugin-api-version": "2.6.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
return array (
|
return array (
|
||||||
'app_version' => 'v6.3.0',
|
'app_version' => 'v6.3.1',
|
||||||
'full_app_version' => 'v6.3.0 - build 12490-g9136415bb',
|
'full_app_version' => 'v6.3.1 - build 12672-g00cea3eb3',
|
||||||
'build_version' => '12490',
|
'build_version' => '12672',
|
||||||
'prerelease_version' => '',
|
'prerelease_version' => '',
|
||||||
'hash_version' => 'g9136415bb',
|
'hash_version' => 'g00cea3eb3',
|
||||||
'full_hash' => 'v6.3.0-729-g9136415bb',
|
'full_hash' => 'v6.3.1-180-g00cea3eb3',
|
||||||
'branch' => 'develop',
|
'branch' => 'master',
|
||||||
);
|
);
|
|
@ -8,6 +8,7 @@ use App\Models\Location;
|
||||||
use App\Models\Manufacturer;
|
use App\Models\Manufacturer;
|
||||||
use App\Models\Supplier;
|
use App\Models\Supplier;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
class AccessoryFactory extends Factory
|
class AccessoryFactory extends Factory
|
||||||
|
@ -140,4 +141,16 @@ class AccessoryFactory extends Factory
|
||||||
$accessory->category->update(['require_acceptance' => 1]);
|
$accessory->category->update(['require_acceptance' => 1]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkedOutToUser(User $user = null)
|
||||||
|
{
|
||||||
|
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
||||||
|
$accessory->users()->attach($accessory->id, [
|
||||||
|
'accessory_id' => $accessory->id,
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'user_id' => 1,
|
||||||
|
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ use App\Models\Location;
|
||||||
use App\Models\Statuslabel;
|
use App\Models\Statuslabel;
|
||||||
use App\Models\Supplier;
|
use App\Models\Supplier;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
@ -289,12 +288,13 @@ class AssetFactory extends Factory
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assignedToUser()
|
public function assignedToUser(User $user = null)
|
||||||
{
|
{
|
||||||
return $this->state(function () {
|
return $this->state(function () use ($user) {
|
||||||
return [
|
return [
|
||||||
'assigned_to' => User::factory(),
|
'assigned_to' => $user->id ?? User::factory(),
|
||||||
'assigned_type' => User::class,
|
'assigned_type' => User::class,
|
||||||
|
'last_checkout' => now()->subDay(),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -352,4 +352,19 @@ class AssetFactory extends Factory
|
||||||
{
|
{
|
||||||
return $this->state(['requestable' => false]);
|
return $this->state(['requestable' => false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This allows bypassing model level validation if you want to purposefully
|
||||||
|
* create an asset in an invalid state. Validation is turned back on
|
||||||
|
* after the model is created via the factory.
|
||||||
|
* @return AssetFactory
|
||||||
|
*/
|
||||||
|
public function canBeInvalidUponCreation()
|
||||||
|
{
|
||||||
|
return $this->afterMaking(function (Asset $asset) {
|
||||||
|
$asset->setValidating(false);
|
||||||
|
})->afterCreating(function (Asset $asset) {
|
||||||
|
$asset->setValidating(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ class DepartmentFactory extends Factory
|
||||||
public function definition()
|
public function definition()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => $this->faker->word() . ' Department',
|
'name' => $this->faker->unique()->word() . ' Department',
|
||||||
'user_id' => User::factory()->superuser(),
|
'user_id' => User::factory()->superuser(),
|
||||||
'location_id' => Location::factory(),
|
'location_id' => Location::factory(),
|
||||||
];
|
];
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Database\Factories;
|
namespace Database\Factories;
|
||||||
|
|
||||||
use App\Models\License;
|
use App\Models\License;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
class LicenseSeatFactory extends Factory
|
class LicenseSeatFactory extends Factory
|
||||||
|
@ -13,4 +14,13 @@ class LicenseSeatFactory extends Factory
|
||||||
'license_id' => License::factory(),
|
'license_id' => License::factory(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function assignedToUser(User $user = null)
|
||||||
|
{
|
||||||
|
return $this->state(function () use ($user) {
|
||||||
|
return [
|
||||||
|
'assigned_to' => $user->id ?? User::factory(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddCreatedByToPermissionGroups extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('permission_groups', function (Blueprint $table) {
|
||||||
|
$table->integer('created_by')->nullable()->default(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('permission_groups', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('permission_groups', 'created_by')) {
|
||||||
|
$table->dropColumn('created_by');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddMinQtyToLicenses extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('licenses', function (Blueprint $table) {
|
||||||
|
$table->integer('min_amt')->nullable()->default(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('licenses', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('licenses', 'min_amt')) {
|
||||||
|
$table->dropColumn('min_amt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
93
package-lock.json
generated
93
package-lock.json
generated
|
@ -1565,9 +1565,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/eslint": {
|
"@types/eslint": {
|
||||||
"version": "8.56.1",
|
"version": "8.56.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
|
||||||
"integrity": "sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ==",
|
"integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/estree": "*",
|
"@types/estree": "*",
|
||||||
"@types/json-schema": "*"
|
"@types/json-schema": "*"
|
||||||
|
@ -2350,9 +2350,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"alpinejs": {
|
"alpinejs": {
|
||||||
"version": "3.13.3",
|
"version": "3.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.5.tgz",
|
||||||
"integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==",
|
"integrity": "sha512-1d2XeNGN+Zn7j4mUAKXtAgdc4/rLeadyTMWeJGXF5DzwawPBxwTiBhFFm6w/Ei8eJxUZeyNWWSD9zknfdz1kEw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/reactivity": "~3.1.1"
|
"@vue/reactivity": "~3.1.1"
|
||||||
}
|
}
|
||||||
|
@ -3611,6 +3611,7 @@
|
||||||
"version": "4.19.1",
|
"version": "4.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
|
||||||
"integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
|
"integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"caniuse-lite": "^1.0.30001286",
|
"caniuse-lite": "^1.0.30001286",
|
||||||
"electron-to-chromium": "^1.4.17",
|
"electron-to-chromium": "^1.4.17",
|
||||||
|
@ -3731,7 +3732,8 @@
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001292",
|
"version": "1.0.30001292",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz",
|
||||||
"integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw=="
|
"integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"canvg": {
|
"canvg": {
|
||||||
"version": "3.0.10",
|
"version": "3.0.10",
|
||||||
|
@ -4931,7 +4933,8 @@
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.4.28",
|
"version": "1.4.28",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz",
|
||||||
"integrity": "sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg=="
|
"integrity": "sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
|
@ -17418,7 +17421,8 @@
|
||||||
"node-releases": {
|
"node-releases": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
|
||||||
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA=="
|
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"normalize-path": {
|
"normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
@ -20097,18 +20101,18 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"webpack": {
|
"webpack": {
|
||||||
"version": "5.89.0",
|
"version": "5.90.1",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz",
|
||||||
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
|
"integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/eslint-scope": "^3.7.3",
|
"@types/eslint-scope": "^3.7.3",
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.5",
|
||||||
"@webassemblyjs/ast": "^1.11.5",
|
"@webassemblyjs/ast": "^1.11.5",
|
||||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||||
"acorn": "^8.7.1",
|
"acorn": "^8.7.1",
|
||||||
"acorn-import-assertions": "^1.9.0",
|
"acorn-import-assertions": "^1.9.0",
|
||||||
"browserslist": "^4.14.5",
|
"browserslist": "^4.21.10",
|
||||||
"chrome-trace-event": "^1.0.2",
|
"chrome-trace-event": "^1.0.2",
|
||||||
"enhanced-resolve": "^5.15.0",
|
"enhanced-resolve": "^5.15.0",
|
||||||
"es-module-lexer": "^1.2.1",
|
"es-module-lexer": "^1.2.1",
|
||||||
|
@ -20122,15 +20126,15 @@
|
||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"schema-utils": "^3.2.0",
|
"schema-utils": "^3.2.0",
|
||||||
"tapable": "^2.1.1",
|
"tapable": "^2.1.1",
|
||||||
"terser-webpack-plugin": "^5.3.7",
|
"terser-webpack-plugin": "^5.3.10",
|
||||||
"watchpack": "^2.4.0",
|
"watchpack": "^2.4.0",
|
||||||
"webpack-sources": "^3.2.3"
|
"webpack-sources": "^3.2.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": {
|
"@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA=="
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
|
||||||
},
|
},
|
||||||
"@jridgewell/sourcemap-codec": {
|
"@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.15",
|
"version": "1.4.15",
|
||||||
|
@ -20138,9 +20142,9 @@
|
||||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||||
},
|
},
|
||||||
"@jridgewell/trace-mapping": {
|
"@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.20",
|
"version": "0.3.22",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
|
||||||
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
|
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
|
@ -20151,16 +20155,42 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
||||||
},
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"version": "4.23.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
|
||||||
|
"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
|
||||||
|
"requires": {
|
||||||
|
"caniuse-lite": "^1.0.30001587",
|
||||||
|
"electron-to-chromium": "^1.4.668",
|
||||||
|
"node-releases": "^2.0.14",
|
||||||
|
"update-browserslist-db": "^1.0.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"caniuse-lite": {
|
||||||
|
"version": "1.0.30001587",
|
||||||
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz",
|
||||||
|
"integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA=="
|
||||||
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.20.3",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||||
},
|
},
|
||||||
|
"electron-to-chromium": {
|
||||||
|
"version": "1.4.670",
|
||||||
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.670.tgz",
|
||||||
|
"integrity": "sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A=="
|
||||||
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||||
},
|
},
|
||||||
|
"node-releases": {
|
||||||
|
"version": "2.0.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||||
|
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
|
||||||
|
},
|
||||||
"schema-utils": {
|
"schema-utils": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||||
|
@ -20172,17 +20202,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serialize-javascript": {
|
"serialize-javascript": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||||
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
|
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"randombytes": "^2.1.0"
|
"randombytes": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"terser": {
|
"terser": {
|
||||||
"version": "5.26.0",
|
"version": "5.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
|
||||||
"integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==",
|
"integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.8.2",
|
"acorn": "^8.8.2",
|
||||||
|
@ -20202,6 +20232,15 @@
|
||||||
"terser": "^5.26.0"
|
"terser": "^5.26.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"update-browserslist-db": {
|
||||||
|
"version": "1.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||||
|
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||||
|
"requires": {
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"picocolors": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"webpack-sources": {
|
"webpack-sources": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
"acorn-import-assertions": "^1.9.0",
|
"acorn-import-assertions": "^1.9.0",
|
||||||
"admin-lte": "^2.4.18",
|
"admin-lte": "^2.4.18",
|
||||||
"ajv": "^6.12.6",
|
"ajv": "^6.12.6",
|
||||||
"alpinejs": "^3.13.3",
|
"alpinejs": "^3.13.5",
|
||||||
"blueimp-file-upload": "^9.34.0",
|
"blueimp-file-upload": "^9.34.0",
|
||||||
"bootstrap": "^3.4.1",
|
"bootstrap": "^3.4.1",
|
||||||
"bootstrap-colorpicker": "^2.5.3",
|
"bootstrap-colorpicker": "^2.5.3",
|
||||||
|
@ -59,6 +59,6 @@
|
||||||
"tableexport.jquery.plugin": "1.28.0",
|
"tableexport.jquery.plugin": "1.28.0",
|
||||||
"tether": "^1.4.0",
|
"tether": "^1.4.0",
|
||||||
"vue-resource": "^1.5.2",
|
"vue-resource": "^1.5.2",
|
||||||
"webpack": "^5.89.0"
|
"webpack": "^5.90.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/css/dist/all.css
vendored
BIN
public/css/dist/all.css
vendored
Binary file not shown.
BIN
public/css/dist/bootstrap-table.css
vendored
BIN
public/css/dist/bootstrap-table.css
vendored
Binary file not shown.
BIN
public/css/dist/signature-pad.min.css
vendored
BIN
public/css/dist/signature-pad.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.css
vendored
BIN
public/css/dist/skins/skin-black-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
BIN
public/css/dist/skins/skin-black-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black.css
vendored
BIN
public/css/dist/skins/skin-black.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-black.min.css
vendored
BIN
public/css/dist/skins/skin-black.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue-dark.css
vendored
BIN
public/css/dist/skins/skin-blue-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue-dark.min.css
vendored
BIN
public/css/dist/skins/skin-blue-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue.css
vendored
BIN
public/css/dist/skins/skin-blue.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-blue.min.css
vendored
BIN
public/css/dist/skins/skin-blue.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-contrast.css
vendored
BIN
public/css/dist/skins/skin-contrast.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-contrast.min.css
vendored
BIN
public/css/dist/skins/skin-contrast.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green-dark.css
vendored
BIN
public/css/dist/skins/skin-green-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green-dark.min.css
vendored
BIN
public/css/dist/skins/skin-green-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green.css
vendored
BIN
public/css/dist/skins/skin-green.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-green.min.css
vendored
BIN
public/css/dist/skins/skin-green.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange-dark.css
vendored
BIN
public/css/dist/skins/skin-orange-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange-dark.min.css
vendored
BIN
public/css/dist/skins/skin-orange-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange.css
vendored
BIN
public/css/dist/skins/skin-orange.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-orange.min.css
vendored
BIN
public/css/dist/skins/skin-orange.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple-dark.css
vendored
BIN
public/css/dist/skins/skin-purple-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple-dark.min.css
vendored
BIN
public/css/dist/skins/skin-purple-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple.css
vendored
BIN
public/css/dist/skins/skin-purple.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-purple.min.css
vendored
BIN
public/css/dist/skins/skin-purple.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red-dark.css
vendored
BIN
public/css/dist/skins/skin-red-dark.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red-dark.min.css
vendored
BIN
public/css/dist/skins/skin-red-dark.min.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red.css
vendored
BIN
public/css/dist/skins/skin-red.css
vendored
Binary file not shown.
BIN
public/css/dist/skins/skin-red.min.css
vendored
BIN
public/css/dist/skins/skin-red.min.css
vendored
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue