Compare commits
225 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85486fa58c | |||
| 10dc1f1368 | |||
| 3618df2ec5 | |||
| 8f7bce7aad | |||
| a201f22fdb | |||
| 6070d530cf | |||
| 4d66f7c93f | |||
| 6c0cf94473 | |||
| 381003eeab | |||
| c3d4c821f3 | |||
| d8038ac483 | |||
| d268f25f16 | |||
| 01c69c8f8f | |||
| 3e29457094 | |||
| 0b3ac2a9cd | |||
| bf4b915168 | |||
| 0f1e5181a6 | |||
| 0a9c2e354f | |||
| e544007185 | |||
| 4b690e8e1f | |||
| 371afde52d | |||
| 7a6a2e3d92 | |||
| 3f8ac59f4c | |||
| 74e7b1cfa5 | |||
| ae0a6d66f3 | |||
| 321839e074 | |||
| e9fc455a30 | |||
| 8af4126de1 | |||
| 47f1b3b14e | |||
| 3044af9410 | |||
| 4f0f0af83b | |||
| 198afee946 | |||
| def0af652d | |||
| 20e4ba0590 | |||
| 72fd9977e5 | |||
| 10f35c682b | |||
| 198b76ebc2 | |||
| 6e84c29ce4 | |||
| 389a06aac5 | |||
| 4b96721393 | |||
| e42ee0c0e1 | |||
| 277a0fd16a | |||
| c163d6774f | |||
| 5546f9676a | |||
| 9e73eaf955 | |||
| 1b2094f4e2 | |||
| 46caa478d2 | |||
| e6106aa7cf | |||
| b45a8f4b5f | |||
| 4d8569e754 | |||
| c9f8a84d48 | |||
| ac1543d568 | |||
| 4dc57f95e2 | |||
| 9d354ca657 | |||
| 316ee6f810 | |||
| 3f2139349e | |||
| f56dd582f9 | |||
| 225ab47b8e | |||
| 4c350bd5a3 | |||
| 509cc812ea | |||
| e7fb29fced | |||
| a2d11b43de | |||
| f80cf666b1 | |||
| d5f66bc0dd | |||
| ce5be8ac24 | |||
| 1e213c31ef | |||
| 1777bb4b93 | |||
| 91874dfe0b | |||
| 5c6c655cdc | |||
| 0e6eda5e8a | |||
| 60eb602156 | |||
| 24560b9362 | |||
| 76b114d820 | |||
| ce33deed86 | |||
| 1e6c172f18 | |||
| a1ce9b9285 | |||
| 6ec3693030 | |||
| 71729f0b16 | |||
| 1c229a8e08 | |||
| 07b1e3fb90 | |||
| 6294516271 | |||
| f3331b5045 | |||
| 6a06a91fa2 | |||
| 0cba5ba67e | |||
| b815352a45 | |||
| 2f299dbb20 | |||
| f76fbe75e0 | |||
| cc1e356c35 | |||
| 8094f3c0e1 | |||
| 900beaa955 | |||
| 2412333152 | |||
| 4a2d2f7336 | |||
| d03baa4613 | |||
| 41437e4d8f | |||
| 7d220dd8c2 | |||
| a99ec062e6 | |||
| 8fe6395287 | |||
| b4d51599ee | |||
| 9350a20189 | |||
| 889d5da71e | |||
| 5546ab08c2 | |||
| ef183f256c | |||
| 3a96e501cc | |||
| 6b1784f2ee | |||
| 1a872b1643 | |||
| deef9ad181 | |||
| 0dc7e8b8dc | |||
| a5f075548a | |||
| 1706ddd511 | |||
| a220986d16 | |||
| 2a8ac81eed | |||
| d3cb3c03d2 | |||
| c60bc5cdbe | |||
| 6cfbf835c7 | |||
| 047b77e038 | |||
| 766b370264 | |||
| da33277234 | |||
| 3e832e5e94 | |||
| 82e795b642 | |||
| 95516b0343 | |||
| 83fb6826ee | |||
| b1e92ab866 | |||
| 96241cb67c | |||
| b680dab5c9 | |||
| 984840dc82 | |||
| 84e447af09 | |||
| c7ddabcc8b | |||
| bee80fcf8a | |||
| 0aff35b622 | |||
| 46a6a84ecb | |||
| 374812f8fe | |||
| 94e00b8a3e | |||
| f2b78d18a4 | |||
| 17eccfcd8b | |||
| 01e4382d20 | |||
| 1c664af326 | |||
| 392d34422a | |||
| 35e7a3163c | |||
| 96fafa6952 | |||
| a55693211f | |||
| 1acd24fdbe | |||
| 59b7d5db4b | |||
| 16aa47509b | |||
| d1bb3ef6bf | |||
| 5cd9dd4a67 | |||
| fc405d9d73 | |||
| 5786ff5035 | |||
| 8a206a6d92 | |||
| 67b3ab820f | |||
| b06501dd02 | |||
| e3049fffd4 | |||
| 5ecdb7e07c | |||
| 4ed3347f52 | |||
| 364775dcfe | |||
| bfebcdc7ed | |||
| 480e4f3a69 | |||
| 78a0417ee9 | |||
| b83d148b37 | |||
| ad794248fe | |||
| b804791ff6 | |||
| c45bf870b7 | |||
| a0bd8b6049 | |||
| ae4f278df1 | |||
| 28abb8d8cc | |||
| 24d948a3f6 | |||
| c1c9c3554e | |||
| fcea564afa | |||
| f44abd0b28 | |||
| 854903805b | |||
| 868f117b67 | |||
| b6cac4baae | |||
| 4fcb3df1d9 | |||
| b60e22bcb4 | |||
| 61312c2eec | |||
| b0063b1d4a | |||
| fffcbdc44d | |||
| 8d1fa362f7 | |||
| 31a2765b30 | |||
| e79a5b7efe | |||
| ca531e85f3 | |||
| 0941c0944a | |||
| 9b80843c77 | |||
| 8684a3efc3 | |||
| fccfce2ae8 | |||
| 935d2ce29a | |||
| 437ddc01b4 | |||
| f19e58b352 | |||
| eb6f330e67 | |||
| f19899543d | |||
| 3a2611f8e1 | |||
| 53ad312700 | |||
| 89d375daad | |||
| d46f9776fe | |||
| f9a47c8a9f | |||
| e395ee1878 | |||
| 4971c54b05 | |||
| 4c5b82ae37 | |||
| fd7082c30f | |||
| 8d0b72293f | |||
| f3d98f90c0 | |||
| 4eccb5ffc6 | |||
| 86049624c7 | |||
| 3c3b922eae | |||
| e8d0147075 | |||
| 3a0b03348e | |||
| b18baf74d2 | |||
| 44dbbeb608 | |||
| 7161b6416e | |||
| c96ccbb6cb | |||
| 950fff31ed | |||
| f05d8281f3 | |||
| d5c9fa823e | |||
| 99741db645 | |||
| d01972bbe5 | |||
| 750015684d | |||
| baa7e7d561 | |||
| ffaacc04ef | |||
| 5bb47e290f | |||
| 48821f8391 | |||
| aaa2858337 | |||
| 99d7155729 | |||
| f30439a544 | |||
| 997eddfed1 | |||
| 52340aca78 | |||
| e1fb446888 |
@@ -3172,6 +3172,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arne-kroeger",
|
||||
"name": "arne-kroeger",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/65785975?v=4",
|
||||
"profile": "https://github.com/arne-kroeger",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -68,3 +68,5 @@ _ide_helper_models.php
|
||||
storage/ldap_client_tls.cert
|
||||
storage/ldap_client_tls.key
|
||||
/storage/framework/testing
|
||||
|
||||
/.phpunit.cache
|
||||
+1
-1
@@ -51,7 +51,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/111287779?v=4" width="110px;"/><br /><sub>NojoudAlshehri</sub>](https://github.com/NojoudAlshehri)<br />[💻](https://github.com/snipe/snipe-it/commits?author=NojoudAlshehri "Code") | [<img src="https://avatars.githubusercontent.com/u/54367449?v=4" width="110px;"/><br /><sub>Stefan Stidl</sub>](https://github.com/stefanstidlffg)<br />[💻](https://github.com/snipe/snipe-it/commits?author=stefanstidlffg "Code") | [<img src="https://avatars.githubusercontent.com/u/87803479?v=4" width="110px;"/><br /><sub>Quentin Aymard</sub>](https://github.com/qay21)<br />[💻](https://github.com/snipe/snipe-it/commits?author=qay21 "Code") | [<img src="https://avatars.githubusercontent.com/u/5396871?v=4" width="110px;"/><br /><sub>Grant Le Roux</sub>](https://github.com/cram42)<br />[💻](https://github.com/snipe/snipe-it/commits?author=cram42 "Code") | [<img src="https://avatars.githubusercontent.com/u/58479551?v=4" width="110px;"/><br /><sub>Bogdan</sub>](http://@singrity)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Singrity "Code") | [<img src="https://avatars.githubusercontent.com/u/3483684?v=4" width="110px;"/><br /><sub>mmanjos</sub>](https://github.com/mmanjos)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mmanjos "Code") | [<img src="https://avatars.githubusercontent.com/u/7429229?v=4" width="110px;"/><br /><sub>Abdelaziz Faki</sub>](https://azooz2014.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Azooz2014 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/47315739?v=4" width="110px;"/><br /><sub>bilias</sub>](https://github.com/bilias)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bilias "Code") | [<img src="https://avatars.githubusercontent.com/u/2565989?v=4" width="110px;"/><br /><sub>coach1988</sub>](https://github.com/coach1988)<br />[💻](https://github.com/snipe/snipe-it/commits?author=coach1988 "Code") | [<img src="https://avatars.githubusercontent.com/u/11910225?v=4" width="110px;"/><br /><sub>MrM</sub>](https://github.com/mauro-miatello)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mauro-miatello "Code") | [<img src="https://avatars.githubusercontent.com/u/60405354?v=4" width="110px;"/><br /><sub>koiakoia</sub>](https://github.com/koiakoia)<br />[💻](https://github.com/snipe/snipe-it/commits?author=koiakoia "Code") | [<img src="https://avatars.githubusercontent.com/u/5323832?v=4" width="110px;"/><br /><sub>Mustafa Online</sub>](https://github.com/mustafa-online)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mustafa-online "Code") | [<img src="https://avatars.githubusercontent.com/u/104601439?v=4" width="110px;"/><br /><sub>franceslui</sub>](https://github.com/franceslui)<br />[💻](https://github.com/snipe/snipe-it/commits?author=franceslui "Code") | [<img src="https://avatars.githubusercontent.com/u/125313163?v=4" width="110px;"/><br /><sub>Q4kK</sub>](https://github.com/Q4kK)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Q4kK "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/55590532?v=4" width="110px;"/><br /><sub>squintfox</sub>](https://github.com/squintfox)<br />[💻](https://github.com/snipe/snipe-it/commits?author=squintfox "Code") | [<img src="https://avatars.githubusercontent.com/u/1380084?v=4" width="110px;"/><br /><sub>Jeff Clay</sub>](https://github.com/jeffclay)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jeffclay "Code") | [<img src="https://avatars.githubusercontent.com/u/52716446?v=4" width="110px;"/><br /><sub>Phil J R</sub>](https://github.com/PP-JN-RL)<br />[💻](https://github.com/snipe/snipe-it/commits?author=PP-JN-RL "Code") | [<img src="https://avatars.githubusercontent.com/u/1496725?v=4" width="110px;"/><br /><sub>i_virus</sub>](https://www.corelight.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=chandanchowdhury "Code") | [<img src="https://avatars.githubusercontent.com/u/1020541?v=4" width="110px;"/><br /><sub>Paul Grime</sub>](https://github.com/gitgrimbo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gitgrimbo "Code") | [<img src="https://avatars.githubusercontent.com/u/922815?v=4" width="110px;"/><br /><sub>Lee Porte</sub>](https://leeporte.co.uk)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeePorte "Code") | [<img src="https://avatars.githubusercontent.com/u/23613427?v=4" width="110px;"/><br /><sub>BRYAN </sub>](https://github.com/bryanlopezinc)<br />[💻](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Code") [⚠️](https://github.com/snipe/snipe-it/commits?author=bryanlopezinc "Tests") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/64061710?v=4" width="110px;"/><br /><sub>U-H-T</sub>](https://github.com/U-H-T)<br />[💻](https://github.com/snipe/snipe-it/commits?author=U-H-T "Code") | [<img src="https://avatars.githubusercontent.com/u/5395363?v=4" width="110px;"/><br /><sub>Matt Tyree</sub>](https://github.com/Tyree)<br />[📖](https://github.com/snipe/snipe-it/commits?author=Tyree "Documentation") | [<img src="https://avatars.githubusercontent.com/u/292081?v=4" width="110px;"/><br /><sub>Florent Bervas</sub>](http://spoontux.net)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorentDotMe "Code") | [<img src="https://avatars.githubusercontent.com/u/4498077?v=4" width="110px;"/><br /><sub>Daniel Albertsen</sub>](https://ditscheri.com)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dbakan "Code") | [<img src="https://avatars.githubusercontent.com/u/100710244?v=4" width="110px;"/><br /><sub>r-xyz</sub>](https://github.com/r-xyz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=r-xyz "Code") | [<img src="https://avatars.githubusercontent.com/u/47491036?v=4" width="110px;"/><br /><sub>Steven Mainor</sub>](https://github.com/DrekiDegga)<br />[💻](https://github.com/snipe/snipe-it/commits?author=DrekiDegga "Code") | [<img src="https://avatars.githubusercontent.com/u/65785975?v=4" width="110px;"/><br /><sub>arne-kroeger</sub>](https://github.com/arne-kroeger)<br />[💻](https://github.com/snipe/snipe-it/commits?author=arne-kroeger "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class FixupAssignedToWithoutAssignedType extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:assigned-to-fixup
|
||||
{--debug : Display debugging output}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Fixes up assets that have an assigned_to but no assigned_type';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$assets = Asset::whereNull("assigned_type")->whereNotNull("assigned_to")->withTrashed();
|
||||
$this->withProgressBar($assets->get(), function (Asset $asset) {
|
||||
//now check each action log, from the most recent backwards, to find the last checkin or checkout
|
||||
foreach($asset->log()->orderBy("id","desc")->get() as $action_log) {
|
||||
if($this->option("debug")) {
|
||||
$this->info("Asset id: " . $asset->id . " action log, action type is: " . $action_log->action_type);
|
||||
}
|
||||
switch($action_log->action_type) {
|
||||
case 'checkin from':
|
||||
if($this->option("debug")) {
|
||||
$this->info("Doing a checkin for ".$asset->id);
|
||||
}
|
||||
$asset->assigned_to = null;
|
||||
// if you have a required custom field, we still want to save, and we *don't* want an action_log
|
||||
$asset->saveQuietly();
|
||||
return;
|
||||
|
||||
case 'checkout':
|
||||
if($this->option("debug")) {
|
||||
$this->info("Doing a checkout for " . $asset->id . " picking target type: " . $action_log->target_type);
|
||||
}
|
||||
if($asset->assigned_to != $action_log->target_id) {
|
||||
$this->error("Asset's assigned_to does *NOT* match Action Log's target_id. \$asset->assigned_to=".$asset->assigned_to." vs. \$action_log->target_id=".$action_log->target_id);
|
||||
//FIXME - do we abort here? Do we try to keep looking? I don't know, this means your data is *really* messed up...
|
||||
}
|
||||
$asset->assigned_type = $action_log->target_type;
|
||||
$asset->saveQuietly(); // see above
|
||||
return;
|
||||
}
|
||||
}
|
||||
$asset->assigned_to = null; //asset was never checked in or out in its lifetime - it stays 'checked in'
|
||||
$asset->saveQuietly(); //see above
|
||||
});
|
||||
$this->newLine();
|
||||
$this->info("Assets assigned_type are fixed");
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
@@ -15,6 +16,8 @@ use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Purge extends Command
|
||||
{
|
||||
@@ -141,6 +144,20 @@ class Purge extends Command
|
||||
$this->info($users->count().' users purged.');
|
||||
$user_assoc = 0;
|
||||
foreach ($users as $user) {
|
||||
|
||||
$rel_path = 'private_uploads/users';
|
||||
$filenames = Actionlog::where('action_type', 'uploaded')
|
||||
->where('item_id', $user->id)
|
||||
->pluck('filename');
|
||||
foreach($filenames as $filename) {
|
||||
try {
|
||||
if (Storage::exists($rel_path . '/' . $filename)) {
|
||||
Storage::delete($rel_path . '/' . $filename);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::info('An error occurred while deleting files: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->info('- User "'.$user->username.'" deleted.');
|
||||
$user_assoc += $user->userlog()->count();
|
||||
$user->userlog()->forceDelete();
|
||||
|
||||
@@ -92,7 +92,7 @@ class SQLStreamer {
|
||||
$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 'users' because the 'accessories_checkout' table?
|
||||
// can't use 'assets' because 'ver1_components_assets'
|
||||
foreach($check_tables as $check_table => $_ignore) {
|
||||
foreach ($parser->tablenames as $tablename => $_count) {
|
||||
|
||||
@@ -721,7 +721,7 @@ class Helper
|
||||
{
|
||||
$alert_threshold = \App\Models\Setting::getSettings()->alert_threshold;
|
||||
$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('checkouts as checkouts_count')->whereNotNull('min_amt')->get();
|
||||
$components = Component::whereNotNull('min_amt')->get();
|
||||
$asset_models = AssetModel::where('min_amt', '>', 0)->get();
|
||||
$licenses = License::where('min_amt', '>', 0)->get();
|
||||
@@ -749,7 +749,7 @@ class Helper
|
||||
}
|
||||
|
||||
foreach ($accessories as $accessory) {
|
||||
$avail = $accessory->qty - $accessory->users_count;
|
||||
$avail = $accessory->qty - $accessory->checkouts_count;
|
||||
if ($avail < ($accessory->min_amt) + $alert_threshold) {
|
||||
if ($accessory->qty > 0) {
|
||||
$percent = number_format((($avail / $accessory->qty) * 100), 0);
|
||||
|
||||
@@ -144,12 +144,12 @@ class AccessoriesController extends Controller
|
||||
*/
|
||||
public function update(ImageUploadRequest $request, $accessoryId = null) : RedirectResponse
|
||||
{
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($accessoryId)) {
|
||||
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId)) {
|
||||
|
||||
$this->authorize($accessory);
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
"qty" => "required|numeric|min:$accessory->users_count"
|
||||
"qty" => "required|numeric|min:$accessory->checkouts_count"
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
@@ -233,7 +233,7 @@ class AccessoriesController extends Controller
|
||||
*/
|
||||
public function show($accessoryID = null) : View | RedirectResponse
|
||||
{
|
||||
$accessory = Accessory::withCount('users as users_count')->find($accessoryID);
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryID);
|
||||
$this->authorize('view', $accessory);
|
||||
if (isset($accessory->id)) {
|
||||
return view('accessories/view', compact('accessory'));
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Events\CheckoutableCheckedIn;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -24,7 +25,7 @@ class AccessoryCheckinController extends Controller
|
||||
*/
|
||||
public function create($accessoryUserId = null, $backto = null) : View | RedirectResponse
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_user = DB::table('accessories_checkout')->find($accessoryUserId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found'));
|
||||
}
|
||||
|
||||
@@ -39,16 +40,16 @@ class AccessoryCheckinController extends Controller
|
||||
*
|
||||
* @uses Accessory::checkin_email() to determine if an email can and should be sent
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param null $accessoryUserId
|
||||
* @param null $accessoryCheckoutId
|
||||
* @param string $backto
|
||||
*/
|
||||
public function store(Request $request, $accessoryUserId = null, $backto = null) : RedirectResponse
|
||||
public function store(Request $request, $accessoryCheckoutId = null, $backto = null) : RedirectResponse
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryCheckoutId))) {
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
@@ -59,10 +60,8 @@ class AccessoryCheckinController extends Controller
|
||||
}
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
$return_to = e($accessory_user->assigned_to);
|
||||
|
||||
event(new CheckoutableCheckedIn($accessory, User::find($return_to), auth()->user(), $request->input('note'), $checkin_at));
|
||||
if ($accessory_checkout->delete()) {
|
||||
event(new CheckoutableCheckedIn($accessory, $accessory_checkout->assignedTo, auth()->user(), $request->input('note'), $checkin_at));
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ namespace App\Http\Controllers\Accessories;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -16,6 +18,9 @@ use \Illuminate\Http\RedirectResponse;
|
||||
|
||||
class AccessoryCheckoutController extends Controller
|
||||
{
|
||||
|
||||
use CheckInOutRequest;
|
||||
|
||||
/**
|
||||
* Return the form to checkout an Accessory to a user.
|
||||
*
|
||||
@@ -25,7 +30,7 @@ class AccessoryCheckoutController extends Controller
|
||||
public function create($id) : View | RedirectResponse
|
||||
{
|
||||
|
||||
if ($accessory = Accessory::withCount('users as users_count')->find($id)) {
|
||||
if ($accessory = Accessory::withCount('checkouts as checkouts_count')->find($id)) {
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
|
||||
@@ -58,30 +63,32 @@ class AccessoryCheckoutController extends Controller
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @param Request $request
|
||||
* @param int $accessory
|
||||
* @param Accessory $accessory
|
||||
*/
|
||||
public function store(AccessoryCheckoutRequest $request, Accessory $accessory) : RedirectResponse
|
||||
{
|
||||
|
||||
|
||||
$this->authorize('checkout', $accessory);
|
||||
$accessory->assigned_to = $request->input('assigned_to');
|
||||
$user = User::find($request->input('assigned_to'));
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
$target = $this->determineCheckoutTarget();
|
||||
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
AccessoryCheckout::create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->input('assigned_to'),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
}
|
||||
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
|
||||
// Set this as user since we only allow checkout to user for this item type
|
||||
$request->request->add(['checkout_to_type' => 'user']);
|
||||
$request->request->add(['assigned_user' => $user->id]);
|
||||
$request->request->add(['checkout_to_type' => request('checkout_to_type')]);
|
||||
$request->request->add(['assigned_user' => $target->id]);
|
||||
|
||||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]);
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ class AcceptanceController extends Controller
|
||||
'item_tag' => $item->asset_tag,
|
||||
'item_model' => $display_model,
|
||||
'item_serial' => $item->serial,
|
||||
'item_status' => $item->assetstatus?->name,
|
||||
'eula' => $item->getEula(),
|
||||
'note' => $request->input('note'),
|
||||
'check_out_date' => Carbon::parse($acceptance->created_at)->format('Y-m-d'),
|
||||
@@ -308,6 +309,7 @@ class AcceptanceController extends Controller
|
||||
'item_tag' => $item->asset_tag,
|
||||
'item_model' => $display_model,
|
||||
'item_serial' => $item->serial,
|
||||
'item_status' => $item->assetstatus?->name,
|
||||
'note' => $request->input('note'),
|
||||
'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'),
|
||||
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\CheckInOutRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\AccessoryCheckoutRequest;
|
||||
use App\Http\Requests\StoreAccessoryRequest;
|
||||
@@ -17,10 +18,12 @@ use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
|
||||
use App\Models\AccessoryCheckout;
|
||||
|
||||
class AccessoriesController extends Controller
|
||||
{
|
||||
use CheckInOutRequest;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
@@ -48,13 +51,13 @@ class AccessoriesController extends Controller
|
||||
'min_amt',
|
||||
'company_id',
|
||||
'notes',
|
||||
'users_count',
|
||||
'checkouts_count',
|
||||
'qty',
|
||||
];
|
||||
|
||||
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'users', 'location', 'supplier')
|
||||
->withCount('users as users_count');
|
||||
$accessories = Accessory::select('accessories.*')->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier')
|
||||
->withCount('checkouts as checkouts_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
@@ -154,7 +157,7 @@ class AccessoriesController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$this->authorize('view', Accessory::class);
|
||||
$accessory = Accessory::withCount('users as users_count')->findOrFail($id);
|
||||
$accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id);
|
||||
|
||||
return (new AccessoriesTransformer)->transformAccessory($accessory);
|
||||
}
|
||||
@@ -197,28 +200,23 @@ class AccessoriesController extends Controller
|
||||
$offset = request('offset', 0);
|
||||
$limit = request('limit', 50);
|
||||
|
||||
$accessory_users = $accessory->users;
|
||||
$total = $accessory_users->count();
|
||||
$accessory_checkouts = $accessory->checkouts;
|
||||
$total = $accessory_checkouts->count();
|
||||
|
||||
if ($total < $offset) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$accessory_users = $accessory->users()->skip($offset)->take($limit)->get();
|
||||
$accessory_checkouts = $accessory->checkouts()->skip($offset)->take($limit)->get();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessory_users = $accessory->users()
|
||||
->where(function ($query) use ($request) {
|
||||
$search_str = '%' . $request->input('search') . '%';
|
||||
$query->where('first_name', 'like', $search_str)
|
||||
->orWhere('last_name', 'like', $search_str)
|
||||
->orWhere('note', 'like', $search_str);
|
||||
})
|
||||
|
||||
$accessory_checkouts = $accessory->checkouts()->TextSearch($request->input('search'))
|
||||
->get();
|
||||
$total = $accessory_users->count();
|
||||
$total = $accessory_checkouts->count();
|
||||
}
|
||||
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_users, $total);
|
||||
return (new AccessoriesTransformer)->transformCheckedoutAccessory($accessory, $accessory_checkouts, $total);
|
||||
}
|
||||
|
||||
|
||||
@@ -282,22 +280,22 @@ class AccessoriesController extends Controller
|
||||
public function checkout(AccessoryCheckoutRequest $request, Accessory $accessory)
|
||||
{
|
||||
$this->authorize('checkout', $accessory);
|
||||
$accessory->assigned_to = $request->input('assigned_to');
|
||||
$user = User::find($request->input('assigned_to'));
|
||||
$target = $this->determineCheckoutTarget();
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
for ($i = 0; $i < $accessory->checkout_qty; $i++) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
AccessoryCheckout::create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => Auth::id(),
|
||||
'assigned_to' => $request->input('assigned_to'),
|
||||
'assigned_to' => $target->id,
|
||||
'assigned_type' => $target::class,
|
||||
'note' => $request->input('note'),
|
||||
]);
|
||||
}
|
||||
|
||||
// Set this value to be able to pass the qty through to the event
|
||||
event(new CheckoutableCheckedOut($accessory, $user, auth()->user(), $request->input('note')));
|
||||
event(new CheckoutableCheckedOut($accessory, $target, auth()->user(), $request->input('note')));
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/accessories/message.checkout.success')));
|
||||
|
||||
@@ -316,19 +314,19 @@ class AccessoriesController extends Controller
|
||||
*/
|
||||
public function checkin(Request $request, $accessoryUserId = null)
|
||||
{
|
||||
if (is_null($accessory_user = DB::table('accessories_users')->find($accessoryUserId))) {
|
||||
if (is_null($accessory_checkout = AccessoryCheckout::find($accessoryUserId))) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.does_not_exist')));
|
||||
}
|
||||
|
||||
$accessory = Accessory::find($accessory_user->accessory_id);
|
||||
$accessory = Accessory::find($accessory_checkout->accessory_id);
|
||||
$this->authorize('checkin', $accessory);
|
||||
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_user->assigned_to), $request->input('note'));
|
||||
$logaction = $accessory->logCheckin(User::find($accessory_checkout->assigned_to), $request->input('note'));
|
||||
|
||||
// Was the accessory updated?
|
||||
if (DB::table('accessories_users')->where('id', '=', $accessory_user->id)->delete()) {
|
||||
if (! is_null($accessory_user->assigned_to)) {
|
||||
$user = User::find($accessory_user->assigned_to);
|
||||
if ($accessory_checkout->delete()) {
|
||||
if (! is_null($accessory_checkout->assigned_to)) {
|
||||
$user = User::find($accessory_checkout->assigned_to);
|
||||
}
|
||||
|
||||
$data['log_id'] = $logaction->id;
|
||||
|
||||
@@ -928,7 +928,7 @@ class AssetsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->has('status_id')) {
|
||||
if ($request->filled('status_id')) {
|
||||
$asset->status_id = $request->input('status_id');
|
||||
}
|
||||
|
||||
@@ -978,7 +978,7 @@ class AssetsController extends Controller
|
||||
public function checkinByTag(Request $request, $tag = null) : JsonResponse
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
if(null == $tag && null !== ($request->input('asset_tag'))) {
|
||||
if (null == $tag && null !== ($request->input('asset_tag'))) {
|
||||
$tag = $request->input('asset_tag');
|
||||
}
|
||||
$asset = Asset::where('asset_tag', $tag)->first();
|
||||
|
||||
@@ -20,9 +20,9 @@ class DepreciationsController extends Controller
|
||||
public function index(Request $request) : JsonResponse | array
|
||||
{
|
||||
$this->authorize('view', Depreciation::class);
|
||||
$allowed_columns = ['id','name','months','depreciation_min','created_at'];
|
||||
$allowed_columns = ['id','name','months','depreciation_min', 'depreciation_type','created_at'];
|
||||
|
||||
$depreciations = Depreciation::select('id','name','months','depreciation_min','user_id','created_at','updated_at');
|
||||
$depreciations = Depreciation::select('id','name','months','depreciation_min','depreciation_type','user_id','created_at','updated_at');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$depreciations = $depreciations->TextSearch($request->input('search'));
|
||||
|
||||
@@ -248,6 +248,7 @@ class LocationsController extends Controller
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->findOrFail($id);
|
||||
|
||||
if (! $location->isDeletable()) {
|
||||
|
||||
@@ -83,11 +83,19 @@ class ReportsController extends Controller
|
||||
$offset = ($request->input('offset') > $total) ? $total : app('api_offset_value');
|
||||
$limit = app('api_limit_value');
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
|
||||
|
||||
switch ($request->input('sort')) {
|
||||
case 'admin':
|
||||
$actionlogs->OrderAdmin($order);
|
||||
break;
|
||||
default:
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||
$actionlogs = $actionlogs->orderBy($sort, $order);
|
||||
break;
|
||||
}
|
||||
|
||||
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
|
||||
$actionlogs = $actionlogs->skip($offset)->take($limit)->get();
|
||||
|
||||
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Http\Transformers\AssetsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Http\Transformers\StatuslabelsTransformer;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Statuslabel;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Transformers\PieChartTransformer;
|
||||
@@ -187,8 +188,14 @@ class StatuslabelsController extends Controller
|
||||
public function getAssetCountByStatuslabel() : array
|
||||
{
|
||||
$this->authorize('view', Statuslabel::class);
|
||||
$statuslabels = Statuslabel::withCount('assets')->get();
|
||||
$total = Array();
|
||||
|
||||
if (Setting::getSettings()->show_archived_in_list == 0 ) {
|
||||
$statuslabels = Statuslabel::withCount('assets')->where('archived','0')->get();
|
||||
} else {
|
||||
$statuslabels = Statuslabel::withCount('assets')->get();
|
||||
}
|
||||
|
||||
$total = [];
|
||||
|
||||
foreach ($statuslabels as $statuslabel) {
|
||||
|
||||
|
||||
@@ -478,9 +478,16 @@ class AssetsController extends Controller
|
||||
$tag = $tag ? $tag : $request->get('assetTag');
|
||||
$topsearch = ($request->get('topsearch') == 'true');
|
||||
|
||||
if (! $asset = Asset::where('asset_tag', '=', $tag)->first()) {
|
||||
return redirect()->route('hardware.index')->with('error', trans('admin/hardware/message.does_not_exist'));
|
||||
// Search for an exact and unique asset tag match
|
||||
$assets = Asset::where('asset_tag', '=', $tag);
|
||||
|
||||
// If not a unique result, redirect to the index view
|
||||
if ($assets->count() != 1) {
|
||||
return redirect()->route('hardware.index')
|
||||
->with('search', $tag)
|
||||
->with('warning', trans('admin/hardware/message.does_not_exist_var', [ 'asset_tag' => $tag ]));
|
||||
}
|
||||
$asset = $assets->first();
|
||||
$this->authorize('view', $asset);
|
||||
|
||||
return redirect()->route('hardware.show', $asset->id)->with('topsearch', $topsearch);
|
||||
@@ -837,7 +844,7 @@ class AssetsController extends Controller
|
||||
{
|
||||
$this->authorize('checkin', Asset::class);
|
||||
|
||||
return view('hardware/quickscan-checkin');
|
||||
return view('hardware/quickscan-checkin')->with('statusLabel_list', Helper::statusLabelList());
|
||||
}
|
||||
|
||||
public function audit($id)
|
||||
|
||||
@@ -227,7 +227,8 @@ class BulkAssetsController extends Controller
|
||||
* its checkout status.
|
||||
*/
|
||||
|
||||
if (($request->filled('purchase_date'))
|
||||
if (($request->filled('name'))
|
||||
|| ($request->filled('purchase_date'))
|
||||
|| ($request->filled('expected_checkin'))
|
||||
|| ($request->filled('purchase_cost'))
|
||||
|| ($request->filled('supplier_id'))
|
||||
@@ -239,6 +240,7 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->filled('status_id'))
|
||||
|| ($request->filled('model_id'))
|
||||
|| ($request->filled('next_audit_date'))
|
||||
|| ($request->filled('null_name'))
|
||||
|| ($request->filled('null_purchase_date'))
|
||||
|| ($request->filled('null_expected_checkin_date'))
|
||||
|| ($request->filled('null_next_audit_date'))
|
||||
@@ -251,13 +253,14 @@ class BulkAssetsController extends Controller
|
||||
$this->update_array = [];
|
||||
|
||||
/**
|
||||
* Leave out model_id and status here because we do math on that later. We have to do some extra
|
||||
* validation and checks on those two.
|
||||
* Leave out model_id and status here because we do math on that later. We have to do some
|
||||
* extra validation and checks on those two.
|
||||
*
|
||||
* It's tempting to make these match the request check above, but some of these values require
|
||||
* extra work to make sure the data makes sense.
|
||||
*/
|
||||
$this->conditionallyAddItem('purchase_date')
|
||||
$this->conditionallyAddItem('name')
|
||||
->conditionallyAddItem('purchase_date')
|
||||
->conditionallyAddItem('expected_checkin')
|
||||
->conditionallyAddItem('order_number')
|
||||
->conditionallyAddItem('requestable')
|
||||
@@ -271,6 +274,11 @@ class BulkAssetsController extends Controller
|
||||
/**
|
||||
* Blank out fields that were requested to be blanked out via checkbox
|
||||
*/
|
||||
if ($request->input('null_name')=='1') {
|
||||
|
||||
$this->update_array['name'] = null;
|
||||
}
|
||||
|
||||
if ($request->input('null_purchase_date')=='1') {
|
||||
$this->update_array['purchase_date'] = null;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class ResetPasswordController extends Controller
|
||||
'password.not_in' => trans('validation.disallow_same_pwd_as_user_fields'),
|
||||
];
|
||||
|
||||
$request->validate($this->rules(), $request->all(), $this->validationErrorMessages());
|
||||
$request->validate($this->rules());
|
||||
|
||||
Log::debug('Checking if '.$request->input('username').' exists');
|
||||
// Check to see if the user even exists - we'll treat the response the same to prevent user sniffing
|
||||
|
||||
@@ -99,12 +99,18 @@ class SamlController extends Controller
|
||||
{
|
||||
$saml = $this->saml;
|
||||
$auth = $saml->getAuth();
|
||||
$auth->processResponse();
|
||||
$saml_exception = false;
|
||||
try {
|
||||
$auth->processResponse();
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("Exception caught in SAML login: " . $e->getMessage());
|
||||
$saml_exception = true;
|
||||
}
|
||||
$errors = $auth->getErrors();
|
||||
|
||||
if (! empty($errors)) {
|
||||
Log::error('There was an error with SAML ACS: '.implode(', ', $errors));
|
||||
Log::error('Reason: '.$auth->getLastErrorReason());
|
||||
if (!empty($errors) || $saml_exception) {
|
||||
Log::warning('There was an error with SAML ACS: ' . implode(', ', $errors));
|
||||
Log::warning('Reason: ' . $auth->getLastErrorReason());
|
||||
|
||||
return redirect()->route('login')->with('error', trans('auth/message.signin.error'));
|
||||
}
|
||||
@@ -132,12 +138,18 @@ class SamlController extends Controller
|
||||
{
|
||||
$auth = $this->saml->getAuth();
|
||||
$retrieveParametersFromServer = $this->saml->getSetting('retrieveParametersFromServer', false);
|
||||
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
|
||||
$saml_exception = false;
|
||||
try {
|
||||
$sloUrl = $auth->processSLO(true, null, $retrieveParametersFromServer, null, true);
|
||||
} catch (\Exception $e) {
|
||||
Log::warning("Exception caught in SAML single-logout: " . $e->getMessage());
|
||||
$saml_exception = true;
|
||||
}
|
||||
$errors = $auth->getErrors();
|
||||
|
||||
if (! empty($errors)) {
|
||||
Log::error('There was an error with SAML SLS: '.implode(', ', $errors));
|
||||
Log::error('Reason: '.$auth->getLastErrorReason());
|
||||
if (!empty($errors) || $saml_exception) {
|
||||
Log::warning('There was an error with SAML SLS: ' . implode(', ', $errors));
|
||||
Log::warning('Reason: ' . $auth->getLastErrorReason());
|
||||
|
||||
return view('errors.403');
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ trait CheckInOutRequest
|
||||
return Location::findOrFail(request('assigned_location'));
|
||||
case 'asset':
|
||||
return Asset::findOrFail(request('assigned_asset'));
|
||||
case 'user':
|
||||
default:
|
||||
return User::findOrFail(request('assigned_user'));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Component;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
@@ -97,6 +98,10 @@ class ComponentCheckoutController extends Controller
|
||||
// Check if the asset exists
|
||||
$asset = Asset::find($request->input('asset_id'));
|
||||
|
||||
if ((Setting::getSettings()->full_multiple_companies_support) && $component->company_id !== $asset->company_id) {
|
||||
return redirect()->route('components.checkout.show', $componentId)->with('error', trans('general.error_user_company'));
|
||||
}
|
||||
|
||||
// Update the component data
|
||||
$component->asset_id = $request->input('asset_id');
|
||||
$component->assets()->attach($component->id, [
|
||||
|
||||
@@ -62,6 +62,20 @@ class DepreciationsController extends Controller
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
$depreciation->user_id = Auth::id();
|
||||
|
||||
$request->validate([
|
||||
'depreciation_min' => [
|
||||
'required',
|
||||
'numeric',
|
||||
function ($attribute, $value, $fail) use ($request) {
|
||||
if ($request->input('depreciation_type') == 'percent' && ($value < 0 || $value > 100)) {
|
||||
$fail(trans('validation.percent'));
|
||||
}
|
||||
},
|
||||
],
|
||||
'depreciation_type' => 'required|in:amount,percent',
|
||||
]);
|
||||
$depreciation->depreciation_type = $request->input('depreciation_type');
|
||||
$depreciation->depreciation_min = $request->input('depreciation_min');
|
||||
|
||||
// Was the asset created?
|
||||
@@ -116,6 +130,20 @@ class DepreciationsController extends Controller
|
||||
// Depreciation data
|
||||
$depreciation->name = $request->input('name');
|
||||
$depreciation->months = $request->input('months');
|
||||
|
||||
$request->validate([
|
||||
'depreciation_min' => [
|
||||
'required',
|
||||
'numeric',
|
||||
function ($attribute, $value, $fail) use ($request) {
|
||||
if ($request->input('depreciation_type') == 'percent' && ($value < 0 || $value > 100)) {
|
||||
$fail(trans('validation.percent'));
|
||||
}
|
||||
},
|
||||
],
|
||||
'depreciation_type' => 'required|in:amount,percent',
|
||||
]);
|
||||
$depreciation->depreciation_type = $request->input('depreciation_type');
|
||||
$depreciation->depreciation_min = $request->input('depreciation_min');
|
||||
|
||||
// Was the asset created?
|
||||
|
||||
@@ -49,6 +49,7 @@ class ProfileController extends Controller
|
||||
$user->gravatar = $request->input('gravatar');
|
||||
$user->skin = $request->input('skin');
|
||||
$user->phone = $request->input('phone');
|
||||
$user->enable_sounds = $request->input('enable_sounds', false);
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
$user->locale = $request->input('locale', 'en-US');
|
||||
|
||||
@@ -324,6 +324,7 @@ class SettingsController extends Controller
|
||||
|
||||
$setting->full_multiple_companies_support = $request->input('full_multiple_companies_support', '0');
|
||||
$setting->unique_serial = $request->input('unique_serial', '0');
|
||||
$setting->shortcuts_enabled = $request->input('shortcuts_enabled', '0');
|
||||
$setting->show_images_in_email = $request->input('show_images_in_email', '0');
|
||||
$setting->show_archived_in_list = $request->input('show_archived_in_list', '0');
|
||||
$setting->dashboard_message = $request->input('dashboard_message');
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Models\Consumable;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
@@ -218,21 +219,19 @@ class BulkUsersController extends Controller
|
||||
}
|
||||
|
||||
$users = User::whereIn('id', $user_raw_array)->get();
|
||||
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', \App\Models\User::class)->get();
|
||||
$accessories = DB::table('accessories_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$assets = Asset::whereIn('assigned_to', $user_raw_array)->where('assigned_type', User::class)->get();
|
||||
$accessoryUserRows = DB::table('accessories_checkout')->where('assigned_type', User::class)->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$licenses = DB::table('license_seats')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$consumables = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
$consumableUserRows = DB::table('consumables_users')->whereIn('assigned_to', $user_raw_array)->get();
|
||||
|
||||
if ((($assets->count() > 0) && ((!$request->filled('status_id')) || ($request->input('status_id') == '')))) {
|
||||
return redirect()->route('users.index')->with('error', 'No status selected');
|
||||
}
|
||||
|
||||
|
||||
$this->logItemCheckinAndDelete($assets, Asset::class);
|
||||
$this->logItemCheckinAndDelete($accessories, Accessory::class);
|
||||
$this->logAccessoriesCheckin($accessoryUserRows);
|
||||
$this->logItemCheckinAndDelete($licenses, License::class);
|
||||
$this->logItemCheckinAndDelete($consumables, Consumable::class);
|
||||
|
||||
$this->logConsumablesCheckin($consumableUserRows);
|
||||
|
||||
Asset::whereIn('id', $assets->pluck('id'))->update([
|
||||
'status_id' => e(request('status_id')),
|
||||
@@ -241,19 +240,14 @@ class BulkUsersController extends Controller
|
||||
'expected_checkin' => null,
|
||||
]);
|
||||
|
||||
|
||||
LicenseSeat::whereIn('id', $licenses->pluck('id'))->update(['assigned_to' => null]);
|
||||
ConsumableAssignment::whereIn('id', $consumables->pluck('id'))->delete();
|
||||
|
||||
ConsumableAssignment::whereIn('id', $consumableUserRows->pluck('id'))->delete();
|
||||
|
||||
foreach ($users as $user) {
|
||||
|
||||
$user->consumables()->sync([]);
|
||||
$user->accessories()->sync([]);
|
||||
if ($request->input('delete_user')=='1') {
|
||||
$user->delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$msg = trans('general.bulk_checkin_success');
|
||||
@@ -279,7 +273,7 @@ class BulkUsersController extends Controller
|
||||
if ($itemType == License::class){
|
||||
$item_id = $item->license_id;
|
||||
}
|
||||
|
||||
|
||||
$logAction->item_id = $item_id;
|
||||
// We can't rely on get_class here because the licenses/accessories fetched above are not eloquent models, but simply arrays.
|
||||
$logAction->item_type = $itemType;
|
||||
@@ -291,6 +285,34 @@ class BulkUsersController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
private function logAccessoriesCheckin(Collection $accessoryUserRows): void
|
||||
{
|
||||
foreach ($accessoryUserRows as $accessoryUserRow) {
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $accessoryUserRow->accessory_id;
|
||||
$logAction->item_type = Accessory::class;
|
||||
$logAction->target_id = $accessoryUserRow->assigned_to;
|
||||
$logAction->target_type = User::class;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->note = 'Bulk checkin items';
|
||||
$logAction->logaction('checkin from');
|
||||
}
|
||||
}
|
||||
|
||||
private function logConsumablesCheckin(Collection $consumableUserRows): void
|
||||
{
|
||||
foreach ($consumableUserRows as $consumableUserRow) {
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_id = $consumableUserRow->consumable_id;
|
||||
$logAction->item_type = Consumable::class;
|
||||
$logAction->target_id = $consumableUserRow->assigned_to;
|
||||
$logAction->target_type = User::class;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->note = 'Bulk checkin items';
|
||||
$logAction->logaction('checkin from');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save bulk-edited users
|
||||
*
|
||||
|
||||
@@ -44,13 +44,10 @@ class AccessoryCheckoutRequest extends ImageUploadRequest
|
||||
|
||||
return array_merge(
|
||||
[
|
||||
'assigned_to' => [
|
||||
'required',
|
||||
'integer',
|
||||
'exists:users,id,deleted_at,NULL',
|
||||
'not_array'
|
||||
],
|
||||
|
||||
'assigned_user' => 'required_without_all:assigned_asset,assigned_location',
|
||||
'assigned_asset' => 'required_without_all:assigned_user,assigned_location',
|
||||
'assigned_location' => 'required_without_all:assigned_user,assigned_asset',
|
||||
|
||||
'number_remaining_after_checkout' => [
|
||||
'min:0',
|
||||
'required',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Traits\MayContainCustomFields;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Company;
|
||||
use App\Models\Setting;
|
||||
@@ -11,6 +12,7 @@ use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreAssetRequest extends ImageUploadRequest
|
||||
{
|
||||
use MayContainCustomFields;
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Traits;
|
||||
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\CustomField;
|
||||
|
||||
trait MayContainCustomFields
|
||||
{
|
||||
// this gets called automatically on a form request
|
||||
public function withValidator($validator)
|
||||
{
|
||||
// find the model
|
||||
if ($this->method() == 'POST') {
|
||||
$asset_model = AssetModel::find($this->model_id);
|
||||
}
|
||||
if ($this->method() == 'PATCH' || $this->method() == 'PUT') {
|
||||
$asset_model = $this->asset->model;
|
||||
}
|
||||
// collect the custom fields in the request
|
||||
$validator->after(function ($validator) use ($asset_model) {
|
||||
$request_fields = $this->collect()->keys()->filter(function ($attributes) {
|
||||
return str_starts_with($attributes, '_snipeit_');
|
||||
});
|
||||
// if there are custom fields, find the one's that don't exist on the model's fieldset and add an error to the validator's error bag
|
||||
if (count($request_fields) > 0) {
|
||||
$request_fields->diff($asset_model?->fieldset?->fields?->pluck('db_column'))
|
||||
->each(function ($request_field_name) use ($request_fields, $validator) {
|
||||
if (CustomField::where('db_column', $request_field_name)->exists()) {
|
||||
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found_on_model'));
|
||||
} else {
|
||||
$validator->errors()->add($request_field_name, trans('validation.custom.custom_field_not_found'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Http\Requests\Traits\MayContainCustomFields;
|
||||
use App\Models\Asset;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateAssetRequest extends ImageUploadRequest
|
||||
{
|
||||
use MayContainCustomFields;
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
|
||||
@@ -39,7 +39,7 @@ class AccessoriesTransformer
|
||||
'order_number' => ($accessory->order_number) ? e($accessory->order_number) : null,
|
||||
'min_qty' => ($accessory->min_amt) ? (int) $accessory->min_amt : null,
|
||||
'remaining_qty' => (int) $accessory->numRemaining(),
|
||||
'users_count' => $accessory->users_count,
|
||||
'checkouts_count' => $accessory->checkouts_count,
|
||||
|
||||
'created_at' => Helper::getFormattedDateObject($accessory->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($accessory->updated_at, 'datetime'),
|
||||
@@ -66,27 +66,42 @@ class AccessoriesTransformer
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_users, $total)
|
||||
public function transformCheckedoutAccessory($accessory, $accessory_checkouts, $total)
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach ($accessory_users as $user) {
|
||||
foreach ($accessory_checkouts as $checkout) {
|
||||
$array[] = [
|
||||
|
||||
'assigned_pivot_id' => $user->pivot->id,
|
||||
'id' => (int) $user->id,
|
||||
'username' => e($user->username),
|
||||
'name' => e($user->getFullNameAttribute()),
|
||||
'first_name'=> e($user->first_name),
|
||||
'last_name'=> e($user->last_name),
|
||||
'employee_number' => e($user->employee_num),
|
||||
'checkout_notes' => e($user->pivot->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($user->pivot->created_at, 'datetime'),
|
||||
'type' => 'user',
|
||||
'id' => $checkout->id,
|
||||
'assigned_to' => $this->transformAssignedTo($checkout),
|
||||
'checkout_notes' => e($checkout->note),
|
||||
'last_checkout' => Helper::getFormattedDateObject($checkout->created_at, 'datetime'),
|
||||
'available_actions' => ['checkin' => true],
|
||||
];
|
||||
}
|
||||
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
}
|
||||
|
||||
public function transformAssignedTo($accessoryCheckout)
|
||||
{
|
||||
if ($accessoryCheckout->checkedOutToUser()) {
|
||||
return [
|
||||
'id' => (int) $accessoryCheckout->assigned->id,
|
||||
'username' => e($accessoryCheckout->assigned->username),
|
||||
'name' => e($accessoryCheckout->assigned->getFullNameAttribute()),
|
||||
'first_name'=> e($accessoryCheckout->assigned->first_name),
|
||||
'last_name'=> ($accessoryCheckout->assigned->last_name) ? e($accessoryCheckout->assigned->last_name) : null,
|
||||
'email'=> ($accessoryCheckout->assigned->email) ? e($accessoryCheckout->assigned->email) : null,
|
||||
'employee_number' => ($accessoryCheckout->assigned->employee_num) ? e($accessoryCheckout->assigned->employee_num) : null,
|
||||
'type' => 'user',
|
||||
];
|
||||
}
|
||||
|
||||
return $accessoryCheckout->assigned ? [
|
||||
'id' => $accessoryCheckout->assigned->id,
|
||||
'name' => e($accessoryCheckout->assigned->display_name),
|
||||
'type' => $accessoryCheckout->assignedType(),
|
||||
] : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,11 +205,11 @@ class ActionlogsTransformer
|
||||
|
||||
|
||||
|
||||
public function transformCheckedoutActionlog (Collection $accessories_users, $total)
|
||||
public function transformCheckedoutActionlog (Collection $accessories_checkout, $total)
|
||||
{
|
||||
|
||||
$array = array();
|
||||
foreach ($accessories_users as $user) {
|
||||
foreach ($accessories_checkout as $user) {
|
||||
$array[] = (new UsersTransformer)->transformUser($user);
|
||||
}
|
||||
return (new DatatablesTransformer)->transformDatatables($array, $total);
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\Depreciable;
|
||||
use App\Models\Depreciation;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DepreciationsTransformer
|
||||
{
|
||||
@@ -26,7 +27,7 @@ class DepreciationsTransformer
|
||||
'id' => (int) $depreciation->id,
|
||||
'name' => e($depreciation->name),
|
||||
'months' => $depreciation->months.' '.trans('general.months'),
|
||||
'depreciation_min' => $depreciation->depreciation_min,
|
||||
'depreciation_min' => $depreciation->depreciation_type === 'percent' ? $depreciation->depreciation_min.'%' : $depreciation->depreciation_min,
|
||||
'created_at' => Helper::getFormattedDateObject($depreciation->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($depreciation->updated_at, 'datetime')
|
||||
];
|
||||
|
||||
@@ -137,7 +137,11 @@ class CheckoutableListener
|
||||
*/
|
||||
private function getCheckoutAcceptance($event)
|
||||
{
|
||||
if (! $event->checkoutable->requireAcceptance()) {
|
||||
$checkedOutToType = get_class($event->checkedOutTo);
|
||||
if ($checkedOutToType != "App\Models\User") {
|
||||
return null;
|
||||
}
|
||||
if (!$event->checkoutable->requireAcceptance()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,9 +253,10 @@ class Accessory extends SnipeModel
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function users()
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->withPivot('id', 'created_at', 'note')->withTrashed();
|
||||
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
|
||||
->with('assignedTo');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +268,9 @@ class Accessory extends SnipeModel
|
||||
*/
|
||||
public function hasUsers()
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\User::class, 'accessories_users', 'accessory_id', 'assigned_to')->count();
|
||||
return $this->hasMany(\App\Models\AccessoryCheckout::class, 'accessory_id')
|
||||
->where('assigned_type', User::class)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,15 +341,15 @@ class Accessory extends SnipeModel
|
||||
*/
|
||||
public function numCheckedOut()
|
||||
{
|
||||
return $this->users_count ?? $this->users()->count();
|
||||
return $this->checkouts_count ?? $this->checkouts()->count();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check how many items of an accessory remain.
|
||||
*
|
||||
* In order to use this model method, you MUST call withCount('users as users_count')
|
||||
* on the eloquent query in the controller, otherwise $this->users_count will be null and
|
||||
* In order to use this model method, you MUST call withCount('checkouts as checkouts_count')
|
||||
* on the eloquent query in the controller, otherwise $this->checkouts_count will be null and
|
||||
* bad things happen.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
@@ -370,12 +373,12 @@ class Accessory extends SnipeModel
|
||||
*/
|
||||
public function declinedCheckout(User $declinedBy, $signature)
|
||||
{
|
||||
if (is_null($accessory_user = \DB::table('accessories_users')->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
|
||||
if (is_null($accessory_checkout = AccessoryCheckout::userAssigned()->where('assigned_to', $declinedBy->id)->where('accessory_id', $this->id)->latest('created_at'))) {
|
||||
// Redirect to the accessory management page with error
|
||||
return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.does_not_exist'));
|
||||
}
|
||||
|
||||
$accessory_user->limit(1)->delete();
|
||||
$accessory_checkout->limit(1)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Executable
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
|
||||
/**
|
||||
* Model for Accessories.
|
||||
*
|
||||
* @version v1.0
|
||||
*/
|
||||
class AccessoryCheckout extends Model
|
||||
{
|
||||
use Searchable;
|
||||
|
||||
protected $fillable = ['user_id', 'accessory_id', 'assigned_to', 'assigned_type', 'note'];
|
||||
protected $table = 'accessories_checkout';
|
||||
|
||||
/**
|
||||
* Establishes the accessory checkout -> accessory relationship
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0.9]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function accessory()
|
||||
{
|
||||
return $this->hasOne(\App\Models\Accessory::class, 'accessory_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the accessory checkout -> user relationship
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0.9]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(\App\Models\User::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target this asset is checked out to
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function assignedTo()
|
||||
{
|
||||
return $this->morphTo('assigned', 'assigned_type', 'assigned_to')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lowercased name of the type of target the asset is assigned to
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return string
|
||||
*/
|
||||
public function assignedType()
|
||||
{
|
||||
return $this->assigned_type ? strtolower(class_basename($this->assigned_type)) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the accessory is checked out to a user
|
||||
*
|
||||
* Even though we allow allow for checkout to things beyond users
|
||||
* this method is an easy way of seeing if we are checked out to a user.
|
||||
*
|
||||
* @author [A. Kroeger]
|
||||
* @since [v7.0]
|
||||
*/
|
||||
public function checkedOutToUser(): bool
|
||||
{
|
||||
return $this->assignedType() === Asset::USER;
|
||||
}
|
||||
|
||||
public function scopeUserAssigned(Builder $query): void
|
||||
{
|
||||
$query->where('assigned_type', '=', User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run additional, advanced searches.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $terms The search terms
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function advancedTextSearch(Builder $query, array $terms)
|
||||
{
|
||||
|
||||
$userQuery = User::where(function ($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('first_name', 'like', $search_str)
|
||||
->orWhere('last_name', 'like', $search_str)
|
||||
->orWhere('note', 'like', $search_str);
|
||||
}
|
||||
})->select('id');
|
||||
|
||||
$locationQuery = Location::where(function ($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('name', 'like', $search_str);
|
||||
}
|
||||
})->select('id');
|
||||
|
||||
$assetQuery = Asset::where(function ($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('name', 'like', $search_str);
|
||||
}
|
||||
})->select('id');
|
||||
|
||||
$query->where(function ($query) use ($userQuery) {
|
||||
$query->where('assigned_type', User::class)
|
||||
->whereIn('assigned_to', $userQuery);
|
||||
})->orWhere(function($query) use ($locationQuery) {
|
||||
$query->where('assigned_type', Location::class)
|
||||
->whereIn('assigned_to', $locationQuery);
|
||||
})->orWhere(function($query) use ($assetQuery) {
|
||||
$query->where('assigned_type', Asset::class)
|
||||
->whereIn('assigned_to', $assetQuery);
|
||||
})->orWhere(function($query) use ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$search_str = '%' . $term . '%';
|
||||
$query->where('note', 'like', $search_str);
|
||||
}
|
||||
});
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,7 +7,6 @@ use App\Presenters\Presentable;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* Model for the Actionlog (the table that keeps a historical log of
|
||||
@@ -17,10 +16,12 @@ use Illuminate\Support\Facades\Auth;
|
||||
*/
|
||||
class Actionlog extends SnipeModel
|
||||
{
|
||||
use CompanyableTrait;
|
||||
use HasFactory;
|
||||
|
||||
// This is to manually set the source (via setActionSource()) for determineActionSource()
|
||||
protected ?string $source = null;
|
||||
protected $with = ['admin'];
|
||||
|
||||
protected $presenter = \App\Presenters\ActionlogPresenter::class;
|
||||
use SoftDeletes;
|
||||
@@ -372,4 +373,9 @@ class Actionlog extends SnipeModel
|
||||
{
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
public function scopeOrderAdmin($query, $order)
|
||||
{
|
||||
return $query->leftJoin('users as admin_sort', 'action_logs.user_id', '=', 'admin_sort.id')->select('action_logs.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Events\AssetCheckedOut;
|
||||
use App\Events\CheckoutableCheckedOut;
|
||||
use App\Exceptions\CheckoutNotAllowed;
|
||||
use App\Helpers\Helper;
|
||||
@@ -31,6 +30,7 @@ class Asset extends Depreciable
|
||||
{
|
||||
|
||||
protected $presenter = AssetPresenter::class;
|
||||
protected $with = ['model', 'admin'];
|
||||
|
||||
use CompanyableTrait;
|
||||
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
||||
@@ -716,7 +716,7 @@ class Asset extends Depreciable
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function adminuser()
|
||||
public function admin()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'user_id');
|
||||
}
|
||||
|
||||
@@ -205,7 +205,11 @@ class Component extends SnipeModel
|
||||
public function numCheckedOut()
|
||||
{
|
||||
$checkedout = 0;
|
||||
foreach ($this->assets as $checkout) {
|
||||
|
||||
// In case there are elements checked out to assets that belong to a different company
|
||||
// than this asset and full multiple company support is on we'll remove the global scope,
|
||||
// so they are included in the count.
|
||||
foreach ($this->assets()->withoutGlobalScope(new CompanyableScope)->get() as $checkout) {
|
||||
$checkedout += $checkout->pivot->assigned_qty;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,9 @@ class Depreciable extends SnipeModel
|
||||
|
||||
if ($months_passed >= $this->get_depreciation()->months){
|
||||
//if there is a floor use it
|
||||
if(!$this->get_depreciation()->depreciation_min == null) {
|
||||
if($this->get_depreciation()->depreciation_min) {
|
||||
|
||||
$current_value = $this->get_depreciation()->depreciation_min;
|
||||
$current_value = $this->calculateDepreciation();
|
||||
|
||||
}else{
|
||||
$current_value = 0;
|
||||
@@ -86,7 +86,7 @@ class Depreciable extends SnipeModel
|
||||
}
|
||||
else {
|
||||
// The equation here is (Purchase_Cost-Floor_min)*(Months_passed/Months_til_depreciated)
|
||||
$current_value = round(($this->purchase_cost-($this->purchase_cost - ($this->get_depreciation()->depreciation_min)) * ($months_passed / $this->get_depreciation()->months)), 2);
|
||||
$current_value = round(($this->purchase_cost-($this->purchase_cost - ($this->calculateDepreciation())) * ($months_passed / $this->get_depreciation()->months)), 2);
|
||||
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class Depreciable extends SnipeModel
|
||||
|
||||
public function getMonthlyDepreciation(){
|
||||
|
||||
return ($this->purchase_cost-$this->get_depreciation()->depreciation_min)/$this->get_depreciation()->months;
|
||||
return ($this->purchase_cost-$this->calculateDepreciation())/$this->get_depreciation()->months;
|
||||
|
||||
}
|
||||
|
||||
@@ -191,4 +191,16 @@ class Depreciable extends SnipeModel
|
||||
{
|
||||
return new \DateTime($time);
|
||||
}
|
||||
|
||||
private function calculateDepreciation()
|
||||
{
|
||||
if($this->get_depreciation()->depreciation_type === 'percent') {
|
||||
$depreciation_percent= $this->get_depreciation()->depreciation_min / 100;
|
||||
$depreciation_min= $this->purchase_cost * $depreciation_percent;
|
||||
return $depreciation_min;
|
||||
}
|
||||
|
||||
$depreciation_min = $this->get_depreciation()->depreciation_min;
|
||||
return $depreciation_min;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,10 +108,11 @@ class Location extends SnipeModel
|
||||
{
|
||||
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->assets_count === 0)
|
||||
&& ($this->assigned_assets_count === 0)
|
||||
&& ($this->children_count === 0)
|
||||
&& ($this->users_count === 0);
|
||||
&& ($this->assets_count == 0)
|
||||
&& ($this->assigned_assets_count == 0)
|
||||
&& ($this->children_count == 0)
|
||||
&& ($this->accessories_count == 0)
|
||||
&& ($this->users_count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -331,7 +331,7 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
*/
|
||||
public function accessories()
|
||||
{
|
||||
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_users', 'assigned_to', 'accessory_id')
|
||||
return $this->belongsToMany(\App\Models\Accessory::class, 'accessories_checkout', 'assigned_to', 'accessory_id')
|
||||
->withPivot('id', 'created_at', 'note')->withTrashed()->orderBy('accessory_id');
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
|
||||
$this->item_tag = $params['item_tag'];
|
||||
$this->item_model = $params['item_model'];
|
||||
$this->item_serial = $params['item_serial'];
|
||||
$this->item_status = $params['item_status'];
|
||||
$this->accepted_date = Helper::getFormattedDateObject($params['accepted_date'], 'date', false);
|
||||
$this->assigned_to = $params['assigned_to'];
|
||||
$this->note = $params['note'];
|
||||
@@ -65,6 +66,7 @@ class AcceptanceAssetAcceptedNotification extends Notification
|
||||
'item_tag' => $this->item_tag,
|
||||
'item_model' => $this->item_model,
|
||||
'item_serial' => $this->item_serial,
|
||||
'item_status' => $this->item_status,
|
||||
'note' => $this->note,
|
||||
'accepted_date' => $this->accepted_date,
|
||||
'assigned_to' => $this->assigned_to,
|
||||
|
||||
@@ -24,6 +24,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
|
||||
$this->item_tag = $params['item_tag'];
|
||||
$this->item_model = $params['item_model'];
|
||||
$this->item_serial = $params['item_serial'];
|
||||
$this->item_status = $params['item_status'];
|
||||
$this->declined_date = Helper::getFormattedDateObject($params['declined_date'], 'date', false);
|
||||
$this->note = $params['note'];
|
||||
$this->assigned_to = $params['assigned_to'];
|
||||
@@ -63,6 +64,7 @@ class AcceptanceAssetDeclinedNotification extends Notification
|
||||
'item_tag' => $this->item_tag,
|
||||
'item_model' => $this->item_model,
|
||||
'item_serial' => $this->item_serial,
|
||||
'item_status' => $this->item_status,
|
||||
'note' => $this->note,
|
||||
'declined_date' => $this->declined_date,
|
||||
'assigned_to' => $this->assigned_to,
|
||||
|
||||
@@ -162,6 +162,7 @@ class CheckinAssetNotification extends Notification
|
||||
$message = (new MailMessage)->markdown('notifications.markdown.checkin-asset',
|
||||
[
|
||||
'item' => $this->item,
|
||||
'status' => $this->item->assetstatus?->name,
|
||||
'admin' => $this->admin,
|
||||
'note' => $this->note,
|
||||
'target' => $this->target,
|
||||
|
||||
@@ -209,6 +209,7 @@ public function toGoogleChat()
|
||||
[
|
||||
'item' => $this->item,
|
||||
'admin' => $this->admin,
|
||||
'status' => $this->item->assetstatus?->name,
|
||||
'note' => $this->note,
|
||||
'target' => $this->target,
|
||||
'fields' => $fields,
|
||||
|
||||
@@ -90,7 +90,7 @@ class AccessoryPresenter extends Presenter
|
||||
'visible' => false,
|
||||
'title' => trans('admin/accessories/general.remaining'),
|
||||
],[
|
||||
'field' => 'users_count',
|
||||
'field' => 'checkouts_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'visible' => true,
|
||||
|
||||
@@ -230,7 +230,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||
|| $user->can('update', Accessory::class)
|
||||
|| $user->can('create', Accessory::class)
|
||||
|| $user->can('update', User::class)
|
||||
|| $user->can('create', User::class);
|
||||
|| $user->can('create', User::class)
|
||||
|| ($user->hasAccess('reports.view'));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -337,12 +337,12 @@ class Saml
|
||||
/**
|
||||
* Get a setting.
|
||||
*
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
* @param string|array|int $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return void
|
||||
* @return mixed
|
||||
* @author Johnson Yi <jyi.dev@outlook.com>
|
||||
*
|
||||
*/
|
||||
public function getSetting($key, $default = null)
|
||||
{
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ class Label implements View
|
||||
|
||||
if ($settings->alt_barcode_enabled) {
|
||||
if ($template->getSupport1DBarcode()) {
|
||||
$barcode1DType = $settings->alt_barcode;
|
||||
$barcode1DType = $settings->label2_1d_type;
|
||||
if ($barcode1DType != 'none') {
|
||||
$assetData->put('barcode1d', (object)[
|
||||
'type' => $barcode1DType,
|
||||
|
||||
+4
-4
@@ -52,7 +52,7 @@
|
||||
"livewire/livewire": "^3.5",
|
||||
"neitanod/forceutf8": "^2.0",
|
||||
"nesbot/carbon": "^2.32",
|
||||
"nunomaduro/collision": "^6.1",
|
||||
"nunomaduro/collision": "^7.0",
|
||||
"okvpn/clock-lts": "^1.0",
|
||||
"onelogin/php-saml": "^3.4",
|
||||
"paragonie/constant_time_encoding": "^2.3",
|
||||
@@ -74,13 +74,13 @@
|
||||
"ext-exif": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^v6.4.4",
|
||||
"brianium/paratest": "^7.0",
|
||||
"fakerphp/faker": "^1.16",
|
||||
"larastan/larastan": "^2.9",
|
||||
"mockery/mockery": "^1.4",
|
||||
"nunomaduro/phpinsights": "^2.7",
|
||||
"nunomaduro/phpinsights": "^2.11",
|
||||
"php-mock/php-mock-phpunit": "^2.10",
|
||||
"phpunit/phpunit": "^9.6.19",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/css-selector": "^4.4",
|
||||
"symfony/dom-crawler": "^4.4",
|
||||
|
||||
Generated
+591
-627
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
return array (
|
||||
'app_version' => 'v7.0.10',
|
||||
'full_app_version' => 'v7.0.10 - build 14684-gc2bcc2e2d',
|
||||
'build_version' => '14684',
|
||||
'app_version' => 'v7.0.11',
|
||||
'full_app_version' => 'v7.0.11 - build 14904-g6c0cf9447',
|
||||
'build_version' => '14904',
|
||||
'prerelease_version' => '',
|
||||
'hash_version' => 'gc2bcc2e2d',
|
||||
'full_hash' => 'v7.0.10-311-gc2bcc2e2d',
|
||||
'hash_version' => 'g6c0cf9447',
|
||||
'full_hash' => 'v7.0.11-218-g6c0cf9447',
|
||||
'branch' => 'master',
|
||||
);
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Category;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
@@ -125,11 +126,12 @@ class AccessoryFactory extends Factory
|
||||
})->afterCreating(function ($accessory) {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => now(),
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => $user->id,
|
||||
'assigned_to' => $user->id,
|
||||
'assigned_type' => User::class,
|
||||
'note' => '',
|
||||
]);
|
||||
});
|
||||
@@ -145,11 +147,12 @@ class AccessoryFactory extends Factory
|
||||
public function checkedOutToUser(User $user = null)
|
||||
{
|
||||
return $this->afterCreating(function (Accessory $accessory) use ($user) {
|
||||
$accessory->users()->attach($accessory->id, [
|
||||
$accessory->checkouts()->create([
|
||||
'accessory_id' => $accessory->id,
|
||||
'created_at' => Carbon::now(),
|
||||
'user_id' => 1,
|
||||
'assigned_to' => $user->id ?? User::factory()->create()->id,
|
||||
'assigned_type' => User::class,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('depreciations', function (Blueprint $table) {
|
||||
$table->string('depreciation_type')->after('depreciation_min')->default('amount');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('depreciations', function (Blueprint $table) {
|
||||
$table->dropColumn('depreciation_type');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('accessories_users')) {
|
||||
Schema::rename('accessories_users', 'accessories_checkout');
|
||||
|
||||
Schema::table('accessories_checkout', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('accessories_checkout', 'assigned_type')) {
|
||||
$table->string('assigned_type')->nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DB::update('update '.DB::getTablePrefix().'accessories_checkout set assigned_type = \'App\\\\Models\\\\User\' where assigned_type is null', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasTable('accessories_checkout')) {
|
||||
Schema::table('accessories_checkout', function (Blueprint $table) {
|
||||
$table->dropColumn('assigned_type');
|
||||
});
|
||||
|
||||
Schema::rename('accessories_checkout', 'accessories_users');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->boolean('shortcuts_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropColumn('shortcuts_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('enable_sounds')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('enable_sounds');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -16,7 +16,7 @@ class AccessorySeeder extends Seeder
|
||||
public function run()
|
||||
{
|
||||
Accessory::truncate();
|
||||
DB::table('accessories_users')->truncate();
|
||||
DB::table('accessories_checkout')->truncate();
|
||||
|
||||
if (! Location::count()) {
|
||||
$this->call(LocationSeeder::class);
|
||||
|
||||
Generated
+8
-8
@@ -5,7 +5,7 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"acorn": "^8.12.0",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
@@ -23,7 +23,7 @@
|
||||
"imagemin": "^8.0.1",
|
||||
"jquery-slimscroll": "^1.3.8",
|
||||
"jquery-ui": "^1.13.3",
|
||||
"jquery-validation": "^1.20.1",
|
||||
"jquery-validation": "^1.21.0",
|
||||
"jquery.iframe-transport": "^1.0.0",
|
||||
"jspdf-autotable": "^3.8.2",
|
||||
"less": "^4.2.0",
|
||||
@@ -1918,9 +1918,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-free": {
|
||||
"version": "6.5.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz",
|
||||
"integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -7064,9 +7064,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jquery-validation": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.20.1.tgz",
|
||||
"integrity": "sha512-rbBy36Xe5WBCO8OLdZLhPhVjb70KayuoX3WYRNwNpy9TXuUadhNTcaipr6jEIacn+V4jgXB2xUJl6hYzJxr5jw==",
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.21.0.tgz",
|
||||
"integrity": "sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w==",
|
||||
"peerDependencies": {
|
||||
"jquery": "^1.7 || ^2.0 || ^3.1"
|
||||
}
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
"postcss": "^8.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"acorn": "^8.12.0",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"admin-lte": "^2.4.18",
|
||||
@@ -43,7 +43,7 @@
|
||||
"imagemin": "^8.0.1",
|
||||
"jquery-slimscroll": "^1.3.8",
|
||||
"jquery-ui": "^1.13.3",
|
||||
"jquery-validation": "^1.20.1",
|
||||
"jquery-validation": "^1.21.0",
|
||||
"jquery.iframe-transport": "^1.0.0",
|
||||
"jspdf-autotable": "^3.8.2",
|
||||
"less": "^4.2.0",
|
||||
|
||||
+8
-10
@@ -1,21 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
backupStaticProperties="false"
|
||||
bootstrap="bootstrap/autoload.php"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||
>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">app/</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
@@ -33,4 +26,9 @@
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<ini name="display_errors" value="true"/>
|
||||
</php>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">app/</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+41
-39
@@ -1,24 +1,25 @@
|
||||
{
|
||||
"/js/build/app.js": "/js/build/app.js?id=05dea4c19ca75f9fc68915a82c341f24",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=3a0b83c0a9919e91f7c795a1971382fd",
|
||||
"/css/build/app.css": "/css/build/app.css?id=d6fd5ea2989e7ab278745d995c167ae7",
|
||||
"/js/build/app.js": "/js/build/app.js?id=ae9230922bbd81534aabec227fd0cbe6",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=5f3abb12a286d6cb8aa523322d7f053b",
|
||||
"/css/dist/skins/_all-skins.css": "/css/dist/skins/_all-skins.css?id=89b7dcd91db033fb19ebbd7f7dcc0c35",
|
||||
"/css/build/overrides.css": "/css/build/overrides.css?id=693e2f5274862fc13e4d2453b0b58631",
|
||||
"/css/build/app.css": "/css/build/app.css?id=1431db05a5856d8efc731120da7caa38",
|
||||
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=a67bd93bed52e6a29967fe472de66d6c",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=dd5eb6c76770bacaa2e960849d275516",
|
||||
"/css/dist/skins/skin-red.css": "/css/dist/skins/skin-red.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=eb99dbccf43841b18be1b3ffc69d9497",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=b391d15ee01e2aaffe90554eae9fde0b",
|
||||
"/css/dist/skins/skin-orange.css": "/css/dist/skins/skin-orange.css?id=268041e902b019730c23ee3875838005",
|
||||
"/css/dist/skins/skin-orange-dark.css": "/css/dist/skins/skin-orange-dark.css?id=8a12dfa3bef796fc17178890e0aff8d6",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=34023bf46b7c2486b7468de9b750dbff",
|
||||
"/css/dist/skins/skin-yellow-dark.css": "/css/dist/skins/skin-yellow-dark.css?id=dd5eb6c76770bacaa2e960849d275516",
|
||||
"/css/dist/skins/skin-yellow.css": "/css/dist/skins/skin-yellow.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
"/css/dist/skins/skin-purple-dark.css": "/css/dist/skins/skin-purple-dark.css?id=b391d15ee01e2aaffe90554eae9fde0b",
|
||||
"/css/dist/skins/skin-purple.css": "/css/dist/skins/skin-purple.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-red-dark.css": "/css/dist/skins/skin-red-dark.css?id=eb99dbccf43841b18be1b3ffc69d9497",
|
||||
"/css/dist/skins/skin-black-dark.css": "/css/dist/skins/skin-black-dark.css?id=5f3abb12a286d6cb8aa523322d7f053b",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=198553147983f411db55d774009bf481",
|
||||
"/css/dist/skins/skin-green.css": "/css/dist/skins/skin-green.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-green-dark.css": "/css/dist/skins/skin-green-dark.css?id=198553147983f411db55d774009bf481",
|
||||
"/css/dist/skins/skin-contrast.css": "/css/dist/skins/skin-contrast.css?id=f0fbbb0ac729ea092578fb05ca615460",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=fba2adaeb1f10de7b4f6628260ee6ef2",
|
||||
"/css/dist/skins/skin-blue.css": "/css/dist/skins/skin-blue.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/dist/skins/skin-blue-dark.css": "/css/dist/skins/skin-blue-dark.css?id=34023bf46b7c2486b7468de9b750dbff",
|
||||
"/css/dist/skins/skin-black.css": "/css/dist/skins/skin-black.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=74363ddd27850adaa2c4b1ed18f866e3",
|
||||
"/css/dist/signature-pad.css": "/css/dist/signature-pad.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/css/dist/signature-pad.min.css": "/css/dist/signature-pad.min.css?id=6a89d3cd901305e66ced1cf5f13147f7",
|
||||
"/js/select2/i18n/af.js": "/js/select2/i18n/af.js?id=4f6fcd73488ce79fae1b7a90aceaecde",
|
||||
@@ -81,33 +82,34 @@
|
||||
"/js/select2/i18n/vi.js": "/js/select2/i18n/vi.js?id=097a5b75b3e146e2d94ab8e1510be607",
|
||||
"/js/select2/i18n/zh-CN.js": "/js/select2/i18n/zh-CN.js?id=2cff662ec5f972b4613566cf5988cda2",
|
||||
"/js/select2/i18n/zh-TW.js": "/js/select2/i18n/zh-TW.js?id=04554a227c2ba0f3bb6ca3d2e01e5440",
|
||||
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=0141634c24336be626e05c8b77d1fa27",
|
||||
"/css/webfonts/fa-brands-400.woff2": "/css/webfonts/fa-brands-400.woff2?id=b3cf7a6dd618bd392f3ddcc61343a463",
|
||||
"/css/webfonts/fa-regular-400.ttf": "/css/webfonts/fa-regular-400.ttf?id=9cf69d99de9d83f82466a647f5cb1f94",
|
||||
"/css/webfonts/fa-regular-400.woff2": "/css/webfonts/fa-regular-400.woff2?id=f0549181a126fe40849a53792bb0e077",
|
||||
"/css/webfonts/fa-solid-900.ttf": "/css/webfonts/fa-solid-900.ttf?id=509c0e46de844df754d10179cf03c953",
|
||||
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=96d16b1bdb177fd796c810b9e706c780",
|
||||
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=8994b282f9f3b7a00380bb1e2731a4bf",
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=111e341dba724e1df946e8d1f406a7bd",
|
||||
"/css/webfonts/fa-brands-400.ttf": "/css/webfonts/fa-brands-400.ttf?id=67c719c0d21dfb09066b4ecdc2310cb8",
|
||||
"/css/webfonts/fa-brands-400.woff2": "/css/webfonts/fa-brands-400.woff2?id=ea42d181254fd563d365feb2b03a07f4",
|
||||
"/css/webfonts/fa-regular-400.ttf": "/css/webfonts/fa-regular-400.ttf?id=60e2f6dc1bf9de9da0aa485ccf81b40e",
|
||||
"/css/webfonts/fa-regular-400.woff2": "/css/webfonts/fa-regular-400.woff2?id=f43a0d63897b14b80bcf10f2fae2d6f8",
|
||||
"/css/webfonts/fa-solid-900.ttf": "/css/webfonts/fa-solid-900.ttf?id=d1d4daff53a4c06220c5b90933fca9dc",
|
||||
"/css/webfonts/fa-solid-900.woff2": "/css/webfonts/fa-solid-900.woff2?id=541cafc702f56f57de95f3d1f792f428",
|
||||
"/css/webfonts/fa-v4compatibility.ttf": "/css/webfonts/fa-v4compatibility.ttf?id=51ade19e1b10d7a0031b18568a2b01d5",
|
||||
"/css/webfonts/fa-v4compatibility.woff2": "/css/webfonts/fa-v4compatibility.woff2?id=1cc408d68a27c3757b4460bbc542433e",
|
||||
"/js/dist/bootstrap-table-locale-all.min.js": "/js/dist/bootstrap-table-locale-all.min.js?id=27eb00f47f9bae70cd630d184b7969f1",
|
||||
"/js/dist/bootstrap-table-en-US.min.js": "/js/dist/bootstrap-table-en-US.min.js?id=57bdb4770b2924f5efeda100caf3c9b7",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=8abbb6aea625ec64cd7ebdad77ebf6e5",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=e27070bdbc5fce3bfd132b952d641fd6",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=859e11e4e6b05c84e4b7302de29bac5e",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=d4e3181b505407e7bd10b1fd802ae109",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=198553147983f411db55d774009bf481",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/_all-skins.min.css": "/css/dist/skins/_all-skins.min.css?id=89b7dcd91db033fb19ebbd7f7dcc0c35",
|
||||
"/css/dist/skins/skin-black-dark.min.css": "/css/dist/skins/skin-black-dark.min.css?id=5f3abb12a286d6cb8aa523322d7f053b",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/dist/skins/skin-black.min.css": "/css/dist/skins/skin-black.min.css?id=1f33ca3d860461c1127ec465ab3ebb6b",
|
||||
"/css/dist/skins/skin-blue-dark.min.css": "/css/dist/skins/skin-blue-dark.min.css?id=34023bf46b7c2486b7468de9b750dbff",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=dd5eb6c76770bacaa2e960849d275516",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=eb99dbccf43841b18be1b3ffc69d9497",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=b391d15ee01e2aaffe90554eae9fde0b",
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=268041e902b019730c23ee3875838005",
|
||||
"/css/dist/skins/skin-blue.min.css": "/css/dist/skins/skin-blue.min.css?id=392cc93cfc0be0349bab9697669dd091",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=f0fbbb0ac729ea092578fb05ca615460",
|
||||
"/css/dist/skins/skin-green-dark.min.css": "/css/dist/skins/skin-green-dark.min.css?id=198553147983f411db55d774009bf481",
|
||||
"/css/dist/skins/skin-green.min.css": "/css/dist/skins/skin-green.min.css?id=b48f4d8af0e1ca5621c161e93951109f",
|
||||
"/css/dist/skins/skin-orange-dark.min.css": "/css/dist/skins/skin-orange-dark.min.css?id=8a12dfa3bef796fc17178890e0aff8d6",
|
||||
"/css/dist/skins/skin-contrast.min.css": "/css/dist/skins/skin-contrast.min.css?id=f0fbbb0ac729ea092578fb05ca615460"
|
||||
"/css/dist/skins/skin-orange.min.css": "/css/dist/skins/skin-orange.min.css?id=268041e902b019730c23ee3875838005",
|
||||
"/css/dist/skins/skin-purple-dark.min.css": "/css/dist/skins/skin-purple-dark.min.css?id=b391d15ee01e2aaffe90554eae9fde0b",
|
||||
"/css/dist/skins/skin-purple.min.css": "/css/dist/skins/skin-purple.min.css?id=cf6c8c340420724b02d6e787ef9bded5",
|
||||
"/css/dist/skins/skin-red-dark.min.css": "/css/dist/skins/skin-red-dark.min.css?id=eb99dbccf43841b18be1b3ffc69d9497",
|
||||
"/css/dist/skins/skin-red.min.css": "/css/dist/skins/skin-red.min.css?id=b9a74ec0cd68f83e7480d5ae39919beb",
|
||||
"/css/dist/skins/skin-yellow-dark.min.css": "/css/dist/skins/skin-yellow-dark.min.css?id=dd5eb6c76770bacaa2e960849d275516",
|
||||
"/css/dist/skins/skin-yellow.min.css": "/css/dist/skins/skin-yellow.min.css?id=fc7adb943668ac69fe4b646625a7571f",
|
||||
"/css/dist/bootstrap-table.css": "/css/dist/bootstrap-table.css?id=8abbb6aea625ec64cd7ebdad77ebf6e5",
|
||||
"/js/build/vendor.js": "/js/build/vendor.js?id=c1c24b883f48dc3d16b817aed0b457cc",
|
||||
"/js/dist/bootstrap-table.js": "/js/dist/bootstrap-table.js?id=859e11e4e6b05c84e4b7302de29bac5e",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=9dc81f7efc796fdef51858d1a3fae2a2"
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -363,8 +363,10 @@ body {
|
||||
}
|
||||
|
||||
@media print {
|
||||
a[href]:after {
|
||||
content: none;
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 0mm;
|
||||
}
|
||||
|
||||
.tab-content > .tab-pane {
|
||||
@@ -372,8 +374,136 @@ body {
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.img-responsive {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 1024px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0 auto;
|
||||
line-height: 1em;
|
||||
word-spacing:1px;
|
||||
letter-spacing:0.2px;
|
||||
font: 15px "Times New Roman", Times, serif;
|
||||
background:white;
|
||||
color:black;
|
||||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
|
||||
/* avoid page-breaks inside a listingContainer*/
|
||||
.listingContainer {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font: 28px "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font: 24px "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font: 20px "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
/* Improve colour contrast of links */
|
||||
a:link, a:visited {
|
||||
color: #781351
|
||||
}
|
||||
|
||||
/* URL */
|
||||
a:link, a:visited {
|
||||
background: transparent;
|
||||
color:#333;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a[href]:after {
|
||||
content: "" !important;
|
||||
}
|
||||
|
||||
a[href^="http://"] {
|
||||
color:#000;
|
||||
}
|
||||
|
||||
#header {
|
||||
height:75px;
|
||||
font-size: 24pt;
|
||||
color:black
|
||||
}
|
||||
|
||||
div.row-new-striped {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.pagination-detail, .fixed-table-toolbar {
|
||||
visibility: hidden;
|
||||
}
|
||||
.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 .col-sm-pull-3 .col-sm-push-9 {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.col-sm-12 {
|
||||
width: 100%;
|
||||
}
|
||||
.col-sm-11 {
|
||||
width: 91.66666666666666%;
|
||||
}
|
||||
.col-sm-10 {
|
||||
width: 83.33333333333334%;
|
||||
}
|
||||
.col-sm-9 {
|
||||
width: 75%;
|
||||
}
|
||||
.col-sm-8 {
|
||||
width: 66.66666666666666%;
|
||||
}
|
||||
.col-sm-7 {
|
||||
width: 58.333333333333336%;
|
||||
}
|
||||
.col-sm-6 {
|
||||
width: 50%;
|
||||
}
|
||||
.col-sm-5 {
|
||||
width: 41.66666666666667%;
|
||||
}
|
||||
.col-sm-4 {
|
||||
width: 33.33333333333333%;
|
||||
}
|
||||
.col-sm-3 {
|
||||
width: 25%;
|
||||
}
|
||||
.col-sm-2 {
|
||||
width: 16.666666666666664%;
|
||||
}
|
||||
.col-sm-1 {
|
||||
width: 8.333333333333332%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.select2-selection__choice__remove {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.select2-selection--multiple {
|
||||
border-color: #d2d6de !important;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.select2-selection__choice {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
|
||||
img.navbar-brand-img, .navbar-brand>img {
|
||||
float: left;
|
||||
padding: 5px 5px 5px 0;
|
||||
@@ -714,6 +844,11 @@ th.css-location > .th-inner::before {
|
||||
margin-top:50px
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1318px) and (min-width: 1200px){
|
||||
.box{
|
||||
height:170px;
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
@@ -880,6 +1015,10 @@ input[type="radio"]:checked::before {
|
||||
padding-left:15px;
|
||||
}
|
||||
|
||||
.nav-tabs-custom > .nav-tabs > li.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/** --------------------------------------- **/
|
||||
/** End checkbox styles to replace iCheck **/
|
||||
/** --------------------------------------- **/
|
||||
|
||||
@@ -12,4 +12,5 @@ return array(
|
||||
'api_reference' => 'Please check the <a href="https://snipe-it.readme.io/reference" target="_blank">API reference</a> to find specific API endpoints and additional API documentation.',
|
||||
'profile_updated' => 'Account successfully updated',
|
||||
'no_tokens' => 'You have not created any personal access tokens.',
|
||||
'enable_sounds' => 'Enable sound effects',
|
||||
);
|
||||
|
||||
@@ -218,6 +218,8 @@ return [
|
||||
'webhook_integration_help' => ':app integration is optional, however the endpoint and channel are required if you wish to use it. To configure :app integration, you must first <a href=":webhook_link" target="_new" rel="noopener">create an incoming webhook</a> on your :app account. Click on the <strong>Test :app Integration</strong> button to confirm your settings are correct before saving. ',
|
||||
'webhook_integration_help_button' => 'Once you have saved your :app information, a test button will appear.',
|
||||
'webhook_test_help' => 'Test whether your :app integration is configured correctly. YOU MUST SAVE YOUR UPDATED :app SETTINGS FIRST.',
|
||||
'shortcuts_enabled' => 'Enable Shortcuts',
|
||||
'shortcuts_help_text' => '<strong>Windows</strong>: Alt + Access key, <strong>Mac</strong>: Control + Option + Access key',
|
||||
'snipe_version' => 'Snipe-IT version',
|
||||
'support_footer' => 'Support Footer Links ',
|
||||
'support_footer_help' => 'Specify who sees the links to the Snipe-IT Support info and Users Manual',
|
||||
|
||||
@@ -279,6 +279,7 @@ return [
|
||||
'site_name' => 'Site Name',
|
||||
'state' => 'State',
|
||||
'status_labels' => 'Status Labels',
|
||||
'status_label' => 'Status Label',
|
||||
'status' => 'Status',
|
||||
'accept_eula' => 'Acceptance Agreement',
|
||||
'supplier' => 'Supplier',
|
||||
|
||||
@@ -125,6 +125,8 @@ return [
|
||||
'symbols' => 'The :attribute field must contain at least one symbol.',
|
||||
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
|
||||
],
|
||||
'percent' => 'The depreciation minimum must be between 0 and 100 when depreciation type is percentage.',
|
||||
|
||||
'present' => 'The :attribute field must be present.',
|
||||
'present_if' => 'The :attribute field must be present when :other is :value.',
|
||||
'present_unless' => 'The :attribute field must be present unless :other is :value.',
|
||||
@@ -188,6 +190,8 @@ return [
|
||||
'hashed_pass' => 'Your current password is incorrect',
|
||||
'dumbpwd' => 'That password is too common.',
|
||||
'statuslabel_type' => 'You must select a valid status label type',
|
||||
'custom_field_not_found' => 'This field does not seem to exist, please double check your custom field names.',
|
||||
'custom_field_not_found_on_model' => 'This field seems to exist, but is not available on this Asset Model\'s fieldset.',
|
||||
|
||||
// date_format validation with slightly less stupid messages. It duplicates a lot, but it gets the job done :(
|
||||
// We use this because the default error message for date_format is reflects php Y-m-d, which non-PHP
|
||||
|
||||
@@ -66,7 +66,8 @@
|
||||
</div>
|
||||
<!-- User -->
|
||||
|
||||
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.select_user'), 'fieldname' => 'assigned_to', 'required'=> 'true'])
|
||||
@include ('partials.forms.edit.user-select', ['translated_name' => trans('general.select_user'), 'fieldname' => 'assigned_user', 'required'=> 'true'])
|
||||
|
||||
|
||||
<!-- Checkout QTY -->
|
||||
<div class="form-group {{ $errors->has('checkout_qty') ? 'error' : '' }} ">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
@section('header_right')
|
||||
@can('create', \App\Models\Accessory::class)
|
||||
<a href="{{ route('accessories.create') }}" accesskey="n" class="btn btn-primary pull-right"> {{ trans('general.create') }}</a>
|
||||
<a href="{{ route('accessories.create') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=n" : ''}} class="btn btn-primary pull-right"> {{ trans('general.create') }}</a>
|
||||
@endcan
|
||||
@stop
|
||||
|
||||
|
||||
@@ -68,9 +68,9 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table
|
||||
data-cookie-id-table="usersTable"
|
||||
data-cookie-id-table="checkoutsTable"
|
||||
data-pagination="true"
|
||||
data-id-table="usersTable"
|
||||
data-id-table="checkoutsTable"
|
||||
data-search="true"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
@@ -78,16 +78,16 @@
|
||||
data-show-export="true"
|
||||
data-show-refresh="true"
|
||||
data-sort-order="asc"
|
||||
id="usersTable"
|
||||
id="checkoutsTable"
|
||||
class="table table-striped snipe-table"
|
||||
data-url="{{ route('api.accessories.checkedout', $accessory->id) }}"
|
||||
data-export-options='{
|
||||
"fileName": "export-accessories-{{ str_slug($accessory->name) }}-users-{{ date('Y-m-d') }}",
|
||||
"fileName": "export-accessories-{{ str_slug($accessory->name) }}-checkouts-{{ date('Y-m-d') }}",
|
||||
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
|
||||
}'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-searchable="false" data-formatter="usersLinkFormatter" data-sortable="false" data-field="name">{{ trans('general.user') }}</th>
|
||||
<th data-searchable="false" data-formatter="polymorphicItemFormatter" data-sortable="false" data-field="assigned_to">{{ trans('general.checked_out_to') }}</th>
|
||||
<th data-searchable="false" data-sortable="false" data-field="checkout_notes">{{ trans('general.notes') }}</th>
|
||||
<th data-searchable="false" data-formatter="dateDisplayFormatter" data-sortable="false" data-field="last_checkout">{{ trans('admin/hardware/table.checkout_date') }}</th>
|
||||
<th data-searchable="false" data-sortable="false" data-field="actions" data-formatter="accessoriesInOutFormatter">{{ trans('table.actions') }}</th>
|
||||
@@ -314,7 +314,7 @@
|
||||
<strong>{{ trans('general.checked_out') }}</strong>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{ $accessory->users_count }}
|
||||
{{ $accessory->checkouts_count }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -337,7 +337,7 @@
|
||||
@endcan
|
||||
|
||||
@can('delete', $accessory)
|
||||
@if ($accessory->users_count == 0)
|
||||
@if ($accessory->checkouts_count == 0)
|
||||
<div class="text-center" style="padding-top:5px;">
|
||||
<button class="btn btn-block btn-danger delete-asset" style="padding-top:5px;" data-toggle="modal" data-title="{{ trans('general.delete') }}" data-content="{{ trans('general.delete_confirm_no_undo', ['item' => $accessory->name]) }}" data-target="#dataConfirmModal">
|
||||
{{ trans('general.delete') }}
|
||||
|
||||
@@ -89,6 +89,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group{{ $errors->has('enable_sounds') ? ' has-error' : '' }}">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<label for="enable_sounds" class="form-control">
|
||||
<input type="checkbox" name="enable_sounds" value="1" {{ old('enable_sounds', $user->enable_sounds) ? 'checked' : '' }}>
|
||||
{{ trans('account/general.enable_sounds') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gravatar Email -->
|
||||
<div class="form-group {{ $errors->has('gravatar') ? ' has-error' : '' }}">
|
||||
<label for="gravatar" class="col-md-3 control-label">{{ trans('general.gravatar_email') }}
|
||||
@@ -152,6 +161,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
</div> <!-- .box-body -->
|
||||
<div class="text-right box-footer">
|
||||
<a class="btn btn-link" href="{{ URL::previous() }}">{{ trans('button.cancel') }}</a>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
@section('header_right')
|
||||
@can('create', \App\Models\Component::class)
|
||||
<a href="{{ route('components.create') }}" accesskey="n" class="btn btn-primary pull-right"> {{ trans('general.create') }}</a>
|
||||
<a href="{{ route('components.create') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=n" : ''}} class="btn btn-primary pull-right"> {{ trans('general.create') }}</a>
|
||||
@endcan
|
||||
@stop
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
@section('header_right')
|
||||
@can('create', \App\Models\Consumable::class)
|
||||
<a href="{{ route('consumables.create') }}" accesskey="n" class="btn btn-primary pull-right"> {{ trans('general.create') }}</a>
|
||||
<a href="{{ route('consumables.create') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "accesskey=n" : ''}} class="btn btn-primary pull-right"> {{ trans('general.create') }}</a>
|
||||
@endcan
|
||||
@stop
|
||||
|
||||
|
||||
@@ -28,9 +28,13 @@
|
||||
<label for="depreciation_min" class="col-md-3 control-label">
|
||||
{{ trans('admin/depreciations/general.depreciation_min') }}
|
||||
</label>
|
||||
<div class="col-md-2" style="padding-left:0px">
|
||||
<input class="form-control" name="depreciation_min" id="depreciation_min" value="{{ old('depreciation_min', $item->depreciation_min) }}" style="width: 80px; margin-left:15px" />
|
||||
<div class="col-md-2" style="display: flex;">
|
||||
<input class="form-control" name="depreciation_min" id="depreciation_min" value="{{ old('depreciation_min', $item->depreciation_min) }}" style="width: 80px; margin-right: 15px; display: inline-block;" />
|
||||
<select class="form-control select2" name="depreciation_type" id="depreciation_type" data-minimum-results-for-search="Infinity" style="width: 150px; display: inline-block;">
|
||||
<option value="amount" {{ old('depreciation_type', $item->depreciation_type) == 'amount' ? 'selected' : '' }}>Amount</option>
|
||||
<option value="percent" {{ old('depreciation_type', $item->depreciation_type) == 'percent' ? 'selected' : '' }}>Percentage</option>
|
||||
</select>
|
||||
</div>
|
||||
{!! $errors->first('depreciation_min', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
{!! $errors->first('depreciation_min', '<span class="col-md-7 col-md-offset-3 alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
@stop
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{{ trans('admin/hardware/table.id') }}</td>
|
||||
<td>{{ trans('admin/hardware/table.name') }}</td>
|
||||
<td>{{ trans('general.asset_name') }}</td>
|
||||
<td>{{ trans('admin/hardware/table.location')}}</td>
|
||||
<td>{{ trans('admin/hardware/table.assigned_to') }}</td>
|
||||
</tr>
|
||||
|
||||
@@ -35,6 +35,25 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Name -->
|
||||
<div class="form-group {{ $errors->has('name') ? ' has-error' : '' }}">
|
||||
<label for="name" class="col-md-3 control-label">
|
||||
{{ trans('admin/hardware/form.name') }}
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" name="name" id="name" value="{{ old('name') }}" maxlength="100" style="width:100%">
|
||||
{!! $errors->first('name', '<span class="alert-msg" aria-hidden="true">
|
||||
<i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('null_name', '1', false) }}
|
||||
{{ trans_choice('general.set_to_null', count($assets), ['asset_count' => count($assets)]) }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Purchase Date -->
|
||||
<div class="form-group {{ $errors->has('purchase_date') ? ' has-error' : '' }}">
|
||||
<label for="purchase_date" class="col-md-3 control-label">{{ trans('admin/hardware/form.date') }}</label>
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
|
||||
<x-redirect_submit_options
|
||||
index_route="hardware.index"
|
||||
:button_label="trans('general.checkout')"
|
||||
:button_label="trans('general.checkin')"
|
||||
:disabled_select="!$asset->model"
|
||||
:options="[
|
||||
'index' => trans('admin/hardware/form.redirect_to_all', ['type' => 'assets']),
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<a href="{{ route('reports/custom') }}" style="margin-right: 5px;" class="btn btn-default">
|
||||
{{ trans('admin/hardware/general.custom_export') }}</a>
|
||||
@can('create', \App\Models\Asset::class)
|
||||
<a href="{{ route('hardware.create') }}" accesskey="n" class="btn btn-primary pull-right"></i> {{ trans('general.create') }}</a>
|
||||
<a href="{{ route('hardware.create') }}" {{$snipeSettings->shortcuts_enabled == 1 ? "n" : ''}} class="btn btn-primary pull-right"></i> {{ trans('general.create') }}</a>
|
||||
@endcan
|
||||
|
||||
@stop
|
||||
@@ -73,6 +73,7 @@
|
||||
data-pagination="true"
|
||||
data-id-table="assetsListingTable"
|
||||
data-search="true"
|
||||
data-search-text="{{ e(Session::get('search')) }}"
|
||||
data-side-pagination="server"
|
||||
data-show-columns="true"
|
||||
data-show-export="true"
|
||||
|
||||
@@ -33,14 +33,25 @@
|
||||
<div class="form-group {{ $errors->has('asset_tag') ? 'error' : '' }}">
|
||||
{{ Form::label('asset_tag', trans('general.asset_tag'), array('class' => 'col-md-3 control-label', 'id' => 'checkin_tag')) }}
|
||||
<div class="col-md-9">
|
||||
<div class="input-group date col-md-5" data-date-format="yyyy-mm-dd">
|
||||
<input type="text" class="form-control" name="asset_tag" id="asset_tag" value="{{ old('asset_tag') }}">
|
||||
<div class="input-group col-md-11 required">
|
||||
<input type="text" class="form-control" name="asset_tag" id="asset_tag" value="{{ old('asset_tag') }}" required>
|
||||
|
||||
</div>
|
||||
{!! $errors->first('asset_tag', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Status -->
|
||||
<div class="form-group {{ $errors->has('status_id') ? 'error' : '' }}">
|
||||
<label for="status_id" class="col-md-3 control-label">
|
||||
{{ trans('admin/hardware/form.status') }}
|
||||
</label>
|
||||
<div class="col-md-7">
|
||||
{{ Form::select('status_id', $statusLabel_list, '', array('class'=>'select2', 'style'=>'width:100%','', 'aria-label'=>'status_id')) }}
|
||||
{!! $errors->first('status_id', '<span class="alert-msg" aria-hidden="true"><i class="fas fa-times" aria-hidden="true"></i> :message</span>') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Locations -->
|
||||
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
|
||||
|
||||
@@ -129,6 +140,12 @@
|
||||
success : function (data) {
|
||||
if (data.status == 'success') {
|
||||
$('#checkedin tbody').prepend("<tr class='success'><td>" + data.payload.asset_tag + "</td><td>" + data.payload.model + "</td><td>" + data.payload.model_number + "</td><td>" + data.messages + "</td><td><i class='fas fa-check text-success'></i></td></tr>");
|
||||
|
||||
@if ($user->enable_sounds)
|
||||
var audio = new Audio('{{ config('app.url') }}/sounds/success.mp3');
|
||||
audio.play()
|
||||
@endif
|
||||
|
||||
incrementOnSuccess();
|
||||
} else {
|
||||
handlecheckinFail(data);
|
||||
@@ -148,6 +165,12 @@
|
||||
});
|
||||
|
||||
function handlecheckinFail (data) {
|
||||
|
||||
@if ($user->enable_sounds)
|
||||
var audio = new Audio('{{ config('app.url') }}/sounds/error.mp3');
|
||||
audio.play()
|
||||
@endif
|
||||
|
||||
if (data.payload.asset_tag) {
|
||||
var asset_tag = data.payload.asset_tag;
|
||||
var model = data.payload.model;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="form-group {{ $errors->has('asset_tag') ? 'error' : '' }}">
|
||||
{{ Form::label('asset_tag', trans('general.asset_tag'), array('class' => 'col-md-3 control-label', 'id' => 'audit_tag')) }}
|
||||
<div class="col-md-9">
|
||||
<div class="input-group date col-md-5" data-date-format="yyyy-mm-dd">
|
||||
<div class="input-group date col-md-11 required" data-date-format="yyyy-mm-dd">
|
||||
<input type="text" class="form-control" name="asset_tag" id="asset_tag" value="{{ old('asset_tag') }}">
|
||||
|
||||
</div>
|
||||
@@ -49,10 +49,13 @@
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-md-9">
|
||||
<label class="form-control">
|
||||
<input type="checkbox" value="1" name="update_location" {{ old('update_location') == '1' ? ' checked="checked"' : '' }}> {{ trans('admin/hardware/form.asset_location') }}
|
||||
<input type="checkbox" value="1" name="update_location" {{ old('update_location') == '1' ? ' checked="checked"' : '' }}>
|
||||
<span>{{ trans('admin/hardware/form.asset_location') }}
|
||||
<a href="#" class="text-dark-gray" tabindex="0" role="button" data-toggle="popover" data-trigger="focus" title="<i class='far fa-life-ring'></i> {{ trans('general.more_info') }}" data-html="true" data-content="{{ trans('general.quickscan_bulk_help') }}"><i class="far fa-life-ring"></i></a></span>
|
||||
|
||||
</label>
|
||||
|
||||
<a href="#" class="text-dark-gray" tabindex="0" role="button" data-toggle="popover" data-trigger="focus" title="<i class='far fa-life-ring'></i>"{{ trans('general.more_info') }} data-html="true" data-content="{{ trans('general.quickscan_bulk_help') }}"><i class="far fa-life-ring"></i></a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -153,7 +156,13 @@
|
||||
data : formData,
|
||||
success : function (data) {
|
||||
if (data.status == 'success') {
|
||||
$('#audited tbody').prepend("<tr class='success'><td>" + data.payload.asset_tag + "</td><td>" + data.messages + "</td><td><i class='fas fa-check text-success'></i></td></tr>");
|
||||
$('#audited tbody').prepend("<tr class='success'><td>" + data.payload.asset_tag + "</td><td>" + data.messages + "</td><td><i class='fas fa-check text-success' style='font-size:18px;'></i></td></tr>");
|
||||
|
||||
@if ($user->enable_sounds)
|
||||
var audio = new Audio('{{ config('app.url') }}/sounds/success.mp3');
|
||||
audio.play()
|
||||
@endif
|
||||
|
||||
incrementOnSuccess();
|
||||
} else {
|
||||
handleAuditFail(data);
|
||||
@@ -173,6 +182,10 @@
|
||||
});
|
||||
|
||||
function handleAuditFail (data) {
|
||||
@if ($user->enable_sounds)
|
||||
var audio = new Audio('{{ config('app.url') }}/sounds/error.mp3');
|
||||
audio.play()
|
||||
@endif
|
||||
if (data.asset_tag) {
|
||||
var asset_tag = data.asset_tag;
|
||||
} else {
|
||||
@@ -183,7 +196,7 @@
|
||||
} else {
|
||||
var messages = '';
|
||||
}
|
||||
$('#audited tbody').prepend("<tr class='danger'><td>" + data.payload.asset_tag + "</td><td>" + messages + "</td><td><i class='fas fa-times text-danger'></i></td></tr>");
|
||||
$('#audited tbody').prepend("<tr class='danger'><td>" + data.payload.asset_tag + "</td><td>" + messages + "</td><td><i class='fas fa-times text-danger' style='font-size:18px;'></i></td></tr>");
|
||||
}
|
||||
|
||||
function incrementOnSuccess() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user