Compare commits
1 Commits
attempt-at
...
wysiwyg-ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
023bf32dca |
@@ -4235,33 +4235,6 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "smarsching",
|
||||
"name": "Sebastian Marsching",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2880129?v=4",
|
||||
"profile": "http://sebastian.marsching.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mohammad-ahmadi1",
|
||||
"name": "Mo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40658372?v=4",
|
||||
"profile": "https://github.com/mohammad-ahmadi1",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MarvelousAnything",
|
||||
"name": "Owen V. Hayes",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/20994684?v=4",
|
||||
"profile": "https://github.com/MarvelousAnything",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -190,14 +190,13 @@ APP_ALLOW_INSECURE_HOSTS=false
|
||||
GOOGLE_MAPS_API=
|
||||
LDAP_MEM_LIM=500M
|
||||
LDAP_TIME_LIM=600
|
||||
BACKUP_TIME_LIMIT=600
|
||||
IMPORT_TIME_LIMIT=600
|
||||
IMPORT_MEMORY_LIMIT=500M
|
||||
REPORT_TIME_LIMIT=12000
|
||||
API_THROTTLE_PER_MINUTE=120
|
||||
CSV_ESCAPE_FORMULAS=true
|
||||
LIVEWIRE_URL_PREFIX=null
|
||||
MAX_UNPAGINATED=5000
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# OPTIONAL: SAML SETTINGS
|
||||
|
||||
60
.github/ISSUE_TEMPLATE/Bug-Report.yml
vendored
60
.github/ISSUE_TEMPLATE/Bug-Report.yml
vendored
@@ -23,23 +23,7 @@ body:
|
||||
attributes:
|
||||
label: Snipe-IT Version
|
||||
description: What version of Snipe-IT are you seeing this issue on? You can find the version number in the footer of any page in Snipe-IT.
|
||||
placeholder: ex. v8.3.2 - build 19577 (master)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: php-version
|
||||
attributes:
|
||||
label: PHP Version
|
||||
description: What version of PHP are you running? You can find the version of PHP your webserver is running in the `Admin Settings` section in the footer, and the cli version by running `php -v` via command line .
|
||||
placeholder: ex. v8.3.1 (web), PHP 8.4.12 (cli)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: composer-version
|
||||
attributes:
|
||||
label: Composer Version
|
||||
description: What version of composer are you running? You can find the version number by running `composer --version`.
|
||||
placeholder: ex. 2.8.10
|
||||
placeholder: ex. v8.3.1 - build 19577 (master)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@@ -64,16 +48,6 @@ body:
|
||||
- Not sure
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: upgrade-or-fresh
|
||||
attributes:
|
||||
label: Is this a fresh install or an upgrade?
|
||||
options:
|
||||
- Fresh install
|
||||
- Upgrade
|
||||
- NA
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
@@ -93,38 +67,6 @@ body:
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
- Other
|
||||
- type: dropdown
|
||||
id: on-demo
|
||||
attributes:
|
||||
label: Can you reproduce this on the public demo?
|
||||
description: You can check this at https://demo.snipeitapp.com.
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
- N/A
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: fmcs
|
||||
attributes:
|
||||
label: Do you have full multiple company support enabled?
|
||||
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: fmcs-location
|
||||
attributes:
|
||||
label: If you have full multiple company support enabled, do you have location scoping to company enabled?
|
||||
description: You can check this in your Snipe-IT installation at `Admin Settings > General Settings > Scoping`.
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
- I do not have full multiple company support enabled
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: server-logs
|
||||
attributes:
|
||||
|
||||
6
.github/workflows/SA-codeql.yml
vendored
6
.github/workflows/SA-codeql.yml
vendored
@@ -30,10 +30,10 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
2
.github/workflows/codacy-analysis.yml
vendored
2
.github/workflows/codacy-analysis.yml
vendored
@@ -52,6 +52,6 @@ jobs:
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/tests-mysql.yml
vendored
2
.github/workflows/tests-mysql.yml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
|
||||
- name: Upload Laravel logs as artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
2
.github/workflows/tests-postgres.yml
vendored
2
.github/workflows/tests-postgres.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Upload Laravel logs as artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
2
.github/workflows/tests-sqlite.yml
vendored
2
.github/workflows/tests-sqlite.yml
vendored
@@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
- name: Upload Laravel logs as artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }}
|
||||
path: |
|
||||
|
||||
@@ -68,8 +68,7 @@ Thanks goes to all of these wonderful people ([emoji key](https://github.com/ken
|
||||
| [<img src="https://avatars.githubusercontent.com/u/181059?v=4" width="110px;"/><br /><sub>Juan Font</sub>](https://github.com/juanfont)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juanfont "Code") | [<img src="https://avatars.githubusercontent.com/u/13137708?v=4" width="110px;"/><br /><sub>Juho Taipale</sub>](https://github.com/juhotaipale)<br />[💻](https://github.com/snipe/snipe-it/commits?author=juhotaipale "Code") | [<img src="https://avatars.githubusercontent.com/u/1007419?v=4" width="110px;"/><br /><sub>Korvin Szanto</sub>](https://github.com/KorvinSzanto)<br />[💻](https://github.com/snipe/snipe-it/commits?author=KorvinSzanto "Code") | [<img src="https://avatars.githubusercontent.com/u/8513053?v=4" width="110px;"/><br /><sub>Lewis Foster</sub>](https://lewisfoster.foo/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=sniff122 "Code") | [<img src="https://avatars.githubusercontent.com/u/33877541?v=4" width="110px;"/><br /><sub>Logan Swartzendruber</sub>](https://github.com/loganswartz)<br />[💻](https://github.com/snipe/snipe-it/commits?author=loganswartz "Code") | [<img src="https://avatars.githubusercontent.com/u/1156208?v=4" width="110px;"/><br /><sub>Lorenzo P.</sub>](https://github.com/lopezio)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lopezio "Code") | [<img src="https://avatars.githubusercontent.com/u/33946590?v=4" width="110px;"/><br /><sub>Lukas Jung</sub>](https://github.com/m4us1ne)<br />[💻](https://github.com/snipe/snipe-it/commits?author=m4us1ne "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/10965027?v=4" width="110px;"/><br /><sub>Ellie</sub>](https://leafedfox.xyz/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=LeafedFox "Code") | [<img src="https://avatars.githubusercontent.com/u/20960555?v=4" width="110px;"/><br /><sub>GA Stamper</sub>](https://github.com/gastamper)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gastamper "Code") | [<img src="https://avatars.githubusercontent.com/u/206553556?v=4" width="110px;"/><br /><sub>Guillaume Lefranc</sub>](https://github.com/gl-pup)<br />[💻](https://github.com/snipe/snipe-it/commits?author=gl-pup "Code") | [<img src="https://avatars.githubusercontent.com/u/733892?v=4" width="110px;"/><br /><sub>Hajo Möller</sub>](https://github.com/dasjoe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=dasjoe "Code") | [<img src="https://avatars.githubusercontent.com/u/3420063?v=4" width="110px;"/><br /><sub>Istvan Basa</sub>](https://github.com/pottom)<br />[💻](https://github.com/snipe/snipe-it/commits?author=pottom "Code") | [<img src="https://avatars.githubusercontent.com/u/810824?v=4" width="110px;"/><br /><sub>JJ Asghar</sub>](https://jjasghar.github.io/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jjasghar "Code") | [<img src="https://avatars.githubusercontent.com/u/40404495?v=4" width="110px;"/><br /><sub>James E. Msenga</sub>](https://github.com/JemCdo)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JemCdo "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/6865786?v=4" width="110px;"/><br /><sub>Jan Felix Wiebe</sub>](https://github.com/jfwiebe)<br />[💻](https://github.com/snipe/snipe-it/commits?author=jfwiebe "Code") | [<img src="https://avatars.githubusercontent.com/u/43412008?v=4" width="110px;"/><br /><sub>Jo Drexl</sub>](https://www.nfon.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=drexljo "Code") | [<img src="https://avatars.githubusercontent.com/u/4807843?v=4" width="110px;"/><br /><sub>Austin Sasko</sub>](https://github.com/austinsasko)<br />[💻](https://github.com/snipe/snipe-it/commits?author=austinsasko "Code") | [<img src="https://avatars.githubusercontent.com/u/4875039?v=4" width="110px;"/><br /><sub>Jasson</sub>](http://jassoncordones.github.io)<br />[💻](https://github.com/snipe/snipe-it/commits?author=JassonCordones "Code") | [<img src="https://avatars.githubusercontent.com/u/76069640?v=4" width="110px;"/><br /><sub>Okean</sub>](https://github.com/Tinyblargon)<br />[💻](https://github.com/snipe/snipe-it/commits?author=Tinyblargon "Code") | [<img src="https://avatars.githubusercontent.com/u/6515064?v=4" width="110px;"/><br /><sub>Alejandro Medrano</sub>](https://www.lst.tfo.upm.es/alejandro-medrano/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=amedranogil "Code") | [<img src="https://avatars.githubusercontent.com/u/58696401?v=4" width="110px;"/><br /><sub>Lukas Kraic</sub>](https://github.com/lukaskraic)<br />[💻](https://github.com/snipe/snipe-it/commits?author=lukaskraic "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") | [<img src="https://avatars.githubusercontent.com/u/14185442?v=4" width="110px;"/><br /><sub>Michael Strobel</sub>](https://strobelm.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=strobelm "Code") | [<img src="https://avatars.githubusercontent.com/u/634790?v=4" width="110px;"/><br /><sub>Nicky West</sub>](http://nickwest.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nickwest "Code") | [<img src="https://avatars.githubusercontent.com/u/1347327?v=4" width="110px;"/><br /><sub>akaspeh1</sub>](https://github.com/akaspeh1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akaspeh1 "Code") | [<img src="https://avatars.githubusercontent.com/u/2880129?v=4" width="110px;"/><br /><sub>Sebastian Marsching</sub>](http://sebastian.marsching.com/)<br />[💻](https://github.com/snipe/snipe-it/commits?author=smarsching "Code") | [<img src="https://avatars.githubusercontent.com/u/40658372?v=4" width="110px;"/><br /><sub>Mo</sub>](https://github.com/mohammad-ahmadi1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mohammad-ahmadi1 "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/20994684?v=4" width="110px;"/><br /><sub>Owen V. Hayes</sub>](https://github.com/MarvelousAnything)<br />[💻](https://github.com/snipe/snipe-it/commits?author=MarvelousAnything "Code") |
|
||||
| [<img src="https://avatars.githubusercontent.com/u/1571724?v=4" width="110px;"/><br /><sub>Герхард PICCORO Lenz McKAY </sub>](https://github-readme-stats.vercel.app/api?username=mckaygerhard)<br />[💻](https://github.com/snipe/snipe-it/commits?author=mckaygerhard "Code") | [<img src="https://avatars.githubusercontent.com/u/15015119?v=4" width="110px;"/><br /><sub>Johannes Pollitt</sub>](https://github.com/FlorestanII)<br />[💻](https://github.com/snipe/snipe-it/commits?author=FlorestanII "Code") | [<img src="https://avatars.githubusercontent.com/u/14185442?v=4" width="110px;"/><br /><sub>Michael Strobel</sub>](https://strobelm.de)<br />[💻](https://github.com/snipe/snipe-it/commits?author=strobelm "Code") | [<img src="https://avatars.githubusercontent.com/u/634790?v=4" width="110px;"/><br /><sub>Nicky West</sub>](http://nickwest.me)<br />[💻](https://github.com/snipe/snipe-it/commits?author=nickwest "Code") | [<img src="https://avatars.githubusercontent.com/u/1347327?v=4" width="110px;"/><br /><sub>akaspeh1</sub>](https://github.com/akaspeh1)<br />[💻](https://github.com/snipe/snipe-it/commits?author=akaspeh1 "Code") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Categories;
|
||||
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DestroyCategoryAction
|
||||
{
|
||||
/**
|
||||
* @throws ItemStillHasAssets
|
||||
* @throws ItemStillHasAssetModels
|
||||
* @throws ItemStillHasComponents
|
||||
* @throws ItemStillHasAccessories
|
||||
* @throws ItemStillHasLicenses
|
||||
* @throws ItemStillHasConsumables
|
||||
*/
|
||||
static function run(Category $category): bool
|
||||
{
|
||||
$category->loadCount([
|
||||
'assets as assets_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
'licenses as licenses_count',
|
||||
'models as models_count'
|
||||
]);
|
||||
|
||||
if ($category->assets_count > 0) {
|
||||
throw new ItemStillHasAssets($category);
|
||||
}
|
||||
if ($category->accessories_count > 0) {
|
||||
throw new ItemStillHasAccessories($category);
|
||||
}
|
||||
if ($category->consumables_count > 0) {
|
||||
throw new ItemStillHasConsumables($category);
|
||||
}
|
||||
if ($category->components_count > 0) {
|
||||
throw new ItemStillHasComponents($category);
|
||||
}
|
||||
if ($category->licenses_count > 0) {
|
||||
throw new ItemStillHasLicenses($category);
|
||||
}
|
||||
if ($category->models_count > 0) {
|
||||
throw new ItemStillHasAssetModels($category);
|
||||
}
|
||||
|
||||
Storage::disk('public')->delete('categories'.'/'.$category->image);
|
||||
$category->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Manufacturers;
|
||||
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DeleteManufacturerAction
|
||||
{
|
||||
/**
|
||||
* @throws ItemStillHasAssets
|
||||
* @throws ItemStillHasComponents
|
||||
* @throws ItemStillHasAccessories
|
||||
* @throws ItemStillHasLicenses
|
||||
* @throws ItemStillHasConsumables
|
||||
*/
|
||||
static function run(Manufacturer $manufacturer): bool
|
||||
{
|
||||
$manufacturer->loadCount([
|
||||
'assets as assets_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
'licenses as licenses_count',
|
||||
]);
|
||||
|
||||
if ($manufacturer->assets_count > 0) {
|
||||
throw new ItemStillHasAssets($manufacturer);
|
||||
}
|
||||
if ($manufacturer->accessories_count > 0) {
|
||||
throw new ItemStillHasAccessories($manufacturer);
|
||||
}
|
||||
if ($manufacturer->consumables_count > 0) {
|
||||
throw new ItemStillHasConsumables($manufacturer);
|
||||
}
|
||||
if ($manufacturer->components_count > 0) {
|
||||
throw new ItemStillHasComponents($manufacturer);
|
||||
}
|
||||
if ($manufacturer->licenses_count > 0) {
|
||||
throw new ItemStillHasLicenses($manufacturer);
|
||||
}
|
||||
|
||||
if ($manufacturer->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
$manufacturer->delete();
|
||||
//dd($manufacturer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Suppliers;
|
||||
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Models\Supplier;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DestroySupplierAction
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @throws ItemStillHasLicenses
|
||||
* @throws ItemStillHasAssets
|
||||
* @throws ItemStillHasMaintenances
|
||||
* @throws ItemStillHasAccessories
|
||||
* @throws ItemStillHasConsumables
|
||||
* @throws ItemStillHasComponents
|
||||
*/
|
||||
static function run(Supplier $supplier): bool
|
||||
{
|
||||
$supplier->loadCount([
|
||||
'maintenances as maintenances_count',
|
||||
'assets as assets_count',
|
||||
'licenses as licenses_count',
|
||||
'accessories as accessories_count',
|
||||
'consumables as consumables_count',
|
||||
'components as components_count',
|
||||
]);
|
||||
if ($supplier->assets_count > 0) {
|
||||
throw new ItemStillHasAssets($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->maintenances_count > 0) {
|
||||
throw new ItemStillHasMaintenances($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
throw new ItemStillHasLicenses($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->accessories_count > 0) {
|
||||
throw new ItemStillHasAccessories($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->consumables_count > 0) {
|
||||
throw new ItemStillHasConsumables($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->components_count > 0) {
|
||||
throw new ItemStillHasComponents($supplier);
|
||||
}
|
||||
|
||||
if ($supplier->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('suppliers/'.$supplier->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -317,21 +317,9 @@ class LdapSync extends Command
|
||||
if($ldap_map["jobtitle"] != null){
|
||||
$user->jobtitle = $item['jobtitle'];
|
||||
}
|
||||
if($ldap_map["address"] != null){
|
||||
$user->address = $item['address'];
|
||||
}
|
||||
if($ldap_map["city"] != null){
|
||||
$user->city = $item['city'];
|
||||
}
|
||||
if($ldap_map["state"] != null){
|
||||
$user->state = $item['state'];
|
||||
}
|
||||
if($ldap_map["country"] != null){
|
||||
$user->country = $item['country'];
|
||||
}
|
||||
if($ldap_map["zip"] != null){
|
||||
$user->zip = $item['zip'];
|
||||
}
|
||||
if($ldap_map["dept"] != null){
|
||||
$user->department_id = $department->id;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Console\Helper\ProgressIndicator;
|
||||
|
||||
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
|
||||
|
||||
/**
|
||||
* Class ObjectImportCommand
|
||||
@@ -50,9 +52,6 @@ class ObjectImportCommand extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('max_execution_time', env('IMPORT_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', env('IMPORT_MEMORY_LIMIT', '500M'));
|
||||
|
||||
$this->progressIndicator = new ProgressIndicator($this->output);
|
||||
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RemoveInvalidUploadDeleteActionLogItems extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:remove-invalid-upload-delete-action-log-items';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Permanently remove invalid "upload deleted" action log items that have a null filename. This command can potentially result in deleted files being "resurrected" in the UI.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$invalidLogs = Actionlog::query()
|
||||
->where('action_type', 'upload deleted')
|
||||
->whereNull('filename')
|
||||
->withTrashed()
|
||||
->get();
|
||||
|
||||
$this->info("{$invalidLogs->count()} invalid log items found.");
|
||||
|
||||
if ($invalidLogs->count() === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->table(['ID', 'Action Type', 'Item Type', 'Item ID', 'Created At', 'Deleted At'], $invalidLogs->map(fn($log) => [
|
||||
$log->id,
|
||||
$log->action_type,
|
||||
$log->item_type,
|
||||
$log->item_id,
|
||||
$log->created_at,
|
||||
$log->deleted_at,
|
||||
])->toArray());
|
||||
|
||||
if ($this->confirm("Do you wish to remove {$invalidLogs->count()} log items?")) {
|
||||
$invalidLogs->each(fn($log) => $log->forceDelete());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace App\Console\Commands;
|
||||
use Illuminate\Console\Command;
|
||||
use ZipArchive;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
class SQLStreamer {
|
||||
private $input;
|
||||
@@ -243,10 +242,9 @@ class RestoreFromBackup extends Command
|
||||
|
||||
$private_dirs = [
|
||||
'storage/private_uploads/accessories',
|
||||
'storage/private_uploads/assetmodels' => 'storage/private_uploads/models', //this was changed from assetmodels => models Aug 10 2025
|
||||
'storage/private_uploads/asset_maintenances' => 'storage/private_uploads/maintenances', //this was changed from asset_maintenances => maintenances Aug 10 2025
|
||||
'storage/private_uploads/maintenances', //but let 'maintenances' take precedence
|
||||
'storage/private_uploads/models', //and let 'models' take precedence
|
||||
'storage/private_uploads/assetmodels',
|
||||
'storage/private_uploads/maintenances',
|
||||
'storage/private_uploads/models',
|
||||
'storage/private_uploads/assets', // these are asset _files_, not the pictures.
|
||||
'storage/private_uploads/audits',
|
||||
'storage/private_uploads/components',
|
||||
@@ -264,7 +262,7 @@ class RestoreFromBackup extends Command
|
||||
];
|
||||
$public_dirs = [
|
||||
'public/uploads/accessories',
|
||||
// 'public/uploads/assetmodels' => 'public/uploads/models', //according to git, this was _never_ a thing... (see below)
|
||||
'public/uploads/assetmodels',
|
||||
'public/uploads/maintenances',
|
||||
'public/uploads/assets', // these are asset _pictures_, not asset files
|
||||
'public/uploads/avatars',
|
||||
@@ -275,7 +273,7 @@ class RestoreFromBackup extends Command
|
||||
'public/uploads/departments',
|
||||
'public/uploads/locations',
|
||||
'public/uploads/manufacturers',
|
||||
'public/uploads/models', // ...it's been this way for 9 years (as of late 2025)
|
||||
'public/uploads/models',
|
||||
'public/uploads/suppliers',
|
||||
];
|
||||
|
||||
@@ -288,6 +286,8 @@ class RestoreFromBackup extends Command
|
||||
'public/uploads/favicon-uploaded.*',
|
||||
];
|
||||
|
||||
$all_files = $private_dirs + $public_dirs;
|
||||
|
||||
$sqlfiles = [];
|
||||
$sqlfile_indices = [];
|
||||
|
||||
@@ -295,20 +295,6 @@ class RestoreFromBackup extends Command
|
||||
$boring_files = [];
|
||||
$unsafe_files = [];
|
||||
|
||||
$good_extensions = config('filesystems.allowed_upload_extensions_array');
|
||||
|
||||
$private_extensions = array_merge($good_extensions, ["csv", "key"]); //add csv, and 'key'
|
||||
$public_extensions = array_diff($good_extensions, ["xml"]); //remove xml
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
|
||||
/**
|
||||
* TODO: I _hate_ the "continue 3" thing we keep doing here
|
||||
* I think a better approach might be to have the "each file" stuff be in a method on this class, and the
|
||||
* boring_files and interesting_files be properties on it that we fill out. Then, in that method, we could
|
||||
* just do a 'return' once the file is actually handled (yay or nay). We could also start to break out some of
|
||||
* the _other_ things that we do into their own methods too? But I don't care about that as much.
|
||||
*/
|
||||
for ($i = 0; $i < $za->numFiles; $i++) {
|
||||
$stat_results = $za->statIndex($i);
|
||||
// echo "index: $i\n";
|
||||
@@ -323,7 +309,7 @@ class RestoreFromBackup extends Command
|
||||
// skip macOS resource fork files (?!?!?!)
|
||||
if (strpos($raw_path, '__MACOSX') !== false && strpos($raw_path, '._') !== false) {
|
||||
//print "SKIPPING macOS Resource fork file: $raw_path\n";
|
||||
// $boring_files[] = $raw_path; //stop adding this to the boring files list; it's just confusing
|
||||
$boring_files[] = $raw_path;
|
||||
continue;
|
||||
}
|
||||
if (@pathinfo($raw_path, PATHINFO_EXTENSION) == 'sql') {
|
||||
@@ -332,70 +318,44 @@ class RestoreFromBackup extends Command
|
||||
$sqlfile_indices[] = $i;
|
||||
continue;
|
||||
}
|
||||
if ($raw_path[-1] == '/') {
|
||||
//last character is '/' - this is a directory, and we don't need it, and we don't need to warn about it
|
||||
continue;
|
||||
}
|
||||
if (in_array(basename($raw_path), [".gitkeep", ".gitignore", ".DS_Store"])) {
|
||||
//skip these boring files silently without reporting on them; they're stupid
|
||||
continue;
|
||||
}
|
||||
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
|
||||
|
||||
foreach (['public' => $public_dirs, 'private' => $private_dirs] as $purpose => $dirs) {
|
||||
$allowed_extensions = match ($purpose) {
|
||||
'public' => $public_extensions,
|
||||
'private' => $private_extensions,
|
||||
};
|
||||
foreach ($dirs as $dir => $destdir) {
|
||||
if (is_int($dir)) {
|
||||
$dir = $destdir;
|
||||
}
|
||||
$last_pos = strrpos($raw_path, $dir . '/');
|
||||
if ($last_pos !== false) {
|
||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
|
||||
//the CSV bit, below, is because we store CSV files as "blahcsv" - without an extension
|
||||
if (!in_array($extension, $allowed_extensions) && !($dir == "storage/private_uploads/imports" && substr($raw_path, -3) == "csv" && $extension == "")) {
|
||||
$unsafe_files[] = $raw_path;
|
||||
Log::debug($raw_path . ' from directory ' . $dir . ' is being skipped');
|
||||
} else {
|
||||
if ($dir != $destdir) {
|
||||
Log::debug("Getting ready to save file $raw_path to new directory $destdir");
|
||||
}
|
||||
$interesting_files[$raw_path] = ['dest' => $destdir, 'index' => $i];
|
||||
}
|
||||
continue 3;
|
||||
foreach (array_merge($private_dirs, $public_dirs) as $dir) {
|
||||
$last_pos = strrpos($raw_path, $dir . '/');
|
||||
if ($last_pos !== false) {
|
||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $dir - last_pos+strlen(\$dir) is: ".($last_pos+strlen($dir))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||
//print("We would copy $raw_path to $dir.\n"); //FIXME append to a path?
|
||||
$interesting_files[$raw_path] = ['dest' => $dir, 'index' => $i];
|
||||
continue 2;
|
||||
if ($last_pos + strlen($dir) + 1 == strlen($raw_path)) {
|
||||
// we don't care about that; we just want files with the appropriate prefix
|
||||
//print("FOUND THE EXACT DIRECTORY: $dir AT: $raw_path!!!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (['public' => $public_files, 'private' => $private_files] as $purpose => $files) {
|
||||
$allowed_extensions = match ($purpose) {
|
||||
'public' => $public_extensions,
|
||||
'private' => $private_extensions,
|
||||
};
|
||||
foreach ($files as $file) {
|
||||
$has_wildcard = (strpos($file, '*') !== false);
|
||||
if ($has_wildcard) {
|
||||
$file = substr($file, 0, -1); //trim last character (which should be the wildcard)
|
||||
$good_extensions = config('filesystems.allowed_upload_extensions_array');
|
||||
|
||||
foreach (array_merge($private_files, $public_files) as $file) {
|
||||
$has_wildcard = (strpos($file, '*') !== false);
|
||||
if ($has_wildcard) {
|
||||
$file = substr($file, 0, -1); //trim last character (which should be the wildcard)
|
||||
}
|
||||
$last_pos = strrpos($raw_path, $file); // no trailing slash!
|
||||
if ($last_pos !== false) {
|
||||
$extension = strtolower(pathinfo($raw_path, PATHINFO_EXTENSION));
|
||||
if (!in_array($extension, $good_extensions)) {
|
||||
// gathering potentially unsafe files here to return at exit
|
||||
$unsafe_files[] = $raw_path;
|
||||
Log::debug('Potentially unsafe file '.$raw_path.' is being skipped');
|
||||
$boring_files[] = $raw_path;
|
||||
continue 2;
|
||||
}
|
||||
$last_pos = strrpos($raw_path, $file); // no trailing slash!
|
||||
if ($last_pos !== false) {
|
||||
if (!in_array($extension, $allowed_extensions)) {
|
||||
// gathering potentially unsafe files here to return at exit
|
||||
$unsafe_files[] = $raw_path;
|
||||
Log::debug('Potentially unsafe file ' . $raw_path . ' is being skipped');
|
||||
$boring_files[] = $raw_path;
|
||||
continue 3;
|
||||
}
|
||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||
//no wildcards found in $file, process 'normally'
|
||||
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
|
||||
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
|
||||
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
|
||||
continue 3;
|
||||
}
|
||||
//print("INTERESTING - last_pos is $last_pos when searching $raw_path for $file - last_pos+strlen(\$file) is: ".($last_pos+strlen($file))." and strlen(\$rawpath) is: ".strlen($raw_path)."\n");
|
||||
//no wildcards found in $file, process 'normally'
|
||||
if ($last_pos + strlen($file) == strlen($raw_path) || $has_wildcard) { //again, no trailing slash. or this is a wildcard and we just take it.
|
||||
// print("FOUND THE EXACT FILE: $file AT: $raw_path!!!\n"); //we *do* care about this, though.
|
||||
$interesting_files[$raw_path] = ['dest' => dirname($file), 'index' => $i];
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,25 +492,18 @@ class RestoreFromBackup extends Command
|
||||
}
|
||||
foreach ($interesting_files as $pretty_file_name => $file_details) {
|
||||
$ugly_file_name = $za->statIndex($file_details['index'])['name'];
|
||||
$migrated_file_name = $file_details['dest'] . '/' . basename($pretty_file_name);
|
||||
if (strcasecmp(substr($pretty_file_name, -4), ".svg") === 0) {
|
||||
$svg_contents = $za->getFromIndex($file_details['index']);
|
||||
$cleaned_svg = $sanitizer->sanitize($svg_contents);
|
||||
file_put_contents($migrated_file_name, $cleaned_svg);
|
||||
} else {
|
||||
$fp = $za->getStream($ugly_file_name);
|
||||
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
||||
if (!is_dir($file_details['dest'])) {
|
||||
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
|
||||
}
|
||||
$migrated_file = fopen($migrated_file_name, 'w');
|
||||
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
|
||||
fwrite($migrated_file, $buffer);
|
||||
}
|
||||
fclose($migrated_file);
|
||||
fclose($fp);
|
||||
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
|
||||
$fp = $za->getStream($ugly_file_name);
|
||||
//$this->info("Weird problem, here are file details? ".print_r($file_details,true));
|
||||
if (!is_dir($file_details['dest'])) {
|
||||
mkdir($file_details['dest'], 0755, true); //0755 is what Laravel uses, so we do that
|
||||
}
|
||||
$migrated_file = fopen($file_details['dest'].'/'.basename($pretty_file_name), 'w');
|
||||
while (($buffer = fgets($fp, SQLStreamer::$buffer_size)) !== false) {
|
||||
fwrite($migrated_file, $buffer);
|
||||
}
|
||||
fclose($migrated_file);
|
||||
fclose($fp);
|
||||
//$this->info("Wrote $ugly_file_name to $pretty_file_name");
|
||||
if ($bar) {
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
@@ -3,18 +3,13 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Mail\UnacceptedAssetReminderMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\CheckoutAssetNotification;
|
||||
use App\Notifications\CurrentInventory;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendAcceptanceReminder extends Command
|
||||
@@ -31,7 +26,7 @@ class SendAcceptanceReminder extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'This will resend users with unaccepted items a reminder to accept or decline them.';
|
||||
protected $description = 'This will resend users with unaccepted assets a reminder to accept or decline them.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@@ -50,30 +45,19 @@ class SendAcceptanceReminder extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$pending = CheckoutAcceptance::query()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $morph) {
|
||||
$morph->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'adminuser', 'company', 'checkouts'],
|
||||
Accessory::class => ['category', 'company', 'checkouts'],
|
||||
LicenseSeat::class => ['user', 'license', 'checkouts'],
|
||||
Component::class => ['assignedTo', 'company', 'checkouts'],
|
||||
Consumable::class => ['company', 'checkouts'],
|
||||
]);
|
||||
},
|
||||
'assignedTo',
|
||||
])
|
||||
->whereHasMorph(
|
||||
'checkoutable',
|
||||
[Asset::class, Accessory::class, LicenseSeat::class, Component::class, Consumable::class],
|
||||
fn ($q) => $q->whereNull('accepted_at')
|
||||
->whereNull('declined_at')
|
||||
)
|
||||
->pending()
|
||||
->get();
|
||||
$pending = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')
|
||||
->whereHas('checkoutable', function($query) {
|
||||
$query->where('accepted_at', null)
|
||||
->where('declined_at', null);
|
||||
})
|
||||
->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model', 'checkoutable.adminuser'])
|
||||
->get();
|
||||
|
||||
$count = 0;
|
||||
$unacceptedAssetGroups = $pending
|
||||
->filter(function($acceptance) {
|
||||
return $acceptance->checkoutable_type == 'App\Models\Asset';
|
||||
})
|
||||
->map(function($acceptance) {
|
||||
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
|
||||
})
|
||||
|
||||
@@ -9,8 +9,6 @@ use App\Notifications\ExpectedCheckinAdminNotification;
|
||||
use App\Notifications\ExpectedCheckinNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use App\Helpers\Helper;
|
||||
|
||||
class SendExpectedCheckinAlerts extends Command
|
||||
{
|
||||
@@ -19,7 +17,7 @@ class SendExpectedCheckinAlerts extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:expected-checkin {--with-output : Display the results in a table in your console in addition to sending the email}';
|
||||
protected $name = 'snipeit:expected-checkin';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -44,47 +42,19 @@ class SendExpectedCheckinAlerts extends Command
|
||||
public function handle()
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
$interval = $settings->due_checkin_days ?? 0;
|
||||
$interval = $settings->audit_warning_days ?? 0;
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval);
|
||||
$count = 0;
|
||||
|
||||
if (!$this->option('with-output')) {
|
||||
$this->info('Run this command with the --with-output option to see the full list in the console.');
|
||||
}
|
||||
|
||||
|
||||
$assets = Asset::whereNull('deleted_at')->DueOrOverdueForCheckin($settings)->orderBy('assets.expected_checkin', 'desc')->get();
|
||||
|
||||
$this->info($assets->count().' assets must be checked on or before '.Helper::getFormattedDateObject($interval_date, 'date', false));
|
||||
$this->info($assets->count().' assets must be checked in on or before '.$interval_date.' is deadline');
|
||||
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
if ($asset->assignedTo && (isset($asset->assignedTo->email)) && ($asset->assignedTo->email!='') && $asset->checkedOutToUser()) {
|
||||
$this->info('Sending User ExpectedCheckinNotification to: '.$asset->assignedTo->email);
|
||||
$asset->assignedTo->notify((new ExpectedCheckinNotification($asset)));
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('with-output')) {
|
||||
if (($assets) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('admin/hardware/form.tag'),
|
||||
trans('admin/hardware/form.model'),
|
||||
trans('general.model_no'),
|
||||
trans('general.purchase_date'),
|
||||
trans('admin/hardware/form.expected_checkin'),
|
||||
],
|
||||
$assets->map(fn($assets) => [
|
||||
trans('general.id') => $assets->id,
|
||||
trans('admin/hardware/form.tag') => $assets->asset_tag,
|
||||
trans('admin/hardware/form.model') => $assets->model->name,
|
||||
trans('general.model_no') => $assets->model->model_number,
|
||||
trans('general.purchase_date') => $assets->purchase_date_formatted,
|
||||
trans('admin/hardware/form.eol_date') => $assets->expected_checkin_formattedDate ? $assets->expected_checkin_formattedDate . ' (' . $assets->expected_checkin_diff_for_humans . ')' : '',
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,11 +63,10 @@ class SendExpectedCheckinAlerts extends Command
|
||||
$recipients = collect(explode(',', $settings->alert_email))->map(function ($item) {
|
||||
return new AlertRecipient($item);
|
||||
});
|
||||
Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
|
||||
$this->info('Sending Admin ExpectedCheckinNotification to: '.$settings->alert_email);
|
||||
\Notification::send($recipients, new ExpectedCheckinAdminNotification($assets));
|
||||
|
||||
}
|
||||
|
||||
$this->info('Sent checkin reminders to to '.$count.' users.');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,57 +60,19 @@ class SendExpirationAlerts extends Command
|
||||
Mail::to($recipients)->send(new ExpiringAssetsMail($assets, $alert_interval));
|
||||
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('admin/hardware/form.tag'),
|
||||
trans('admin/hardware/form.model'),
|
||||
trans('general.model_no'),
|
||||
trans('general.purchase_date'),
|
||||
trans('admin/hardware/form.eol_rate'),
|
||||
trans('admin/hardware/form.eol_date'),
|
||||
trans('admin/hardware/form.warranty_expires'),
|
||||
],
|
||||
$assets->map(fn($item) =>
|
||||
[
|
||||
trans('general.id') => $item->id,
|
||||
trans('admin/hardware/form.tag') => $item->asset_tag,
|
||||
trans('admin/hardware/form.model') => $item->model->name,
|
||||
trans('general.model_no') => $item->model->model_number,
|
||||
trans('general.purchase_date') => $item->purchase_date_formatted,
|
||||
trans('admin/hardware/form.eol_rate') => $item->model->eol,
|
||||
trans('admin/hardware/form.eol_date') => $item->eol_date ? $item->eol_formatted_date .' ('.$item->eol_diff_for_humans.')' : '',
|
||||
trans('admin/hardware/form.warranty_expires') => $item->warranty_expires ? $item->warranty_expires_formatted_date .' ('.$item->warranty_expires_diff_for_humans.')' : '',
|
||||
])
|
||||
);
|
||||
['ID', 'Tag', 'Model', 'Model Number', 'EOL', 'EOL Months', 'Warranty Expires', 'Warranty Months'],
|
||||
$assets->map(fn($item) => ['ID' => $item->id, 'Tag' => $item->asset_tag, 'Model' => $item->model->name, 'Model Number' => $item->model->model_number, 'EOL' => $item->asset_eol_date, 'EOL Months' => $item->model->eol, 'Warranty Expires' => $item->warranty_expires, 'Warranty Months' => $item->warranty_months])
|
||||
);
|
||||
}
|
||||
|
||||
// Expiring licenses
|
||||
$licenses = License::query()->ExpiringLicenses($alert_interval)
|
||||
->with('manufacturer','category')
|
||||
->orderBy('expiration_date', 'ASC')
|
||||
->orderBy('termination_date', 'ASC')
|
||||
->get();
|
||||
$licenses = License::getExpiringLicenses($alert_interval);
|
||||
if ($licenses->count() > 0) {
|
||||
Mail::to($recipients)->send(new ExpiringLicenseMail($licenses, $alert_interval));
|
||||
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('general.name'),
|
||||
trans('general.purchase_date'),
|
||||
trans('admin/licenses/form.expiration'),
|
||||
trans('mail.expires'),
|
||||
trans('admin/licenses/form.termination_date'),
|
||||
trans('mail.terminates')],
|
||||
$licenses->map(fn($item) => [
|
||||
trans('general.id') => $item->id,
|
||||
trans('general.name') => $item->name,
|
||||
trans('general.purchase_date') => $item->purchase_date_formatted,
|
||||
trans('admin/licenses/form.expiration') => $item->expires_formatted_date,
|
||||
trans('mail.expires') => $item->expires_formatted_date ? $item->expires_diff_for_humans : '',
|
||||
trans('admin/licenses/form.termination_date') => $item->terminates_formatted_date,
|
||||
trans('mail.terminates') => $item->terminates_diff_for_humans
|
||||
])
|
||||
['ID', 'Name', 'Expires', 'Termination Date'],
|
||||
$licenses->map(fn($item) => ['ID' => $item->id, 'Name' => $item->name, 'Expires' => $item->expiration_date, 'Termination Date' => $item->termination_date])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,7 @@ class SendInventoryAlerts extends Command
|
||||
return new AlertRecipient($item);
|
||||
});
|
||||
|
||||
Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
|
||||
} else {
|
||||
$this->info('No low inventory items found. No mail sent.');
|
||||
\Notification::send($recipients, new InventoryAlert($items, $settings->alert_threshold));
|
||||
}
|
||||
} else {
|
||||
if ($settings->alert_email == '') {
|
||||
|
||||
@@ -16,7 +16,7 @@ class SendUpcomingAuditReport extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:upcoming-audits {--with-output : Display the results in a table in your console in addition to sending the email}';
|
||||
protected $signature = 'snipeit:upcoming-audits';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -47,69 +47,21 @@ class SendUpcomingAuditReport extends Command
|
||||
$today = Carbon::now();
|
||||
$interval_date = $today->copy()->addDays($interval);
|
||||
|
||||
$assets_query = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'asc')->with('supplier');
|
||||
$asset_count = $assets_query->count();
|
||||
$this->info(number_format($asset_count) . ' assets must be audited on or before ' . $interval_date);
|
||||
if (!$this->option('with-output')) {
|
||||
$this->info('Run this command with the --with-output option to see the full list in the console.');
|
||||
}
|
||||
$assets = Asset::whereNull('deleted_at')->dueOrOverdueForAudit($settings)->orderBy('assets.next_audit_date', 'desc')->get();
|
||||
$this->info($assets->count() . ' assets must be audited in on or before ' . $interval_date . ' is deadline');
|
||||
|
||||
|
||||
if ($asset_count > 0) {
|
||||
|
||||
$assets_for_email = $assets_query->limit(30)->get();
|
||||
|
||||
if ((count($assets) !== 0) && ($assets->count() > 0) && ($settings->alert_email != '')) {
|
||||
// Send a rollup to the admin, if settings dictate
|
||||
if ($settings->alert_email != '') {
|
||||
|
||||
$recipients = collect(explode(',', $settings->alert_email))
|
||||
->map(fn($item) => trim($item))
|
||||
->filter(fn($item) => !empty($item))
|
||||
->all();
|
||||
|
||||
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets_for_email, $settings->audit_warning_days, $asset_count));
|
||||
$this->info('Audit notification sent to: ' . $settings->alert_email);
|
||||
|
||||
} else {
|
||||
$this->info('There is no admin alert email set so no email will be sent.');
|
||||
}
|
||||
$recipients = collect(explode(',', $settings->alert_email))
|
||||
->map(fn($item) => trim($item))
|
||||
->filter(fn($item) => !empty($item))
|
||||
->all();
|
||||
|
||||
|
||||
|
||||
if ($this->option('with-output')) {
|
||||
|
||||
|
||||
// Get the full list if the user wants output in the console
|
||||
$assets_for_output = $assets_query->limit(null)->get();
|
||||
|
||||
$this->table(
|
||||
[
|
||||
trans('general.id'),
|
||||
trans('general.name'),
|
||||
trans('general.last_audit'),
|
||||
trans('general.next_audit_date'),
|
||||
trans('mail.Days'),
|
||||
trans('mail.supplier'),
|
||||
trans('mail.assigned_to'),
|
||||
|
||||
],
|
||||
$assets_for_output->map(fn($item) => [
|
||||
trans('general.id') => $item->id,
|
||||
trans('general.name') => $item->display_name,
|
||||
trans('general.last_audit') => $item->last_audit_formatted_date,
|
||||
trans('general.next_audit_date') => $item->next_audit_formatted_date,
|
||||
trans('mail.Days') => round($item->next_audit_diff_in_days),
|
||||
trans('mail.supplier') => $item->supplier ? $item->supplier->name : '',
|
||||
trans('mail.assigned_to') => $item->assignedTo ? $item->assignedTo->display_name : '',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->info('There are no assets due for audit in the next ' . $interval . ' days.');
|
||||
$this->info('Sending Admin SendUpcomingAuditNotification to: ' . $settings->alert_email);
|
||||
Mail::to($recipients)->send(new SendUpcomingAuditMail($assets, $settings->audit_warning_days));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ class SystemBackup extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('max_execution_time', env('BACKUP_TIME_LIMIT', 600)); //600 seconds = 10 minutes
|
||||
|
||||
if ($this->option('filename')) {
|
||||
$filename = $this->option('filename');
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
enum ActionType: string
|
||||
{
|
||||
// General
|
||||
case Create = 'create';
|
||||
case Update = 'update';
|
||||
case Delete = 'delete';
|
||||
case Restore = 'restore';
|
||||
|
||||
// Assets/Accessories/Components/Licenses/Consumables
|
||||
case Checkout = 'checkout';
|
||||
case CheckinFrom = 'checkin from';
|
||||
case Requested = 'requested';
|
||||
case RequestCanceled = 'request canceled';
|
||||
case Accepted = 'accepted';
|
||||
case Declined = 'declined';
|
||||
case Audit = 'audit';
|
||||
case NoteAdded = 'note added';
|
||||
|
||||
// Users
|
||||
case TwoFactorReset = '2FA reset';
|
||||
case Merged = 'merged';
|
||||
|
||||
// Licenses
|
||||
case DeleteSeats = 'delete seats';
|
||||
case AddSeats = 'add seats';
|
||||
|
||||
// File Uploads
|
||||
case Uploaded = 'uploaded';
|
||||
case UploadDeleted = 'upload deleted';
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasAccessories extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasAssetModels extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasAssets extends ItemStillHasChildren
|
||||
{
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasChildren extends Exception
|
||||
{
|
||||
//public function __construct($message, $code = 0, Exception $previous = null, $parent, $children)
|
||||
//{
|
||||
// trans()
|
||||
//
|
||||
//}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasComponents extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasConsumables extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasLicenses extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ItemStillHasMaintenances extends ItemStillHasChildren
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -40,8 +40,6 @@ class IconHelper
|
||||
return 'fa-solid fa-trash-arrow-up';
|
||||
case 'external-link':
|
||||
return 'fa fa-external-link';
|
||||
case 'link':
|
||||
return 'fa fa-link';
|
||||
case 'email':
|
||||
return 'fa-regular fa-envelope';
|
||||
case 'phone':
|
||||
@@ -197,10 +195,6 @@ class IconHelper
|
||||
case 'note':
|
||||
case 'notes':
|
||||
return 'fas fa-sticky-note';
|
||||
case 'tip':
|
||||
return 'fa-solid fa-lightbulb';
|
||||
case 'highlight':
|
||||
return 'fa-solid fa-highlighter';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,11 +182,7 @@ class AccessoriesController extends Controller
|
||||
|
||||
$accessory = $request->handleImages($accessory);
|
||||
|
||||
if($request->get('redirect_option') === 'back'){
|
||||
session()->put(['redirect_option' => 'index']);
|
||||
} else {
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
}
|
||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||
|
||||
if ($accessory->save()) {
|
||||
return Helper::getRedirectOption($request, $accessory->id, 'Accessories')
|
||||
|
||||
@@ -77,6 +77,7 @@ class AcceptanceController extends Controller
|
||||
$acceptance = CheckoutAcceptance::find($id);
|
||||
$assigned_user = User::find($acceptance->assigned_to_id);
|
||||
$settings = Setting::getSettings();
|
||||
$path_logo = '';
|
||||
$sig_filename='';
|
||||
|
||||
|
||||
@@ -116,6 +117,8 @@ class AcceptanceController extends Controller
|
||||
|
||||
$item = $acceptance->checkoutable_type::find($acceptance->checkoutable_id);
|
||||
|
||||
|
||||
|
||||
// If signatures are required, make sure we have one
|
||||
if (Setting::getSettings()->require_accept_signature == '1') {
|
||||
|
||||
@@ -135,13 +138,6 @@ class AcceptanceController extends Controller
|
||||
}
|
||||
|
||||
|
||||
// Convert PDF logo to base64 for TCPDF
|
||||
// This is needed for TCPDF to properly embed the image if it's a png and the cache isn't writable
|
||||
$encoded_logo = null;
|
||||
if (($settings->acceptance_pdf_logo) && (Storage::disk('public')->exists($settings->acceptance_pdf_logo))) {
|
||||
$encoded_logo = base64_encode(file_get_contents(public_path() . '/uploads/' . $settings->acceptance_pdf_logo));
|
||||
}
|
||||
|
||||
// Get the data array ready for the notifications and PDF generation
|
||||
$data = [
|
||||
'item_tag' => $item->asset_tag,
|
||||
@@ -155,16 +151,16 @@ class AcceptanceController extends Controller
|
||||
'accepted_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false),
|
||||
'declined_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false),
|
||||
'assigned_to' => $assigned_user->display_name,
|
||||
'email' => $assigned_user->email,
|
||||
'employee_num' => $assigned_user->employee_num,
|
||||
'site_name' => $settings->site_name,
|
||||
'company_name' => $item->company?->name?? $settings->site_name,
|
||||
'signature' => (($sig_filename && array_key_exists('1', $encoded_image))) ? $encoded_image[1] : null,
|
||||
'logo' => ($encoded_logo) ?? null,
|
||||
'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null,
|
||||
'logo' => ($settings->acceptance_pdf_logo) ? public_path() . '/uploads/' . $settings->acceptance_pdf_logo : null,
|
||||
'date_settings' => $settings->date_display_format,
|
||||
'admin' => auth()->user()->present()?->fullName,
|
||||
'qty' => $acceptance->qty ?? 1,
|
||||
];
|
||||
|
||||
|
||||
if ($request->input('asset_acceptance') == 'accepted') {
|
||||
|
||||
|
||||
|
||||
@@ -54,15 +54,6 @@ class AccessoriesController extends Controller
|
||||
'notes',
|
||||
'checkouts_count',
|
||||
'qty',
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
// BUT we account for them in the ordering switch down at the end of this method
|
||||
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
|
||||
'company',
|
||||
'location',
|
||||
'category',
|
||||
'supplier',
|
||||
'manufacturer',
|
||||
];
|
||||
|
||||
|
||||
@@ -70,23 +61,10 @@ class AccessoriesController extends Controller
|
||||
->with('category', 'company', 'manufacturer', 'checkouts', 'location', 'supplier', 'adminuser')
|
||||
->withCount('checkouts as checkouts_count');
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$accessories = $accessories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$accessories->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$accessories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('company_id')) {
|
||||
$accessories->where('accessories.company_id', '=', $request->input('company_id'));
|
||||
}
|
||||
|
||||
@@ -54,12 +54,6 @@ class AssetModelsController extends Controller
|
||||
'deleted_at',
|
||||
'updated_at',
|
||||
'require_serial',
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
// BUT we account for them in the ordering switch down at the end of this method
|
||||
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
|
||||
'manufacturer',
|
||||
'category',
|
||||
];
|
||||
|
||||
$assetmodels = AssetModel::select([
|
||||
@@ -87,24 +81,6 @@ class AssetModelsController extends Controller
|
||||
->withCount('assignedAssets as assets_assigned_count')
|
||||
->withCount('archivedAssets as assets_archived_count');
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$assetmodels->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$assetmodels->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->input('status')=='deleted') {
|
||||
$assetmodels->onlyTrashed();
|
||||
}
|
||||
|
||||
@@ -116,22 +116,6 @@ class AssetsController extends Controller
|
||||
'asset_eol_date',
|
||||
'requestable',
|
||||
'jobtitle',
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
// BUT we account for them in the ordering switch down at the end of this method
|
||||
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
|
||||
'company',
|
||||
'model',
|
||||
'location',
|
||||
'rtd_location',
|
||||
'category',
|
||||
'status_label',
|
||||
'manufacturer',
|
||||
'supplier',
|
||||
'jobtitle',
|
||||
'assigned_to',
|
||||
'created_by',
|
||||
|
||||
];
|
||||
|
||||
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
|
||||
@@ -148,7 +132,6 @@ class AssetsController extends Controller
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
$assets = Asset::select('assets.*')
|
||||
@@ -183,7 +166,7 @@ class AssetsController extends Controller
|
||||
// Search custom fields by column name
|
||||
foreach ($all_custom_fields as $field) {
|
||||
if ($request->filled($field->db_column_name()) && $field->db_column_name()) {
|
||||
$assets->where('assets.'.$field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
$assets->where($field->db_column_name(), '=', $request->input($field->db_column_name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\CategoriesTransformer;
|
||||
@@ -63,23 +61,6 @@ class CategoriesController extends Controller
|
||||
->withCount('accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count');
|
||||
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$categories->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$categories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
/*
|
||||
* This checks to see if we should override the Admin Setting to show archived assets in list.
|
||||
* We don't currently use it within the Snipe-IT GUI, but will be useful for API integrations where they
|
||||
@@ -93,6 +74,10 @@ class CategoriesController extends Controller
|
||||
$categories = $categories->withCount('showableAssets as assets_count');
|
||||
}
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$categories = $categories->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$categories->where('name', '=', $request->input('name'));
|
||||
}
|
||||
@@ -226,21 +211,17 @@ class CategoriesController extends Controller
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Category $category): JsonResponse
|
||||
public function destroy($id) : JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
try {
|
||||
DestroyCategoryAction::run(category: $category);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
$category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($id);
|
||||
|
||||
if (! $category->isDeletable()) {
|
||||
return response()->json(
|
||||
Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.general_assoc_warning', ['asset_type' => $category->category_type]))
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return response()->json(
|
||||
Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong'))
|
||||
Helper::formatStandardApiResponse('error', null, trans('admin/categories/message.assoc_items', ['asset_type'=>$category->category_type]))
|
||||
);
|
||||
}
|
||||
$category->delete();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/categories/message.delete.success')));
|
||||
}
|
||||
|
||||
@@ -45,40 +45,16 @@ class ComponentsController extends Controller
|
||||
'qty',
|
||||
'image',
|
||||
'notes',
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
// BUT we account for them in the ordering switch down at the end of this method
|
||||
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
|
||||
'company',
|
||||
'location',
|
||||
'category',
|
||||
'manufacturer',
|
||||
'supplier',
|
||||
|
||||
];
|
||||
|
||||
$components = Component::select('components.*')
|
||||
->with('company', 'location', 'category', 'assets', 'supplier', 'adminuser', 'manufacturer', 'uncontrainedAssets')
|
||||
->withSum('uncontrainedAssets', 'components_assets.assigned_qty');
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$components = $components->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$components->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$components->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$components->where('name', '=', $request->input('name'));
|
||||
}
|
||||
|
||||
@@ -31,53 +31,10 @@ class ConsumablesController extends Controller
|
||||
$consumables = Consumable::with('company', 'location', 'category', 'supplier', 'manufacturer')
|
||||
->withCount('users as consumables_users_count');
|
||||
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
|
||||
// These must match a column on the consumables table directly.
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'order_number',
|
||||
'min_amt',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'company',
|
||||
'category',
|
||||
'model_number',
|
||||
'item_no',
|
||||
'manufacturer',
|
||||
'location',
|
||||
'qty',
|
||||
'image',
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
// BUT we account for them in the ordering switch down at the end of this method
|
||||
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
|
||||
'company',
|
||||
'location',
|
||||
'category',
|
||||
'supplier',
|
||||
'manufacturer',
|
||||
];
|
||||
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$consumables = $consumables->TextSearch(e($request->input('search')));
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$consumables->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$consumables->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
|
||||
if ($request->filled('name')) {
|
||||
$consumables->where('name', '=', $request->input('name'));
|
||||
}
|
||||
@@ -139,6 +96,25 @@ class ConsumablesController extends Controller
|
||||
$consumables = $consumables->OrderByCreatedBy($order);
|
||||
break;
|
||||
default:
|
||||
// This array is what determines which fields should be allowed to be sorted on ON the table itself.
|
||||
// These must match a column on the consumables table directly.
|
||||
$allowed_columns = [
|
||||
'id',
|
||||
'name',
|
||||
'order_number',
|
||||
'min_amt',
|
||||
'purchase_date',
|
||||
'purchase_cost',
|
||||
'company',
|
||||
'category',
|
||||
'model_number',
|
||||
'item_no',
|
||||
'manufacturer',
|
||||
'location',
|
||||
'qty',
|
||||
'image'
|
||||
];
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'created_at';
|
||||
$consumables = $consumables->orderBy($sort, $order);
|
||||
break;
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\StoreDepartmentRequest;
|
||||
use App\Http\Transformers\DepartmentsTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\Department;
|
||||
@@ -27,19 +26,18 @@ class DepartmentsController extends Controller
|
||||
$allowed_columns = ['id', 'name', 'image', 'users_count', 'notes'];
|
||||
|
||||
$departments = Department::select(
|
||||
[
|
||||
'departments.id',
|
||||
'departments.name',
|
||||
'departments.phone',
|
||||
'departments.fax',
|
||||
'departments.location_id',
|
||||
'departments.company_id',
|
||||
'departments.manager_id',
|
||||
'departments.created_at',
|
||||
'departments.updated_at',
|
||||
'departments.image',
|
||||
'departments.notes'
|
||||
])->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
|
||||
'departments.id',
|
||||
'departments.name',
|
||||
'departments.phone',
|
||||
'departments.fax',
|
||||
'departments.location_id',
|
||||
'departments.company_id',
|
||||
'departments.manager_id',
|
||||
'departments.created_at',
|
||||
'departments.updated_at',
|
||||
'departments.image',
|
||||
'departments.notes',
|
||||
)->with('users')->with('location')->with('manager')->with('company')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$departments = $departments->TextSearch($request->input('search'));
|
||||
@@ -96,17 +94,18 @@ class DepartmentsController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param \App\Http\Requests\ImageUploadRequest $request
|
||||
*/
|
||||
public function store(StoreDepartmentRequest $request): JsonResponse
|
||||
public function store(ImageUploadRequest $request) : JsonResponse
|
||||
{
|
||||
$this->authorize('create', Department::class);
|
||||
$department = new Department;
|
||||
$department->fill($request->validated());
|
||||
$department->fill($request->all());
|
||||
$department = $request->handleImages($department);
|
||||
|
||||
$department->created_by = auth()->id();
|
||||
$department->manager_id = ($request->filled('manager_id') ? $request->input('manager_id') : null);
|
||||
|
||||
if ($department->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new DepartmentsTransformer)->transformDepartment($department), trans('admin/departments/message.create.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.create.success')));
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $department->getErrors()));
|
||||
|
||||
@@ -122,7 +121,7 @@ class DepartmentsController extends Controller
|
||||
public function show($id) : array
|
||||
{
|
||||
$this->authorize('view', Department::class);
|
||||
$department = Department::withCount('users as users_count')->findOrFail($id);
|
||||
$department = Department::findOrFail($id);
|
||||
return (new DepartmentsTransformer)->transformDepartment($department);
|
||||
}
|
||||
|
||||
@@ -142,7 +141,7 @@ class DepartmentsController extends Controller
|
||||
$department = $request->handleImages($department);
|
||||
|
||||
if ($department->save()) {
|
||||
return response()->json(Helper::formatStandardApiResponse('success', (new DepartmentsTransformer)->transformDepartment($department), trans('admin/departments/message.update.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('success', $department, trans('admin/departments/message.update.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $department->getErrors()));
|
||||
|
||||
@@ -24,7 +24,7 @@ class GroupsController extends Controller
|
||||
|
||||
$this->authorize('view', Group::class);
|
||||
|
||||
$groups = Group::select(['id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by'])->with('adminuser')->withCount('users as users_count');
|
||||
$groups = Group::select('id', 'name', 'permissions', 'notes', 'created_at', 'updated_at', 'created_by')->with('adminuser')->withCount('users as users_count');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$groups = $groups->TextSearch($request->input('search'));
|
||||
@@ -50,7 +50,6 @@ class GroupsController extends Controller
|
||||
'id',
|
||||
'name',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'users_count',
|
||||
];
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ class LicenseSeatsController extends Controller
|
||||
if ($license = License::find($licenseId)) {
|
||||
$this->authorize('view', $license);
|
||||
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department', 'user.company', 'asset.company')
|
||||
$seats = LicenseSeat::with('license', 'user', 'asset', 'user.department')
|
||||
->where('license_seats.license_id', $licenseId);
|
||||
|
||||
if ($request->input('status') == 'available') {
|
||||
$seats->whereNull('license_seats.assigned_to')->whereNull('license_seats.asset_id');
|
||||
$seats->whereNull('license_seats.assigned_to');
|
||||
}
|
||||
|
||||
if ($request->input('status') == 'assigned') {
|
||||
@@ -40,10 +40,8 @@ class LicenseSeatsController extends Controller
|
||||
|
||||
$order = $request->input('order') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
if ($request->input('sort') == 'assigned_user.department') {
|
||||
if ($request->input('sort') == 'department') {
|
||||
$seats->OrderDepartments($order);
|
||||
} elseif ($request->input('sort') == 'assigned_user.company') {
|
||||
$seats->OrderCompany($order);
|
||||
} else {
|
||||
$seats->orderBy('updated_at', $order);
|
||||
}
|
||||
@@ -85,7 +83,7 @@ class LicenseSeatsController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat not found'));
|
||||
}
|
||||
// 2. does the seat belong to the specified license?
|
||||
if (! $licenseSeat = $licenseSeat->license()->first() || $licenseSeat->id != intval($licenseId)) {
|
||||
if (! $license = $licenseSeat->license()->first() || $license->id != intval($licenseId)) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, 'Seat does not belong to the specified license'));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\LicensesTransformer;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
use App\Models\License;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -26,12 +25,9 @@ class LicensesController extends Controller
|
||||
$this->authorize('view', License::class);
|
||||
|
||||
$licenses = License::with('company', 'manufacturer', 'supplier','category', 'adminuser')->withCount('freeSeats as free_seats_count');
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if ($request->input('status')=='inactive') {
|
||||
$licenses->ExpiredLicenses();
|
||||
} elseif ($request->input('status')=='expiring') {
|
||||
$licenses->ExpiringLicenses($settings->alert_interval);
|
||||
} else {
|
||||
$licenses->ActiveLicenses();
|
||||
}
|
||||
|
||||
@@ -52,10 +52,6 @@ class MaintenancesController extends Controller
|
||||
$maintenances->where('maintenances.created_by', '=', $request->input('created_by'));
|
||||
}
|
||||
|
||||
if ($request->filled('url')) {
|
||||
$maintenances->where('maintenances.url', '=', $request->input('url'));
|
||||
}
|
||||
|
||||
if ($request->filled('asset_maintenance_type')) {
|
||||
$maintenances->where('asset_maintenance_type', '=', $request->input('asset_maintenance_type'));
|
||||
}
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\ManufacturersTransformer;
|
||||
@@ -191,19 +184,19 @@ class ManufacturersController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
*/
|
||||
public function destroy(Manufacturer $manufacturer): JsonResponse
|
||||
public function destroy($id) : JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
$manufacturer = Manufacturer::findOrFail($id);
|
||||
$this->authorize('delete', $manufacturer);
|
||||
try {
|
||||
DeleteManufacturerAction::run($manufacturer);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.manufacturer')])));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
|
||||
|
||||
if ($manufacturer->isDeletable()) {
|
||||
$manufacturer->delete();
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
|
||||
}
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/manufacturers/message.delete.success')));
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/manufacturers/message.assoc_users')));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Suppliers\DestroySupplierAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Transformers\SelectlistTransformer;
|
||||
@@ -198,40 +191,27 @@ class SuppliersController extends Controller
|
||||
* @since [v4.0]
|
||||
* @param int $id
|
||||
*/
|
||||
public function destroy(Supplier $supplier): JsonResponse
|
||||
public function destroy($id) : JsonResponse
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
$supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->findOrFail($id);
|
||||
$this->authorize('delete', $supplier);
|
||||
try {
|
||||
DestroySupplierAction::run(supplier: $supplier);
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_assets', [
|
||||
'asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasMaintenances $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_maintenances', [
|
||||
'asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_licenses', [
|
||||
'licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_accessories', [
|
||||
'accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_consumables', [
|
||||
'consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.bulk_delete_associations.assoc_components', [
|
||||
'components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier')
|
||||
])));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.something_went_wrong')));
|
||||
|
||||
|
||||
if ($supplier->assets_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count])));
|
||||
}
|
||||
|
||||
if ($supplier->maintenances_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count])));
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count])));
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/suppliers/message.delete.success')));
|
||||
}
|
||||
|
||||
|
||||
@@ -193,12 +193,8 @@ class UploadedFilesController extends Controller
|
||||
|
||||
|
||||
// Check for the file
|
||||
$log = Actionlog::query()
|
||||
->where('id', $file_id)
|
||||
->where('action_type', 'uploaded')
|
||||
->where('item_type', self::$map_object_type[$object_type])
|
||||
->where('item_id', $object->id)
|
||||
->first();
|
||||
$log = Actionlog::find($file_id)->where('item_type', self::$map_object_type[$object_type])
|
||||
->where('item_id', $object->id)->first();
|
||||
|
||||
if ($log) {
|
||||
// Check the file actually exists, and delete it
|
||||
@@ -217,4 +213,4 @@ class UploadedFilesController extends Controller
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans_choice('general.file_upload_status.delete.error', 1)), 500);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,75 +103,9 @@ class UsersController extends Controller
|
||||
'managedLocations as manages_locations_count'
|
||||
]);
|
||||
|
||||
$allowed_columns =
|
||||
[
|
||||
'last_name',
|
||||
'first_name',
|
||||
'display_name',
|
||||
'email',
|
||||
'jobtitle',
|
||||
'username',
|
||||
'employee_num',
|
||||
'groups',
|
||||
'activated',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'two_factor_enrolled',
|
||||
'two_factor_optin',
|
||||
'last_login',
|
||||
'assets_count',
|
||||
'licenses_count',
|
||||
'consumables_count',
|
||||
'accessories_count',
|
||||
'manages_users_count',
|
||||
'manages_locations_count',
|
||||
'phone',
|
||||
'mobile',
|
||||
'address',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'zip',
|
||||
'id',
|
||||
'ldap_import',
|
||||
'two_factor_optin',
|
||||
'two_factor_enrolled',
|
||||
'remote',
|
||||
'vip',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'autoassign_licenses',
|
||||
'website',
|
||||
'locale',
|
||||
'notes',
|
||||
'employee_num',
|
||||
|
||||
// These are *relationships* so we wouldn't normally include them in this array,
|
||||
// since they would normally create a `column not found` error,
|
||||
// BUT we account for them in the ordering switch down at the end of this method
|
||||
// DO NOT ADD ANYTHING TO THIS LIST WITHOUT CHECKING THE ORDERING SWITCH BELOW!
|
||||
'company',
|
||||
'location',
|
||||
'department',
|
||||
'manager',
|
||||
'created_by',
|
||||
|
||||
];
|
||||
|
||||
$filter = [];
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$filter = json_decode($request->input('filter'), true);
|
||||
$filter = array_filter($filter, function ($key) use ($allowed_columns) {
|
||||
return in_array($key, $allowed_columns);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
}
|
||||
|
||||
if ((! is_null($filter)) && (count($filter)) > 0) {
|
||||
$users->ByFilter($filter);
|
||||
} elseif ($request->filled('search')) {
|
||||
$users->TextSearch($request->input('search'));
|
||||
if ($request->filled('search') != '') {
|
||||
$users = $users->TextSearch($request->input('search'));
|
||||
}
|
||||
|
||||
if ($request->filled('activated')) {
|
||||
@@ -352,6 +286,49 @@ class UsersController extends Controller
|
||||
$users->orderBy('first_name', $order);
|
||||
break;
|
||||
default:
|
||||
$allowed_columns =
|
||||
[
|
||||
'last_name',
|
||||
'first_name',
|
||||
'display_name',
|
||||
'email',
|
||||
'jobtitle',
|
||||
'username',
|
||||
'employee_num',
|
||||
'groups',
|
||||
'activated',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'two_factor_enrolled',
|
||||
'two_factor_optin',
|
||||
'last_login',
|
||||
'assets_count',
|
||||
'licenses_count',
|
||||
'consumables_count',
|
||||
'accessories_count',
|
||||
'manages_users_count',
|
||||
'manages_locations_count',
|
||||
'phone',
|
||||
'mobile',
|
||||
'address',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'zip',
|
||||
'id',
|
||||
'ldap_import',
|
||||
'two_factor_optin',
|
||||
'two_factor_enrolled',
|
||||
'remote',
|
||||
'vip',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'autoassign_licenses',
|
||||
'website',
|
||||
'locale',
|
||||
'notes',
|
||||
];
|
||||
|
||||
$sort = in_array($request->input('sort'), $allowed_columns) ? $request->input('sort') : 'first_name';
|
||||
$users = $users->orderBy($sort, $order);
|
||||
break;
|
||||
|
||||
@@ -398,7 +398,6 @@ class AssetsController extends Controller
|
||||
$asset->assigned_to = null;
|
||||
$asset->assigned_type = null;
|
||||
$asset->accepted = null;
|
||||
$asset->last_checkin = now();
|
||||
event(new CheckoutableCheckedIn($asset, $target, auth()->user(), 'Checkin on asset update with '.$status->getStatuslabelType().' status', date('Y-m-d H:i:s'), $originalValues));
|
||||
}
|
||||
|
||||
|
||||
@@ -240,6 +240,10 @@ class BulkAssetsController extends Controller
|
||||
$custom_fields_to_null[str_replace('null', '', $key)] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (! $request->filled('ids') || count($request->input('ids')) == 0) {
|
||||
return redirect($bulk_back_url)->with('error', trans('admin/hardware/message.update.no_assets_selected'));
|
||||
@@ -270,7 +274,6 @@ class BulkAssetsController extends Controller
|
||||
|| ($request->filled('company_id'))
|
||||
|| ($request->filled('status_id'))
|
||||
|| ($request->filled('model_id'))
|
||||
|| ($request->filled('notes'))
|
||||
|| ($request->filled('next_audit_date'))
|
||||
|| ($request->filled('asset_eol_date'))
|
||||
|| ($request->filled('null_name'))
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BulkCategoriesController extends Controller
|
||||
{
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
|
||||
$errors = [];
|
||||
$success_count = 0;
|
||||
|
||||
foreach ($request->ids as $id) {
|
||||
$category = Category::find($id);
|
||||
if (is_null($category)) {
|
||||
$errors[] = trans('admin/categories/message.does_not_exist');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DestroyCategoryAction::run(category: $category);
|
||||
$success_count++;
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasAssetModels) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_asset_models_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasAssets) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasComponents) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_components_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasConsumables) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_consumables_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);
|
||||
} catch (ItemStillHasLicenses) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_licenses_no_count', ['item_name' => $category->name, 'item' => trans('general.category')]);;
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
$errors[] = trans('general.something_went_wrong');
|
||||
}
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
if ($success_count > 0) {
|
||||
return redirect()->route('categories.index')->with('success', trans_choice('admin/categories/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
|
||||
}
|
||||
return redirect()->route('categories.index')->with('multi_error_messages', $errors);
|
||||
} else {
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.bulk_success'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Manufacturer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BulkManufacturersController extends Controller
|
||||
{
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
|
||||
$errors = [];
|
||||
$success_count = 0;
|
||||
foreach ($request->ids as $id) {
|
||||
$manufacturer = Manufacturer::find($id);
|
||||
if (is_null($manufacturer)) {
|
||||
$errors[] = trans('admin/manufacturers/message.does_not_exist');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DeleteManufacturerAction::run(manufacturer: $manufacturer);
|
||||
$success_count++;
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_accessories_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_consumables_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_components_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_licenses_no_count', ['item_name' => $manufacturer->name, 'item' => trans('general.manufacturer')]);;
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
$errors[] = trans('general.something_went_wrong');
|
||||
}
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
if ($success_count > 0) {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans_choice('admin/manufacturers/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
|
||||
}
|
||||
return redirect()->route('manufacturers.index')->with('multi_error_messages', $errors);
|
||||
} else {
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.bulk_success'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Suppliers\DestroySupplierAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BulkSuppliersController extends Controller
|
||||
{
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
|
||||
$errors = [];
|
||||
$success_count = 0;
|
||||
|
||||
foreach ($request->ids as $id) {
|
||||
$supplier = Supplier::find($id);
|
||||
if (is_null($supplier)) {
|
||||
$errors[] = trans('admin/suppliers/message.delete.not_found');
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DestroySupplierAction::run(supplier: $supplier);
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_assets', ['asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasMaintenances $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_maintenances', ['asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_accessories', ['accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_consumables', ['consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
$errors[] = trans('general.bulk_delete_associations.assoc_components', ['components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier'), 'item_name' => $supplier->name]);
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
$errors[] = trans('general.something_went_wrong');
|
||||
}
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
if ($success_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('success', trans_choice('admin/suppliers/message.delete.partial_success', $success_count, ['count' => $success_count]))->with('multi_error_messages', $errors);
|
||||
}
|
||||
return redirect()->route('suppliers.index')->with('multi_error_messages', $errors);
|
||||
} else {
|
||||
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.delete.bulk_success'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Categories\DestroyCategoryAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssetModels;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Category;
|
||||
@@ -151,18 +143,20 @@ class CategoriesController extends Controller
|
||||
* @since [v1.0]
|
||||
* @param int $categoryId
|
||||
*/
|
||||
public function destroy(Category $category): RedirectResponse
|
||||
public function destroy($categoryId) : RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Category::class);
|
||||
try {
|
||||
DestroyCategoryAction::run($category);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return redirect()->route('categories.index')->with('error', trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.category')]));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.delete.error'));
|
||||
// Check if the category exists
|
||||
if (is_null($category = Category::withCount('assets as assets_count', 'accessories as accessories_count', 'consumables as consumables_count', 'components as components_count', 'licenses as licenses_count', 'models as models_count')->findOrFail($categoryId))) {
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.not_found'));
|
||||
}
|
||||
|
||||
if (! $category->isDeletable()) {
|
||||
return redirect()->route('categories.index')->with('error', trans('admin/categories/message.assoc_items', ['asset_type'=> $category->category_type]));
|
||||
}
|
||||
|
||||
Storage::disk('public')->delete('categories'.'/'.$category->image);
|
||||
$category->delete();
|
||||
return redirect()->route('categories.index')->with('success', trans('admin/categories/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ use App\Models\Consumable;
|
||||
use App\Models\License;
|
||||
use App\Models\Location;
|
||||
use App\Models\Maintenance;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
@@ -46,14 +45,12 @@ abstract class Controller extends BaseController
|
||||
'accessories' => Accessory::class,
|
||||
'maintenances' => Maintenance::class,
|
||||
'assets' => Asset::class,
|
||||
'audits' => Asset::class,
|
||||
'components' => Component::class,
|
||||
'consumables' => Consumable::class,
|
||||
'hardware' => Asset::class,
|
||||
'licenses' => License::class,
|
||||
'locations' => Location::class,
|
||||
'models' => AssetModel::class,
|
||||
'suppliers' => Supplier::class,
|
||||
'users' => User::class,
|
||||
];
|
||||
|
||||
@@ -61,14 +58,12 @@ abstract class Controller extends BaseController
|
||||
'accessories' => 'private_uploads/accessories/',
|
||||
'maintenances' => 'private_uploads/maintenances/',
|
||||
'assets' => 'private_uploads/assets/',
|
||||
'audits' => 'private_uploads/audits/',
|
||||
'components' => 'private_uploads/components/',
|
||||
'consumables' => 'private_uploads/consumables/',
|
||||
'hardware' => 'private_uploads/assets/',
|
||||
'licenses' => 'private_uploads/licenses/',
|
||||
'locations' => 'private_uploads/locations/',
|
||||
'models' => 'private_uploads/models/',
|
||||
'suppliers' => 'private_uploads/suppliers/',
|
||||
'users' => 'private_uploads/users/',
|
||||
];
|
||||
|
||||
@@ -76,14 +71,12 @@ abstract class Controller extends BaseController
|
||||
'accessories' => 'accessory',
|
||||
'maintenances' => 'maintenance',
|
||||
'assets' => 'asset',
|
||||
'audits' => 'audits',
|
||||
'components' => 'component',
|
||||
'consumables' => 'consumable',
|
||||
'hardware' => 'asset',
|
||||
'licenses' => 'license',
|
||||
'locations' => 'location',
|
||||
'models' => 'model',
|
||||
'suppliers' => 'supplier',
|
||||
'users' => 'user',
|
||||
];
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Models\Group;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use \App\Models\User;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to User Groups for
|
||||
@@ -44,20 +43,9 @@ class GroupsController extends Controller
|
||||
$permissions = config('permissions');
|
||||
$groupPermissions = Helper::selectedPermissionsArray($permissions, $permissions);
|
||||
$selectedPermissions = $request->old('permissions', $groupPermissions);
|
||||
$users_query = User::where('show_in_list', 1)->whereNull('deleted_at');
|
||||
$users_count = $users_query->count();
|
||||
|
||||
$users = collect();
|
||||
if ($users_count <= config('app.max_unpaginated_records')) {
|
||||
$users = $users_query->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
|
||||
}
|
||||
|
||||
// Show the page
|
||||
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))
|
||||
->with('group', $group)
|
||||
->with('associated_users', [])
|
||||
->with('unselected_users', $users)
|
||||
->with('all_users_count', $users_count);
|
||||
return view('groups/edit', compact('permissions', 'selectedPermissions', 'groupPermissions'))->with('group', $group);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,23 +60,11 @@ class GroupsController extends Controller
|
||||
// create a new group instance
|
||||
$group = new Group();
|
||||
$group->name = $request->input('name');
|
||||
|
||||
if ($request->filled('permission')) {
|
||||
$group->permissions = json_encode($request->array('permission'));
|
||||
} else {
|
||||
$group->permissions = null;
|
||||
}
|
||||
|
||||
$group->permissions = json_encode($request->input('permission'));
|
||||
$group->created_by = auth()->id();
|
||||
$group->notes = $request->input('notes');
|
||||
|
||||
if ($group->save()) {
|
||||
|
||||
if ($request->filled('users_to_sync')) {
|
||||
$associated_users = explode(',',$request->input('users_to_sync'));
|
||||
$group->users()->sync($associated_users);
|
||||
}
|
||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.create'));
|
||||
}
|
||||
|
||||
@@ -111,26 +87,8 @@ class GroupsController extends Controller
|
||||
if ((!is_array($groupPermissions)) || (!$groupPermissions)) {
|
||||
$groupPermissions = [];
|
||||
}
|
||||
|
||||
$selected_array = Helper::selectedPermissionsArray($permissions, $groupPermissions);
|
||||
|
||||
|
||||
$users_query = User::where('show_in_list', 1)->whereNull('deleted_at');
|
||||
$users_count = $users_query->count();
|
||||
|
||||
$associated_users = collect();
|
||||
$unselected_users = collect();
|
||||
|
||||
if ($users_count <= config('app.max_unpaginated_records')) {
|
||||
$associated_users = $group->users()->where('show_in_list', 1)->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
|
||||
// Get the unselected users
|
||||
$unselected_users = User::where('show_in_list', 1)->whereNotIn('id', $associated_users->pluck('id')->toArray())->orderBy('first_name', 'asc')->orderBy('last_name', 'asc')->get();
|
||||
}
|
||||
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'))
|
||||
->with('associated_users', $associated_users)
|
||||
->with('unselected_users', $unselected_users)
|
||||
->with('all_users_count', $users_count);
|
||||
return view('groups.edit', compact('group', 'permissions', 'selected_array', 'groupPermissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,24 +102,11 @@ class GroupsController extends Controller
|
||||
public function update(Request $request, Group $group) : RedirectResponse
|
||||
{
|
||||
$group->name = $request->input('name');
|
||||
|
||||
if ($request->filled('permission')) {
|
||||
$group->permissions = json_encode($request->array('permission'));
|
||||
} else {
|
||||
$group->permissions = null;
|
||||
}
|
||||
|
||||
$group->permissions = json_encode($request->input('permission'));
|
||||
$group->notes = $request->input('notes');
|
||||
|
||||
|
||||
if (! config('app.lock_passwords')) {
|
||||
if ($group->save()) {
|
||||
|
||||
if ($request->has('users_to_sync')) {
|
||||
$associated_users = explode(',',$request->input('users_to_sync'));
|
||||
$group->users()->sync($associated_users);
|
||||
}
|
||||
|
||||
return redirect()->route('groups.index')->with('success', trans('admin/groups/message.success.update'));
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ class MaintenancesController extends Controller
|
||||
$maintenance->is_warranty = $request->input('is_warranty');
|
||||
$maintenance->cost = $request->input('cost');
|
||||
$maintenance->notes = $request->input('notes');
|
||||
$maintenance->url = $request->input('url');
|
||||
|
||||
// Save the asset maintenance data
|
||||
$maintenance->asset_id = $asset->id;
|
||||
@@ -153,7 +152,6 @@ class MaintenancesController extends Controller
|
||||
$maintenance->name = $request->input('name');
|
||||
$maintenance->start_date = $request->input('start_date');
|
||||
$maintenance->completion_date = $request->input('completion_date');
|
||||
$maintenance->url = $request->input('url');
|
||||
|
||||
|
||||
// Todo - put this in a getter/setter?
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Manufacturers\DeleteManufacturerAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasChildren;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Helpers\Helper;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Manufacturer;
|
||||
@@ -165,18 +157,32 @@ class ManufacturersController extends Controller
|
||||
* @param int $manufacturerId
|
||||
* @since [v1.0]
|
||||
*/
|
||||
public function destroy(Manufacturer $manufacturer): RedirectResponse
|
||||
public function destroy($manufacturerId) : RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', $manufacturer);
|
||||
try {
|
||||
DeleteManufacturerAction::run($manufacturer);
|
||||
} catch (ItemStillHasChildren $e) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('general.bulk_delete_associations.general_assoc_warning', ['item' => trans('general.manufacturer')]));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('general.something_went_wrong'));
|
||||
$this->authorize('delete', Manufacturer::class);
|
||||
if (is_null($manufacturer = Manufacturer::withTrashed()->withCount('models as models_count')->find($manufacturerId))) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.not_found'));
|
||||
}
|
||||
|
||||
if (! $manufacturer->isDeletable()) {
|
||||
return redirect()->route('manufacturers.index')->with('error', trans('admin/manufacturers/message.assoc_users'));
|
||||
}
|
||||
|
||||
if ($manufacturer->image) {
|
||||
try {
|
||||
Storage::disk('public')->delete('manufacturers/'.$manufacturer->image);
|
||||
} catch (\Exception $e) {
|
||||
Log::info($e);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete the manufacturer if active, permanent delete if is already deleted
|
||||
if ($manufacturer->deleted_at === null) {
|
||||
$manufacturer->delete();
|
||||
} else {
|
||||
$manufacturer->forceDelete();
|
||||
}
|
||||
// Redirect to the manufacturers management page
|
||||
return redirect()->route('manufacturers.index')->with('success', trans('admin/manufacturers/message.delete.success'));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,21 +3,12 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Mail\CheckoutAccessoryMail;
|
||||
use App\Mail\CheckoutAssetMail;
|
||||
use App\Mail\CheckoutComponentMail;
|
||||
use App\Mail\CheckoutConsumableMail;
|
||||
use App\Mail\CheckoutLicenseMail;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\Checkoutable;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Maintenance;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Company;
|
||||
@@ -27,11 +18,9 @@ use App\Models\License;
|
||||
use App\Models\ReportTemplate;
|
||||
use App\Models\Setting;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use League\Csv\Reader;
|
||||
@@ -447,8 +436,10 @@ class ReportsController extends Controller
|
||||
// Open output stream
|
||||
$handle = fopen('php://output', 'w');
|
||||
stream_set_timeout($handle, 2000);
|
||||
|
||||
fprintf($handle, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||
|
||||
if ($request->filled('use_bom')) {
|
||||
fprintf($handle, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||
}
|
||||
|
||||
$header = [];
|
||||
|
||||
@@ -1127,30 +1118,34 @@ class ReportsController extends Controller
|
||||
$this->authorize('reports.view');
|
||||
$showDeleted = $deleted == 'deleted';
|
||||
|
||||
$query = CheckoutAcceptance::Pending()
|
||||
$query = CheckoutAcceptance::pending()
|
||||
->where('checkoutable_type', 'App\Models\Asset')
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $query) {
|
||||
$query->withTrashed()->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'company'],
|
||||
Accessory::class => ['category','checkouts', 'company'],
|
||||
LicenseSeat::class => ['user', 'license'],
|
||||
Component::class => ['assignedTo', 'company'],
|
||||
Consumable::class => ['company'],
|
||||
]);
|
||||
$query->morphWith([
|
||||
AssetModel::class => ['model'],
|
||||
Company::class => ['company'],
|
||||
Asset::class => ['assignedTo'],
|
||||
])->with('model.category');
|
||||
},
|
||||
'assignedTo' => function($query){
|
||||
$query->withTrashed();
|
||||
}
|
||||
])->orderByDesc('checkout_acceptances.created_at');
|
||||
|
||||
]);
|
||||
|
||||
if ($showDeleted) {
|
||||
$query->withTrashed();
|
||||
}
|
||||
|
||||
$itemsForReport = $query->get()->map(fn ($unaccepted) => Checkoutable::fromAcceptance($unaccepted));
|
||||
$assetsForReport = $query->get()
|
||||
->map(function ($acceptance) {
|
||||
return [
|
||||
'assetItem' => $acceptance->checkoutable,
|
||||
'acceptance' => $acceptance,
|
||||
];
|
||||
});
|
||||
|
||||
return view('reports/unaccepted_assets', compact('itemsForReport','showDeleted' ));
|
||||
return view('reports/unaccepted_assets', compact('assetsForReport','showDeleted' ));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1162,77 +1157,41 @@ class ReportsController extends Controller
|
||||
public function sentAssetAcceptanceReminder(Request $request) : RedirectResponse
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
$id = $request->input('acceptance_id');
|
||||
$query = CheckoutAcceptance::query()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $query) {
|
||||
$query->withTrashed()->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'company', 'checkouts'],
|
||||
Accessory::class => ['category', 'company', 'checkouts'],
|
||||
LicenseSeat::class => ['user', 'license', 'checkouts'],
|
||||
Component::class => ['assignedTo', 'company', 'checkouts'],
|
||||
Consumable::class => ['company', 'checkouts'],
|
||||
]);
|
||||
},
|
||||
'assignedTo' => fn ($q) => $q->withTrashed(),
|
||||
])
|
||||
->pending();
|
||||
$acceptance = $query->find($id);
|
||||
if (!$acceptance) {
|
||||
|
||||
if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
|
||||
Log::debug('No pending acceptances');
|
||||
// Redirect to the unaccepted items report page with error
|
||||
// Redirect to the unaccepted assets report page with error
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
$item = $acceptance->checkoutable;
|
||||
$assignee = $acceptance->assignedTo ?? $item->assignedTo ?? null;
|
||||
$email = $assignee?->email;
|
||||
$locale = $assignee?->locale;
|
||||
|
||||
Log::debug(print_r($acceptance, true));
|
||||
$assetItem = $acceptance->checkoutable;
|
||||
|
||||
Log::debug(print_r($assetItem, true));
|
||||
|
||||
if (is_null($acceptance->created_at)){
|
||||
Log::debug('No acceptance created_at');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
} else {
|
||||
if($item instanceof LicenseSeat){
|
||||
$logItem_res = $item->license->checkouts()->with('adminuser')->where('created_at', '=', $acceptance->created_at)->get();
|
||||
}
|
||||
else{
|
||||
$logItem_res = $item->checkouts()->with('adminuser')->where('created_at', '=', $acceptance->created_at)->get();
|
||||
}
|
||||
$logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get();
|
||||
|
||||
if ($logItem_res->isEmpty()){
|
||||
Log::debug('Acceptance date mismatch');
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
|
||||
}
|
||||
$logItem = $logItem_res[0];
|
||||
}
|
||||
$email = $assetItem->assignedTo?->email;
|
||||
$locale = $assetItem->assignedTo?->locale;
|
||||
|
||||
if (is_null($email) || $email === '') {
|
||||
return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.no_email'));
|
||||
}
|
||||
$mailable = $this->getCheckoutMailType($acceptance, $logItem);
|
||||
Mail::to($email)->send($mailable->locale($locale));
|
||||
|
||||
Mail::to($email)->send((new CheckoutAssetMail($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note, firstTimeSending: false))->locale($locale));
|
||||
|
||||
return redirect()->route('reports/unaccepted_assets')->with('success', trans('admin/reports/general.reminder_sent'));
|
||||
}
|
||||
private function getCheckoutMailType(CheckoutAcceptance $acceptance, $logItem) : Mailable
|
||||
{
|
||||
$lookup = [
|
||||
Accessory::class => CheckoutAccessoryMail::class,
|
||||
Asset::class => CheckoutAssetMail::class,
|
||||
LicenseSeat::class => CheckoutLicenseMail::class,
|
||||
Consumable::class => CheckoutConsumableMail::class,
|
||||
Component::class => CheckoutComponentMail::class,
|
||||
];
|
||||
$mailable= $lookup[get_class($acceptance->checkoutable)];
|
||||
|
||||
return new $mailable($acceptance->checkoutable,
|
||||
$acceptance->checkedOutTo ?? $acceptance->assignedTo,
|
||||
$logItem->adminuser,
|
||||
$acceptance,
|
||||
$acceptance->note);
|
||||
|
||||
}
|
||||
/**
|
||||
* sentAssetAcceptanceReminder
|
||||
*
|
||||
@@ -1264,41 +1223,31 @@ class ReportsController extends Controller
|
||||
public function postAssetAcceptanceReport($deleted = false) : Response
|
||||
{
|
||||
$this->authorize('reports.view');
|
||||
$showDeleted = request('deleted') === 'deleted';;
|
||||
$showDeleted = $deleted == 'deleted';
|
||||
|
||||
/**
|
||||
* Get all assets with pending checkout acceptances
|
||||
*/
|
||||
if($showDeleted) {
|
||||
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->withTrashed()->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model'])->get();
|
||||
} else {
|
||||
$acceptances = CheckoutAcceptance::pending()->where('checkoutable_type', 'App\Models\Asset')->with(['assignedTo', 'checkoutable.assignedTo', 'checkoutable.model'])->get();
|
||||
}
|
||||
|
||||
$acceptances = CheckoutAcceptance::pending()
|
||||
->with([
|
||||
'checkoutable' => function (MorphTo $acceptance) {
|
||||
$acceptance->withTrashed()->morphWith([
|
||||
Asset::class => ['model.category', 'assignedTo', 'company'],
|
||||
Accessory::class => ['category','checkouts', 'company'],
|
||||
LicenseSeat::class => ['user', 'license'],
|
||||
Component::class => ['assignedTo', 'company'],
|
||||
Consumable::class => ['company'],
|
||||
]);
|
||||
},
|
||||
'assignedTo',
|
||||
])->orderByDesc('checkout_acceptances.created_at');
|
||||
|
||||
if ($showDeleted) {
|
||||
$acceptances->withTrashed();
|
||||
}
|
||||
|
||||
$itemsForReport = $acceptances->get()->map(fn ($unaccepted) => Checkoutable::fromAcceptance($unaccepted));
|
||||
$assetsForReport = $acceptances
|
||||
->filter(function($acceptance) {
|
||||
return $acceptance->checkoutable_type == 'App\Models\Asset';
|
||||
})
|
||||
->map(function($acceptance) {
|
||||
return ['assetItem' => $acceptance->checkoutable, 'acceptance' => $acceptance];
|
||||
});
|
||||
|
||||
$rows = [];
|
||||
|
||||
$header = [
|
||||
trans('general.date'),
|
||||
trans('general.type'),
|
||||
trans('admin/companies/table.title'),
|
||||
trans('general.category'),
|
||||
trans('admin/hardware/form.model'),
|
||||
trans('general.name'),
|
||||
trans('admin/hardware/form.name'),
|
||||
trans('admin/hardware/table.asset_tag'),
|
||||
trans('admin/hardware/table.checkoutto'),
|
||||
];
|
||||
@@ -1306,19 +1255,16 @@ class ReportsController extends Controller
|
||||
$header = array_map('trim', $header);
|
||||
$rows[] = implode(',', $header);
|
||||
|
||||
foreach ($itemsForReport as $item) {
|
||||
foreach ($assetsForReport as $item) {
|
||||
|
||||
if ($item != null){
|
||||
if ($item['assetItem'] != null){
|
||||
|
||||
$row = [ ];
|
||||
$row[] = str_replace(',', '', $item->acceptance->created_at);
|
||||
$row[] = str_replace(',', '', $item->type);
|
||||
$row[] = str_replace(',', '', $item->plain_text_company);
|
||||
$row[] = str_replace(',', '', $item->plain_text_category);
|
||||
$row[] = str_replace(',', '', $item->plain_text_model);
|
||||
$row[] = str_replace(',', '', $item->plain_text_name);
|
||||
$row[] = str_replace(',', '', $item->asset_tag);
|
||||
$row[] = str_replace(',', '', ($item->acceptance->assignedto) ? $item->acceptance->assignedto->display_name : trans('admin/reports/general.deleted_user'));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->model->category->name));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->model->name));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->name));
|
||||
$row[] = str_replace(',', '', e($item['assetItem']->asset_tag));
|
||||
$row[] = str_replace(',', '', e(($item['acceptance']->assignedTo) ? $item['acceptance']->assignedTo->display_name : trans('admin/reports/general.deleted_user')));
|
||||
$rows[] = implode(',', $row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,7 +589,6 @@ class SettingsController extends Controller
|
||||
$setting->time_display_format = $request->input('time_display_format');
|
||||
$setting->digit_separator = $request->input('digit_separator');
|
||||
$setting->name_display_format = $request->input('name_display_format');
|
||||
$setting->week_start = $request->input('week_start', 0);
|
||||
|
||||
if ($setting->save()) {
|
||||
return redirect()->route('settings.index')
|
||||
@@ -773,7 +772,6 @@ class SettingsController extends Controller
|
||||
$setting->label2_asset_logo = $request->input('label2_asset_logo');
|
||||
$setting->label2_1d_type = $request->input('label2_1d_type');
|
||||
$setting->label2_2d_type = $request->input('label2_2d_type');
|
||||
$setting->label2_2d_prefix = $request->input('label2_2d_prefix');
|
||||
$setting->label2_2d_target = $request->input('label2_2d_target');
|
||||
$setting->label2_fields = $request->input('label2_fields');
|
||||
$setting->label2_empty_row_count = $request->input('label2_empty_row_count');
|
||||
|
||||
@@ -2,18 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Suppliers\DestroySupplierAction;
|
||||
use App\Exceptions\ItemStillHasAccessories;
|
||||
use App\Exceptions\ItemStillHasComponents;
|
||||
use App\Exceptions\ItemStillHasConsumables;
|
||||
use App\Exceptions\ItemStillHasMaintenances;
|
||||
use App\Exceptions\ItemStillHasAssets;
|
||||
use App\Exceptions\ItemStillHasLicenses;
|
||||
use App\Http\Requests\ImageUploadRequest;
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use \Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\MessageBag;
|
||||
|
||||
/**
|
||||
* This controller handles all actions related to Suppliers for
|
||||
@@ -126,41 +118,30 @@ class SuppliersController extends Controller
|
||||
*
|
||||
* @param int $supplierId
|
||||
*/
|
||||
public function destroy(Supplier $supplier): RedirectResponse
|
||||
public function destroy($supplierId) : RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', Supplier::class);
|
||||
try {
|
||||
DestroySupplierAction::run(supplier: $supplier);
|
||||
} catch (ItemStillHasAssets $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_assets', [
|
||||
'asset_count' => (int) $supplier->assets_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasMaintenances $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_maintenances', [
|
||||
'asset_maintenances_count' => $supplier->asset_maintenances_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasLicenses $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_licenses', [
|
||||
'licenses_count' => (int) $supplier->licenses_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasAccessories $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_accessories', [
|
||||
'accessories_count' => (int) $supplier->accessories_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasConsumables $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_consumables', [
|
||||
'consumables_count' => (int) $supplier->consumables_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (ItemStillHasComponents $e) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('general.bulk_delete_associations.assoc_components', [
|
||||
'components_count' => (int) $supplier->components_count, 'item' => trans('general.supplier')
|
||||
]));
|
||||
} catch (\Exception $e) {
|
||||
report($e);
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.error'));
|
||||
if (is_null($supplier = Supplier::with('maintenances', 'assets', 'licenses')->withCount('maintenances as maintenances_count', 'assets as assets_count', 'licenses as licenses_count')->find($supplierId))) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.not_found'));
|
||||
}
|
||||
|
||||
return redirect()->route('suppliers.index')->with('success', trans('admin/suppliers/message.delete.success'));
|
||||
if ($supplier->assets_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_assets', ['asset_count' => (int) $supplier->assets_count]));
|
||||
}
|
||||
|
||||
if ($supplier->maintenances_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_maintenances', ['maintenances_count' => $supplier->maintenances_count]));
|
||||
}
|
||||
|
||||
if ($supplier->licenses_count > 0) {
|
||||
return redirect()->route('suppliers.index')->with('error', trans('admin/suppliers/message.delete.assoc_licenses', ['licenses_count' => (int) $supplier->licenses_count]));
|
||||
}
|
||||
|
||||
$supplier->delete();
|
||||
|
||||
return redirect()->route('suppliers.index')->with('success',
|
||||
trans('admin/suppliers/message.delete.success')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,5 +154,6 @@ class SuppliersController extends Controller
|
||||
{
|
||||
$this->authorize('view', Supplier::class);
|
||||
return view('suppliers/view', compact('supplier'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\CheckoutRequests\CancelCheckoutRequestAction;
|
||||
use App\Actions\CheckoutRequests\CreateCheckoutRequestAction;
|
||||
use App\Enums\ActionType;
|
||||
use App\Exceptions\AssetNotRequestable;
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
@@ -156,19 +155,7 @@ class ViewAssetsController extends Controller
|
||||
public function getRequestableIndex() : View
|
||||
{
|
||||
$assets = Asset::with('model', 'defaultLoc', 'location', 'assignedTo', 'requests')->Hardware()->RequestableAssets();
|
||||
$models = AssetModel::with([
|
||||
'category',
|
||||
'requests',
|
||||
'assets' => function ($q) {
|
||||
$q->where('requestable', 1)
|
||||
->whereHas('assetstatus', fn ($s) =>
|
||||
$s->where('archived', 0)
|
||||
->where(fn ($s) =>
|
||||
$s->where('deployable', 1)->orWhere('pending', 1)
|
||||
)
|
||||
);
|
||||
},
|
||||
])->RequestableModels()->get();
|
||||
$models = AssetModel::with('category', 'requests', 'assets')->RequestableModels()->get();
|
||||
|
||||
return view('account/requestable-assets', compact('assets', 'models'));
|
||||
}
|
||||
@@ -214,7 +201,7 @@ class ViewAssetsController extends Controller
|
||||
if (($item_request = $item->isRequestedBy($user)) || $cancel_by_admin) {
|
||||
$item->cancelRequest($requestingUser);
|
||||
$data['item_quantity'] = ($item_request) ? $item_request->qty : 1;
|
||||
$logaction->logaction(ActionType::RequestCanceled);
|
||||
$logaction->logaction('request_canceled');
|
||||
|
||||
if (($settings->alert_email != '') && ($settings->alerts_enabled == '1') && (! config('app.lock_passwords'))) {
|
||||
$settings->notify(new RequestAssetCancelation($data));
|
||||
|
||||
@@ -109,7 +109,7 @@ class SettingsSamlRequest extends FormRequest
|
||||
];
|
||||
|
||||
$pkey = openssl_pkey_new([
|
||||
'private_key_bits' => (int) config('app.saml_key_size'),
|
||||
'private_key_bits' => config('app.saml_key_size'),
|
||||
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||||
]);
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ class StoreAssetRequest extends ImageUploadRequest
|
||||
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
parent::prepareForValidation(); // call ImageUploadRequest thing
|
||||
// Guard against users passing in an array for company_id instead of an integer.
|
||||
// If the company_id is not an integer then we simply use what was
|
||||
// provided to be caught by model level validation later.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Department;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class StoreDepartmentRequest extends ImageUploadRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return Gate::allows('create', new Department);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$modelRules = (new Department)->getRules();
|
||||
|
||||
return array_merge(
|
||||
$modelRules,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ class StoreLabelSettings extends FormRequest
|
||||
'labels_pagewidth' => 'numeric|nullable',
|
||||
'labels_pageheight' => 'numeric|nullable',
|
||||
'qr_text' => 'max:31|nullable',
|
||||
'label2_2d_prefix' => 'nullable|max:191',
|
||||
'label2_template' => [
|
||||
'required',
|
||||
Rule::in($names),
|
||||
|
||||
@@ -32,7 +32,7 @@ class StoreNotificationSettings extends FormRequest
|
||||
],
|
||||
'alert_threshold' => 'numeric|nullable',
|
||||
'alert_interval' => 'numeric|nullable|gt:0',
|
||||
'audit_warning_days' => 'numeric|nullable|gte:0',
|
||||
'audit_warning_days' => 'numeric|nullable',
|
||||
'due_checkin_days' => 'numeric|nullable|gt:0',
|
||||
'audit_interval' => 'numeric|nullable|gt:0',
|
||||
];
|
||||
|
||||
@@ -28,31 +28,23 @@ class UpdateAssetRequest extends ImageUploadRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$setting = Setting::getSettings();
|
||||
|
||||
$rules = array_merge(
|
||||
parent::rules(),
|
||||
(new Asset)->getRules(),
|
||||
// This overwrites the rulesets that are set at the model level (via Watson) but are not necessarily required at the request level when doing a PATCH update.
|
||||
// Confusingly, this skips the unique_undeleted validator at the model level (and therefore the UniqueUndeletedTrait), so we have to re-add those
|
||||
// rules here without the requiredness, since those values will already exist if you're updating an existing asset.
|
||||
// this is to overwrite rulesets that include required, and rewrite unique_undeleted
|
||||
[
|
||||
'model_id' => ['integer', 'exists:models,id,deleted_at,NULL', 'not_array'],
|
||||
'status_id' => ['integer', 'exists:status_labels,id'],
|
||||
'asset_tag' => [
|
||||
'min:1', 'max:255', 'not_array',
|
||||
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed(),
|
||||
],
|
||||
'serial' => [
|
||||
'string', 'max:255', 'not_array',
|
||||
$setting->unique_serial=='1' ? Rule::unique('assets', 'serial')->ignore($this->asset)->withoutTrashed() : 'nullable',
|
||||
Rule::unique('assets', 'asset_tag')->ignore($this->asset)->withoutTrashed()
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
// if the purchase cost is passed in as a string **and** the digit_separator is ',' (as is common in the EU)
|
||||
// then we tweak the purchase_cost rule to make it a string
|
||||
if ($setting->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) {
|
||||
if (Setting::getSettings()->digit_separator === '1.234,56' && is_string($this->input('purchase_cost'))) {
|
||||
$rules['purchase_cost'] = ['nullable', 'string'];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Traits;
|
||||
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -39,13 +38,20 @@ trait ConvertsBase64ToFiles
|
||||
if (!$base64Contents) {
|
||||
return;
|
||||
}
|
||||
|
||||
// autogenerate filenames
|
||||
if ($filename == 'auto'){
|
||||
$header = explode(';', $base64Contents, 2)[0];
|
||||
// Grab the image type from the header while we're at it.
|
||||
$filename = $key . '.' . substr($header, strpos($header, '/')+1);
|
||||
}
|
||||
|
||||
// Generate a temporary path to store the Base64 contents
|
||||
$tempFilePath = tempnam(sys_get_temp_dir(), $filename);
|
||||
|
||||
// Store the contents using a stream, or throw an Error (which doesn't do anything?)
|
||||
// Store the contents using a stream, or by decoding manually
|
||||
if (Str::startsWith($base64Contents, 'data:') && count(explode(',', $base64Contents)) > 1) {
|
||||
$source = fopen($base64Contents, 'r'); // PHP has special processing for "data:" URL's
|
||||
$source = fopen($base64Contents, 'r');
|
||||
$destination = fopen($tempFilePath, 'w');
|
||||
|
||||
stream_copy_to_stream($source, $destination);
|
||||
@@ -53,8 +59,7 @@ trait ConvertsBase64ToFiles
|
||||
fclose($source);
|
||||
fclose($destination);
|
||||
} else {
|
||||
// TODO - to get a better error message here, can we maybe do something with modifying the errorBag?
|
||||
throw new ValidationException("Need Base64 URL starting with 'data:'"); // This doesn't actually throw?
|
||||
file_put_contents($tempFilePath, base64_decode($base64Contents, true));
|
||||
}
|
||||
|
||||
$uploadedFile = new UploadedFile($tempFilePath, $filename, null, null, true);
|
||||
|
||||
@@ -149,7 +149,6 @@ class ActionlogsTransformer
|
||||
'filename' => $actionlog->filename,
|
||||
'inlineable' => StorageHelper::allowSafeInline($actionlog->uploads_file_path()),
|
||||
'exists_on_disk' => Storage::exists($actionlog->uploads_file_path()) ? true : false,
|
||||
'mediatype' => StorageHelper::getMediaType($actionlog->uploads_file_path()),
|
||||
] : null,
|
||||
|
||||
'item' => ($actionlog->item) ? [
|
||||
|
||||
@@ -68,7 +68,7 @@ class AssetModelsTransformer
|
||||
'default_fieldset_values' => $default_field_values,
|
||||
'eol' => ($assetmodel->eol > 0) ? $assetmodel->eol.' months' : 'None',
|
||||
'requestable' => ($assetmodel->requestable == '1') ? true : false,
|
||||
'require_serial' => ($assetmodel->require_serial == '1') ? true : false,
|
||||
'require_serial' => $assetmodel->require_serial,
|
||||
'notes' => Helper::parseEscapedMarkedownInline($assetmodel->notes),
|
||||
'created_by' => ($assetmodel->adminuser) ? [
|
||||
'id' => (int) $assetmodel->adminuser->id,
|
||||
|
||||
@@ -43,7 +43,7 @@ class DepartmentsTransformer
|
||||
'id' => (int) $department->location->id,
|
||||
'name' => e($department->location->name),
|
||||
] : null,
|
||||
'users_count' => (int) ($department->users_count),
|
||||
'users_count' => e($department->users_count),
|
||||
'notes' => Helper::parseEscapedMarkedownInline($department->notes),
|
||||
'created_at' => Helper::getFormattedDateObject($department->created_at, 'datetime'),
|
||||
'updated_at' => Helper::getFormattedDateObject($department->updated_at, 'datetime'),
|
||||
|
||||
@@ -36,12 +36,6 @@ class LicenseSeatsTransformer
|
||||
'name' => e($seat->user->department->name),
|
||||
|
||||
] : null,
|
||||
'company'=> ($seat->user->company) ?
|
||||
[
|
||||
'id' => (int) $seat->user->company->id,
|
||||
'name' => e($seat->user->company->name),
|
||||
|
||||
] : null,
|
||||
'created_at' => Helper::getFormattedDateObject($seat->created_at, 'datetime'),
|
||||
] : null,
|
||||
'assigned_asset' => ($seat->asset) ? [
|
||||
|
||||
@@ -66,7 +66,6 @@ class MaintenancesTransformer
|
||||
'id' => $assetmaintenance->supplier->id,
|
||||
'name'=> e($assetmaintenance->supplier->name)
|
||||
] : null,
|
||||
'url' => ($assetmaintenance->url) ? e($assetmaintenance->url) : null,
|
||||
'cost' => Helper::formatCurrencyOutput($assetmaintenance->cost),
|
||||
'asset_maintenance_type' => e($assetmaintenance->asset_maintenance_type),
|
||||
'start_date' => Helper::getFormattedDateObject($assetmaintenance->start_date, 'date'),
|
||||
|
||||
@@ -44,7 +44,7 @@ class AssetImporter extends ItemImporter
|
||||
foreach ($this->customFields as $customField) {
|
||||
$customFieldValue = $this->array_smart_custom_field_fetch($row, $customField);
|
||||
|
||||
if (!is_null($customFieldValue)) {
|
||||
if ($customFieldValue) {
|
||||
if ($customField->field_encrypted == 1) {
|
||||
$this->item['custom_fields'][$customField->db_column_name()] = Crypt::encrypt($customFieldValue);
|
||||
$this->log('Custom Field '.$customField->name.': '.Crypt::encrypt($customFieldValue));
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
|
||||
class CategoryEditForm extends Component
|
||||
@@ -13,25 +12,43 @@ class CategoryEditForm extends Component
|
||||
|
||||
public $eulaText;
|
||||
|
||||
public $originalSendCheckInEmailValue;
|
||||
|
||||
public bool $requireAcceptance;
|
||||
|
||||
public bool $sendCheckInEmail;
|
||||
|
||||
public bool $useDefaultEula;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->originalSendCheckInEmailValue = $this->sendCheckInEmail;
|
||||
|
||||
if ($this->eulaText || $this->useDefaultEula) {
|
||||
$this->sendCheckInEmail = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.category-edit-form');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function emailWillBeSendDueToEula(): bool
|
||||
public function updated($property, $value)
|
||||
{
|
||||
if (! in_array($property, ['eulaText', 'useDefaultEula'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sendCheckInEmail = $this->eulaText || $this->useDefaultEula ? 1 : $this->originalSendCheckInEmailValue;
|
||||
}
|
||||
|
||||
public function getShouldDisplayEmailMessageProperty(): bool
|
||||
{
|
||||
return $this->eulaText || $this->useDefaultEula;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function emailMessage(): string
|
||||
public function getEmailMessageProperty(): string
|
||||
{
|
||||
if ($this->useDefaultEula) {
|
||||
return trans('admin/categories/general.email_will_be_sent_due_to_global_eula');
|
||||
@@ -40,9 +57,13 @@ class CategoryEditForm extends Component
|
||||
return trans('admin/categories/general.email_will_be_sent_due_to_category_eula');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function eulaTextDisabled()
|
||||
public function getEulaTextDisabledProperty()
|
||||
{
|
||||
return (bool)$this->useDefaultEula;
|
||||
}
|
||||
|
||||
public function getSendCheckInEmailDisabledProperty()
|
||||
{
|
||||
return $this->eulaText || $this->useDefaultEula;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ class SlackSettingsForm extends Component
|
||||
]);
|
||||
|
||||
try {
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload, 'headers' => ['Content-Type' => 'application/json']]);
|
||||
$test = $webhook->post($this->webhook_endpoint, ['body' => $payload, ['headers' => ['Content-Type' => 'application/json']]]);
|
||||
|
||||
if(($test->getStatusCode() == 302)||($test->getStatusCode() == 301)){
|
||||
return session()->flash('error' , trans('admin/settings/message.webhook.error_redirect', ['endpoint' => $this->webhook_endpoint]));
|
||||
|
||||
@@ -87,7 +87,7 @@ class CheckoutAssetMail extends Mailable
|
||||
$name = $this->target->assignedto?->display_name;
|
||||
}
|
||||
else if($this->target instanceof Location){
|
||||
$name = $this->target->manager?->name;
|
||||
$name = $this->target->manager->name;
|
||||
}
|
||||
|
||||
// Check if the item has custom fields associated with it
|
||||
|
||||
@@ -17,11 +17,10 @@ class SendUpcomingAuditMail extends Mailable
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($params, $threshold, $total)
|
||||
public function __construct($params, $threshold)
|
||||
{
|
||||
$this->assets = $params;
|
||||
$this->threshold = $threshold;
|
||||
$this->total = $total;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ class SendUpcomingAuditMail extends Mailable
|
||||
|
||||
return new Envelope(
|
||||
from: $from,
|
||||
subject: trans_choice('mail.upcoming-audits', $this->total, ['count' => $this->total, 'threshold' => $this->threshold]),
|
||||
subject: trans_choice('mail.upcoming-audits', $this->assets->count(), ['count' => $this->assets->count(), 'threshold' => $this->threshold]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +49,6 @@ class SendUpcomingAuditMail extends Mailable
|
||||
with: [
|
||||
'assets' => $this->assets,
|
||||
'threshold' => $this->threshold,
|
||||
'total' => $this->total,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -391,91 +390,7 @@ class Accessory extends SnipeModel
|
||||
* BEGIN QUERY SCOPES
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('accessories.name', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('accessories.notes', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('accessories.model_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('accessories.order_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('accessories.purchase_cost', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on created_by name
|
||||
|
||||
@@ -9,7 +9,6 @@ use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Enums\ActionType;
|
||||
|
||||
/**
|
||||
* Model for the Actionlog (the table that keeps a historical log of
|
||||
@@ -336,12 +335,9 @@ class Actionlog extends SnipeModel
|
||||
* @since [v3.0]
|
||||
* @return bool
|
||||
*/
|
||||
public function logaction(string|ActionType $actiontype)
|
||||
public function logaction($actiontype)
|
||||
{
|
||||
if (is_string($actiontype)) {
|
||||
$actiontype = ActionType::from($actiontype);
|
||||
}
|
||||
$this->action_type = $actiontype->value;
|
||||
$this->action_type = $actiontype;
|
||||
$this->remote_ip = request()->ip();
|
||||
$this->user_agent = request()->header('User-Agent');
|
||||
$this->action_source = $this->determineActionSource();
|
||||
@@ -364,7 +360,7 @@ class Actionlog extends SnipeModel
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$last_audit_date = $this->created_at; // this is the action log's created at, not the asset itself
|
||||
$next_audit = $last_audit_date->addMonth((int) $monthInterval); // this actually *modifies* the $last_audit_date
|
||||
$next_audit = $last_audit_date->addMonth($monthInterval); // this actually *modifies* the $last_audit_date
|
||||
$next_audit_days = (int) round($now->diffInDays($next_audit, true));
|
||||
$override_default_next = $next_audit;
|
||||
|
||||
@@ -482,10 +478,6 @@ class Actionlog extends SnipeModel
|
||||
$object = 'models';
|
||||
}
|
||||
|
||||
if ($this->action_type == 'audit') {
|
||||
$object = 'audits';
|
||||
}
|
||||
|
||||
return route('ui.files.show', [
|
||||
'object_type' => $object,
|
||||
'id' => $this->item_id,
|
||||
@@ -501,10 +493,6 @@ class Actionlog extends SnipeModel
|
||||
return 'private_uploads/eula-pdfs/'.$this->filename;
|
||||
}
|
||||
|
||||
if ($this->action_type == 'audit') {
|
||||
return 'private_uploads/audits/'.$this->filename;
|
||||
}
|
||||
|
||||
switch ($this->item_type) {
|
||||
case Accessory::class:
|
||||
return 'private_uploads/accessories/'.$this->filename;
|
||||
@@ -522,8 +510,6 @@ class Actionlog extends SnipeModel
|
||||
return 'private_uploads/locations/'.$this->filename;
|
||||
case Maintenance::class:
|
||||
return 'private_uploads/maintenances/'.$this->filename;
|
||||
case Supplier::class:
|
||||
return 'private_uploads/suppliers/'.$this->filename;
|
||||
case User::class:
|
||||
return 'private_uploads/users/'.$this->filename;
|
||||
default:
|
||||
|
||||
@@ -9,8 +9,6 @@ use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Requestable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\AssetPresenter;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -229,6 +227,7 @@ class Asset extends Depreciable
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function customFieldValidationRules()
|
||||
{
|
||||
|
||||
@@ -267,6 +266,7 @@ class Asset extends Depreciable
|
||||
return parent::save($params);
|
||||
}
|
||||
|
||||
|
||||
public function getDisplayNameAttribute()
|
||||
{
|
||||
return $this->present()->name();
|
||||
@@ -277,144 +277,23 @@ class Asset extends Depreciable
|
||||
*
|
||||
* @return \Carbon\Carbon|null
|
||||
*/
|
||||
|
||||
|
||||
protected function warrantyExpires(): Attribute
|
||||
public function getWarrantyExpiresAttribute()
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => ($attributes['warranty_months'] && $attributes['purchase_date']) ? Carbon::parse($attributes['purchase_date'])->addMonths((int)$attributes['warranty_months']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function warrantyExpiresFormattedDate(): Attribute
|
||||
{
|
||||
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->warrantyExpires, 'date', false)
|
||||
);
|
||||
}
|
||||
|
||||
protected function warrantyExpiresDiff(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? round((Carbon::now()->diffInDays($this->warrantyExpires))) : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function warrantyExpiresDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? Carbon::parse($this->warrantyExpires)->diffForHumans() : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected function lastAuditFormattedDate(): Attribute
|
||||
{
|
||||
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->last_audit_date, 'datetime', false)
|
||||
);
|
||||
}
|
||||
|
||||
protected function lastAuditDiff(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $this->warrantyExpires ? round((Carbon::now()->diffInDays($this->warrantyExpires))) : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function lastAuditDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['last_audit_date'] ? Carbon::parse($attributes['last_audit_date'])->diffForHumans() : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function nextAuditFormattedDate(): Attribute
|
||||
{
|
||||
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => Helper::getFormattedDateObject($this->next_audit_date, 'date', false)
|
||||
);
|
||||
}
|
||||
|
||||
protected function nextAuditDiffInDays(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['next_audit_date'] ? Carbon::now()->diffInDays($attributes['next_audit_date']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function nextAuditDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['next_audit_date'] ? Carbon::parse($attributes['next_audit_date'])->diffForHumans() : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function eolDate(): Attribute
|
||||
{
|
||||
|
||||
return Attribute:: make(
|
||||
get: function(mixed $value, array $attributes) {
|
||||
if ($attributes['asset_eol_date'] && $attributes['eol_explicit'] == '1') {
|
||||
return Carbon::parse($attributes['asset_eol_date']);
|
||||
} elseif ($attributes['purchase_date'] && $this->model && ((int) $this->model->eol > 0)) {
|
||||
return Carbon::parse($attributes['purchase_date'])->addMonths((int) $this->model->eol);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (isset($this->attributes['warranty_months']) && isset($this->attributes['purchase_date'])) {
|
||||
if (is_string($this->attributes['purchase_date']) || is_string($this->attributes['purchase_date'])) {
|
||||
$purchase_date = \Carbon\Carbon::parse($this->attributes['purchase_date']);
|
||||
} else {
|
||||
$purchase_date = \Carbon\Carbon::instance($this->attributes['purchase_date']);
|
||||
}
|
||||
);
|
||||
$purchase_date->setTime(0, 0, 0);
|
||||
|
||||
return $purchase_date->addMonths((int) $this->attributes['warranty_months']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected function eolFormattedDate(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $this->eolDate ? Helper::getFormattedDateObject($this->eolDate, 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function eolDiffInDays(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $this->eolDate ? round((Carbon::now()->diffInDays(Carbon::parse($this->eolDate), false, 1))) : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function eolDiffForHumans(): Attribute
|
||||
{
|
||||
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $this->eolDate ? Carbon::parse($this->eolDate)->diffForHumans() : null,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function expectedCheckinFormattedDate(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expected_checkin', $attributes) ? Helper::getFormattedDateObject($attributes['expected_checkin'], 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function expectedCheckinDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expected_checkin', $attributes) ? Carbon::parse($this->expected_checkin)->diffForHumans() : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the asset -> company relationship
|
||||
*
|
||||
@@ -946,37 +825,25 @@ class Asset extends Depreciable
|
||||
*/
|
||||
public static function getExpiringWarrantyOrEol($days = 30)
|
||||
{
|
||||
$now = now();
|
||||
$end = now()->addDays($days);
|
||||
|
||||
$expired_assets = self::query()
|
||||
->where('archived', '=', '0')
|
||||
|
||||
return self::where('archived', '=', '0')
|
||||
->NotArchived()
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('asset_eol_date')
|
||||
->whereBetween('asset_eol_date', [$now, $end])
|
||||
->where(function ($query) use ($days) {
|
||||
// Check for manual asset EOL first
|
||||
$query->where(function ($query) use ($days) {
|
||||
$query->whereNotNull('asset_eol_date')
|
||||
->whereBetween('asset_eol_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
// Otherwise use the warranty months + purchase date + threshold
|
||||
})->orWhere(function ($query) use ($days) {
|
||||
$query->whereNotNull('purchase_date')
|
||||
->whereNotNull('warranty_months')
|
||||
->whereBetween('purchase_date', [Carbon::now(), Carbon::now()->addMonths('assets.warranty_months')->addDays($days)]);
|
||||
});
|
||||
})
|
||||
->orderBy('asset_eol_date', 'ASC')
|
||||
->orderBy('purchase_date', 'ASC')
|
||||
->get();
|
||||
|
||||
$assets_with_warranties = self::query()
|
||||
->where('archived', '=', '0')
|
||||
->NotArchived()
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('purchase_date')
|
||||
->whereNotNull('warranty_months')
|
||||
->get();
|
||||
|
||||
$expired_warranties = $assets_with_warranties->filter(function ($asset) use ($now, $end) {
|
||||
$expiration_window = Carbon::parse($asset->purchase_date)->addMonths((int) $asset->warranty_months);
|
||||
|
||||
return $expiration_window->betweenIncluded($now, $end);
|
||||
});
|
||||
return $expired_assets->concat($expired_warranties)
|
||||
->unique('id')
|
||||
->sortBy([
|
||||
['asset_eol_date', 'ASC'],
|
||||
['purchase_date', 'ASC']
|
||||
])
|
||||
->values();
|
||||
}
|
||||
|
||||
|
||||
@@ -1908,32 +1775,16 @@ class Asset extends Depreciable
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'assigned_to') {
|
||||
if ($fieldname =='assigned_to') {
|
||||
$query->whereHasMorph(
|
||||
'assignedTo', [User::class], function ($query) use ($search_val) {
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('users.username', 'LIKE', '%'.$search_val.'%');
|
||||
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
)->orWhereHasMorph(
|
||||
'assignedTo', [Location::class], function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
)->orWhereHasMorph(
|
||||
'assignedTo', [Asset::class], function ($query) use ($search_val) {
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
// Don't use the asset table prefix here because it will pull from the original asset,
|
||||
// not the subselect we're doing here to get the assigned asset
|
||||
$query->where('name', 'LIKE', '%'.$search_val.'%')
|
||||
->orWhere('asset_tag', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1973,44 +1824,51 @@ class Asset extends Depreciable
|
||||
}
|
||||
|
||||
if ($fieldname == 'model') {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->where('models.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->where('models.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->whereHas(
|
||||
'model', function ($query) use ($search_val) {
|
||||
$query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'status_label') {
|
||||
$query->whereHas(
|
||||
'assetstatus', function ($query) use ($search_val) {
|
||||
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
$query->where(
|
||||
function ($query) use ($search_val) {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Requestable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\AssetModelPresenter;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use \App\Presenters\AssetModelPresenter;
|
||||
use App\Http\Traits\TwoColumnUniqueUndeletedTrait;
|
||||
|
||||
/**
|
||||
* Model for Asset Models. Asset Models contain higher level
|
||||
@@ -258,57 +256,6 @@ class AssetModel extends SnipeModel
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('models.name', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('models.notes', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('models.model_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* scopeInCategory
|
||||
* Get all models that are in the array of category ids
|
||||
|
||||
@@ -290,35 +290,6 @@ class Category extends SnipeModel
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('categories.name', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'category_type') {
|
||||
$query->where('categories.category_type', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope for whether or not the category requires acceptance
|
||||
*
|
||||
|
||||
@@ -184,17 +184,12 @@ class CheckoutAcceptance extends Model
|
||||
|
||||
$pdf->AddPage();
|
||||
if ($data['logo'] != null) {
|
||||
$pdf->writeHTML('<img src="@'.$data['logo'].'">', true, 0, true, 0, '');
|
||||
$pdf->writeHTML('<img src="'.$data['logo'].'">', true, 0, true, 0, '');
|
||||
} else {
|
||||
$pdf->writeHTML('<h3>'.$data['site_name'].'</h3><br /><br />', true, 0, true, 0, 'C');
|
||||
}
|
||||
|
||||
$pdf->Ln();
|
||||
|
||||
// Check for CJK in the translation string for date. (This is a good proxy for the rest of the document)
|
||||
Helper::hasRtl(trans('general.date')) ? $pdf->setRTL(true) : $pdf->setRTL(false);
|
||||
Helper::isCjk(trans('general.date')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
$pdf->writeHTML(trans('general.date') . ': ' . Helper::getFormattedDateObject(now(), 'datetime', false), true, 0, true, 0, '');
|
||||
|
||||
if ($data['company_name'] != null) {
|
||||
@@ -215,10 +210,7 @@ class CheckoutAcceptance extends Model
|
||||
if (($data['qty'] != null) && ($data['qty'] > 1)) {
|
||||
$pdf->writeHTML(trans('general.qty').': '.e($data['qty']), true, 0, true, 0, '');
|
||||
}
|
||||
$pdf->writeHTML(trans('general.assignee').': '.e($data['assigned_to']) . ($data['employee_num'] ? ' ('.$data['employee_num'].')' : ''), true, 0, true, 0, '');
|
||||
if ($data['email'] != null) {
|
||||
$pdf->writeHTML(trans('general.email').': '.e($data['email']), true, 0, true, 0, '');
|
||||
}
|
||||
$pdf->writeHTML(trans('general.assignee').': '.e($data['assigned_to']), true, 0, true, 0, '');
|
||||
$pdf->Ln();
|
||||
$pdf->writeHTML('<hr>', true, 0, true, 0, '');
|
||||
|
||||
@@ -229,6 +221,7 @@ class CheckoutAcceptance extends Model
|
||||
foreach ($eula_lines as $eula_line) {
|
||||
Helper::hasRtl($eula_line) ? $pdf->setRTL(true) : $pdf->setRTL(false);
|
||||
Helper::isCjk($eula_line) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
$pdf->writeHTML(Helper::parseEscapedMarkedown($eula_line), true, 0, true, 0, '');
|
||||
}
|
||||
$pdf->Ln();
|
||||
@@ -237,17 +230,14 @@ class CheckoutAcceptance extends Model
|
||||
$pdf->Ln();
|
||||
|
||||
if ($data['signature'] != null) {
|
||||
$pdf->writeHTML('<img src="@'.$data['signature'].'">', true, 0, true, 0, '');
|
||||
$pdf->writeHTML('<img src="'.$data['signature'].'">', true, 0, true, 0, '');
|
||||
$pdf->writeHTML('<hr>', true, 0, true, 0, '');
|
||||
$pdf->writeHTML(e($data['assigned_to']), true, 0, true, 0, 'C');
|
||||
$pdf->Ln();
|
||||
}
|
||||
|
||||
Helper::hasRtl(trans('general.notes')) ? $pdf->setRTL(true) : $pdf->setRTL(false);
|
||||
Helper::isCjk(trans('general.notes')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
|
||||
if ($data['note'] != null) {
|
||||
Helper::isCjk(trans('general.notes')) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
Helper::isCjk($data['note']) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true);
|
||||
$pdf->writeHTML(trans('general.notes') . ': ' . e($data['note']), true, 0, true, 0, '');
|
||||
$pdf->Ln();
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
class Checkoutable
|
||||
{
|
||||
public function __construct(
|
||||
public int $acceptance_id,
|
||||
public string $company,
|
||||
public string $category,
|
||||
public string $model,
|
||||
public string $asset_tag,
|
||||
public string $name,
|
||||
public string $type,
|
||||
public object $acceptance,
|
||||
public object $assignee,
|
||||
public readonly string $plain_text_category,
|
||||
public readonly string $plain_text_model,
|
||||
public readonly string $plain_text_name,
|
||||
public readonly string $plain_text_company,
|
||||
){}
|
||||
|
||||
public static function fromAcceptance(CheckoutAcceptance $unaccepted): self
|
||||
{
|
||||
$unaccepted_row = $unaccepted->checkoutable;
|
||||
$acceptance = $unaccepted;
|
||||
|
||||
$assignee = $acceptance->assignedTo;
|
||||
$company = $unaccepted_row->company ? optional($unaccepted_row->company)->present()->nameUrl() : '';
|
||||
$category = $model = $name = $tag = '';
|
||||
$type = $acceptance->checkoutable_item_type ?? '';
|
||||
|
||||
|
||||
if($unaccepted_row instanceof Asset){
|
||||
$category = optional($unaccepted_row->model?->category?->present())->nameUrl() ?? '';
|
||||
$model = optional($unaccepted_row->present())->modelUrl() ?? '';
|
||||
$name = optional($unaccepted_row->present())->nameUrl() ?? '';
|
||||
$tag = (string) ($unaccepted_row->asset_tag ?? '');
|
||||
}
|
||||
if($unaccepted_row instanceof Accessory){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
$model = $unaccepted_row->model_number ?? '';
|
||||
$name = optional($unaccepted_row->present())->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof LicenseSeat){
|
||||
$category = optional($unaccepted_row->license->category?->present())->nameUrl() ?? '';
|
||||
$company = optional($unaccepted_row->license->company?->present())?->nameUrl() ?? '';
|
||||
$model = '';
|
||||
$name = $unaccepted_row->license->present()->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof Consumable){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
$model = $unaccepted_row->model_number ?? '';
|
||||
$name = $unaccepted_row?->present()?->nameUrl() ?? '';
|
||||
}
|
||||
if($unaccepted_row instanceof Component){
|
||||
$category = optional($unaccepted_row->category?->present())->nameUrl() ?? '';
|
||||
$model = $unaccepted_row->model_number ?? '';
|
||||
$name = $unaccepted_row?->present()?->nameUrl() ?? '';
|
||||
}
|
||||
|
||||
return new self(
|
||||
acceptance_id: $acceptance->id,
|
||||
company: $company,
|
||||
category: $category,
|
||||
model: $model,
|
||||
asset_tag: $tag,
|
||||
name: $name,
|
||||
type: $type,
|
||||
acceptance: $acceptance,
|
||||
assignee: $assignee,
|
||||
//plain text for CSVs
|
||||
plain_text_category: ($unaccepted_row->model?->category?->name ?? $unaccepted_row->license->category?->name ?? $unaccepted_row->category?->name ?? ''),
|
||||
plain_text_model: ($unaccepted_row->model?->name ?? $unaccepted_row->model_number ?? ''),
|
||||
plain_text_name: ($unaccepted_row->name ?? $unaccepted_row->license?->name ?? ''),
|
||||
plain_text_company: ($unaccepted_row->company)->name ?? $unaccepted_row->license->company?->name ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace App\Models;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -275,19 +274,7 @@ class Component extends SnipeModel
|
||||
{
|
||||
return $this->category?->checkin_email;
|
||||
}
|
||||
/**
|
||||
* Get the list of checkouts for this License
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check how many items within a component are remaining
|
||||
@@ -334,97 +321,6 @@ class Component extends SnipeModel
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('components.name', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('components.notes', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('components.model_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('components.order_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('components.serial', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('components.serial', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('components.purchase_cost', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Helpers\Helper;
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\ConsumablePresenter;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -317,20 +316,6 @@ class Consumable extends SnipeModel
|
||||
|
||||
return $this->purchase_cost !== null ? $this->qty * $this->purchase_cost : null;
|
||||
}
|
||||
/**
|
||||
* Get the list of checkouts for this consumable
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* -----------------------------------------------
|
||||
* BEGIN MUTATORS
|
||||
@@ -360,99 +345,6 @@ class Consumable extends SnipeModel
|
||||
* -----------------------------------------------
|
||||
**/
|
||||
|
||||
/**
|
||||
* Query builder scope to search on text filters for complex Bootstrap Tables API
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder $query Query builder instance
|
||||
* @param text $filter JSON array of search keys and terms
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder Modified query builder
|
||||
*/
|
||||
public function scopeByFilter($query, $filter)
|
||||
{
|
||||
return $query->where(
|
||||
function ($query) use ($filter) {
|
||||
foreach ($filter as $fieldname => $search_val) {
|
||||
|
||||
if ($fieldname == 'name') {
|
||||
$query->where('consumables.name', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'notes') {
|
||||
$query->where('consumables.notes', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'model_number') {
|
||||
$query->where('consumables.model_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'order_number') {
|
||||
$query->where('consumables.order_number', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'item_no') {
|
||||
$query->where('consumables.item_no', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'serial') {
|
||||
$query->where('consumables.serial', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'purchase_cost') {
|
||||
$query->where('consumables.purchase_cost', 'LIKE', '%' . $search_val . '%');
|
||||
}
|
||||
|
||||
if ($fieldname == 'location') {
|
||||
$query->whereHas(
|
||||
'location', function ($query) use ($search_val) {
|
||||
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'manufacturer') {
|
||||
$query->whereHas(
|
||||
'manufacturer', function ($query) use ($search_val) {
|
||||
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'supplier') {
|
||||
$query->whereHas(
|
||||
'supplier', function ($query) use ($search_val) {
|
||||
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if ($fieldname == 'category') {
|
||||
$query->whereHas(
|
||||
'category', function ($query) use ($search_val) {
|
||||
$query->where('categories.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($fieldname == 'company') {
|
||||
$query->whereHas(
|
||||
'company', function ($query) use ($search_val) {
|
||||
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query builder scope to order on company
|
||||
*
|
||||
|
||||
@@ -31,13 +31,10 @@ class Department extends SnipeModel
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|max:255|is_unique_across_company_and_location:departments,name',
|
||||
'location_id' => 'numeric|nullable|exists:locations,id',
|
||||
'company_id' => 'numeric|nullable|exists:companies,id',
|
||||
'manager_id' => 'numeric|nullable|exists:users,id',
|
||||
'phone' => 'string|max:255|nullable',
|
||||
'fax' => 'string|max:255|nullable',
|
||||
'notes' => 'string|max:255|nullable',
|
||||
'name' => 'required|max:255|is_unique_department',
|
||||
'location_id' => 'numeric|nullable',
|
||||
'company_id' => 'numeric|nullable',
|
||||
'manager_id' => 'numeric|nullable',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,15 +4,15 @@ namespace App\Models\Labels\Tapes\Brother;
|
||||
|
||||
class TZe_24mm_E extends TZe_24mm
|
||||
{
|
||||
private const BARCODE_MARGIN = 1.75;
|
||||
private const BARCODE_MARGIN = 1.50;
|
||||
private const TAG_SIZE = 2.00;
|
||||
private const TITLE_SIZE = 2.80;
|
||||
private const TITLE_MARGIN = 0.50;
|
||||
private const LABEL_SIZE = 2.00;
|
||||
private const LABEL_MARGIN = - 0.75;
|
||||
private const LABEL_MARGIN = - 0.35;
|
||||
private const FIELD_SIZE = 2.80;
|
||||
private const FIELD_MARGIN = 0.15;
|
||||
private const BARCODE1D_SIZE = - 2.25;
|
||||
private const BARCODE1D_SIZE = - 1.00;
|
||||
|
||||
public function getUnit() { return 'mm'; }
|
||||
public function getWidth() { return 45.0; }
|
||||
|
||||
@@ -104,78 +104,6 @@ class Ldap extends Model
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds user via Admin search *first*, and _then_ try to bind as that user, returning the user attributes on success,
|
||||
* or false on failure. This enables login when the DN is harder to programmatically 'guess' due to having users in
|
||||
* various different OU's or other LDAP entities.
|
||||
*/
|
||||
public static function findAndBindMultiOU(string $baseDn, string $filterQuery, string $password, int $slow_failure = 3): array|false
|
||||
{
|
||||
/**
|
||||
* If you *don't* set the slow_failure variable, do note that we might permit timing attacks in here - if
|
||||
* your find results come back 'slow' when a user *does* exist, but fast if they *don't* exist, then you
|
||||
* can use this to enumerate users.
|
||||
*
|
||||
* Even if that's *not* true, we still might have an issue: if we don't find the user, then we don't even _try_
|
||||
* to bind as them. Again, that could permit a timing attack.
|
||||
*
|
||||
* Instead of checking every little thing, we just wrap everything in a try/catch in order to unify the
|
||||
* 'slow_failure' treatment. All failures are re-raised as exceptions so that all failures exit from the
|
||||
* same place.
|
||||
*/
|
||||
$connection = null;
|
||||
$admin_conn = null;
|
||||
try {
|
||||
/**
|
||||
* First we get an 'admin' connection, which will need search permissions. That was already a requirement
|
||||
* here, so that's not a big lift. But it _is_ possible to configure LDAP to only login, and *not* to be
|
||||
* able to import lists of users. In that case, this function *will not work* - and you should use the
|
||||
* legacy 'findAndBindUserLdap' method, below. Otherwise, it looks like this would attempt an anonymous
|
||||
* bind - which you might want, but you probably don't.
|
||||
*
|
||||
**/
|
||||
$admin_conn = self::connectToLdap();
|
||||
self::bindAdminToLdap($admin_conn);
|
||||
$results = ldap_search($admin_conn, $baseDn, $filterQuery);
|
||||
$entry_count = ldap_count_entries($admin_conn, $results);
|
||||
if ($entry_count != 1) {
|
||||
throw new \Exception('Wrong number of entries found: ' . $entry_count);
|
||||
}
|
||||
$entry = ldap_first_entry($admin_conn, $results);
|
||||
$user = ldap_get_attributes($admin_conn, $entry);
|
||||
$userDn = ldap_get_dn($admin_conn, $entry);
|
||||
if (!$userDn) {
|
||||
throw new \Exception("No user DN found");
|
||||
}
|
||||
\Log::debug("FOUND DN IS: $userDn");
|
||||
// The temptation now is to do ldap_unbind on the $admin_conn, but that gets handled in the 'finally' below.
|
||||
// I don't know if that means a separate 'connection' is maintained to the LDAP server or not, and would
|
||||
// definitely prefer to not do that if we can avoid it. But I don't know enough about the LDAP protocol to
|
||||
// be certain that that happens.
|
||||
|
||||
//now we try to log in (bind) as that found user
|
||||
$connection = self::connectToLdap();
|
||||
$bind_results = ldap_bind($connection, $userDn, $password);
|
||||
if (!$bind_results) {
|
||||
throw new \Exception("Unable to bind as user");
|
||||
}
|
||||
return array_change_key_case($user);
|
||||
} catch (\Exception $e) {
|
||||
\Log::debug("Exception on fast find-and-bind: " . $e->getMessage());
|
||||
if ($slow_failure) {
|
||||
sleep($slow_failure);
|
||||
}
|
||||
return false; //TODO - make this null instead for a slightly nicer type signature
|
||||
} finally {
|
||||
if ($admin_conn) {
|
||||
ldap_unbind($admin_conn);
|
||||
}
|
||||
if ($connection) {
|
||||
ldap_unbind($connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds/authenticates the user to LDAP, and returns their attributes.
|
||||
@@ -219,27 +147,25 @@ class Ldap extends Model
|
||||
|
||||
Log::debug('Filter query: '.$filterQuery);
|
||||
|
||||
// only try this if we have an Admin username set; otherwise use the 'legacy' method
|
||||
if (($settings->ldap_uname) && ($baseDn)) {
|
||||
// in the fallowing call, we pick a slow-failure of 0 because we might need to fall through to 'legacy'
|
||||
$fast_bind = self::findAndBindMultiOU($baseDn, $filterQuery, $password, 0);
|
||||
if ($fast_bind) {
|
||||
\Log::debug("Fast bind worked");
|
||||
return $fast_bind;
|
||||
}
|
||||
\Log::debug("Fast bind failed; falling through to legacy bind");
|
||||
}
|
||||
|
||||
if (! $ldapbind = @ldap_bind($connection, $userDn, $password)) {
|
||||
Log::debug("Status of binding user: $userDn to directory: (directly!) ".($ldapbind ? "success" : "FAILURE"));
|
||||
// replicate the old bad-decryption-key detection behavior here
|
||||
try {
|
||||
Crypt::decrypt(Setting::getSettings()->ldap_pword);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.');
|
||||
if (! $ldapbind = self::bindAdminToLdap($connection)) {
|
||||
/*
|
||||
* TODO PLEASE:
|
||||
*
|
||||
* this isn't very clear, so it's important to note: the $ldapbind value is never correctly returned - we never 'return true' from self::bindAdminToLdap() (the function
|
||||
* just "falls off the end" without ever explictly returning 'true')
|
||||
*
|
||||
* but it *does* have an interesting side-effect of checking for the LDAP password being incorrectly encrypted with the wrong APP_KEY, so I'm leaving it in for now.
|
||||
*
|
||||
* If it *did* correctly return 'true' on a succesful bind, it would _probably_ allow users to log in with an incorrect password. Which would be horrible!
|
||||
*
|
||||
* Let's definitely fix this at the next refactor!!!!
|
||||
*
|
||||
*/
|
||||
Log::debug("Status of binding Admin user: $userDn to directory instead: ".($ldapbind ? "success" : "FAILURE"));
|
||||
return false;
|
||||
}
|
||||
//regardless of anything else; stuff isn't working. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $results = ldap_search($connection, $baseDn, $filterQuery)) {
|
||||
|
||||
@@ -5,11 +5,9 @@ namespace App\Models;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -158,29 +156,6 @@ class License extends Depreciable
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
protected function terminatesFormattedDate(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Helper::getFormattedDateObject($attributes['termination_date'], 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function terminatesDiffInDays(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Carbon::now()->diffInDays($attributes['termination_date']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function terminatesDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['termination_date'] ? Carbon::parse($attributes['termination_date'])->diffForHumans() : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function prepareLimitChangeRule($parameters, $field)
|
||||
{
|
||||
$actual_seat_count = $this->licenseseats()->count(); //we use the *actual* seat count here, in case your license has gone wonky
|
||||
@@ -416,12 +391,7 @@ class License extends Depreciable
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user should be required to accept the license
|
||||
*
|
||||
@@ -736,6 +706,49 @@ class License extends Depreciable
|
||||
return $this->hasMany(\App\Models\LicenseSeat::class)->whereNull('assigned_to')->whereNull('deleted_at')->whereNull('asset_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns expiring licenses.
|
||||
*
|
||||
* This checks if:
|
||||
*
|
||||
* 1) The license has not been deleted
|
||||
* 2) The expiration date is between now and the number of days specified
|
||||
* 3) There is an expiration date set and the termination date has not passed
|
||||
* 4) The license termination date is null or has not passed
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
* @see \App\Console\Commands\SendExpiringLicenseNotifications
|
||||
*/
|
||||
public static function getExpiringLicenses($days = 60)
|
||||
{
|
||||
|
||||
return self::whereNull('licenses.deleted_at')
|
||||
|
||||
// The termination date is null or within range
|
||||
->where(function ($query) use ($days) {
|
||||
$query->whereNull('termination_date')
|
||||
->orWhereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
})
|
||||
->where(function ($query) use ($days) {
|
||||
$query->whereNotNull('expiration_date')
|
||||
// Handle expired licenses without termination dates
|
||||
->where(function ($query) use ($days) {
|
||||
$query->whereNull('termination_date')
|
||||
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
})
|
||||
|
||||
// Handle expired licenses with termination dates in the future
|
||||
->orWhere(function ($query) use ($days) {
|
||||
$query->whereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
});
|
||||
})
|
||||
->orderBy('expiration_date', 'ASC')
|
||||
->orderBy('termination_date', 'ASC')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function scopeActiveLicenses($query)
|
||||
{
|
||||
|
||||
@@ -752,57 +765,19 @@ class License extends Depreciable
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiried/terminated licenses scope
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
* @see \App\Console\Commands\SendExpiringLicenseNotifications
|
||||
*/
|
||||
public function scopeExpiredLicenses($query)
|
||||
{
|
||||
return $query->whereDate('termination_date', '<=', Carbon::now())// The termination date is null or within range
|
||||
->orWhere(function ($query) {
|
||||
$query->whereDate('expiration_date', '<=', Carbon::now());
|
||||
})
|
||||
->whereNull('licenses.deleted_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiring/terminating licenses scope
|
||||
*
|
||||
* This checks if:
|
||||
*
|
||||
* 1) The license has not been deleted
|
||||
* 2) The expiration date is between now and the number of days specified
|
||||
* 3) There is an expiration date set and the termination date has not passed
|
||||
* 4) The license termination date is null or has not passed
|
||||
*
|
||||
* @author A. Gianotto <snipe@snipe.net>
|
||||
* @since [v1.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
* @see \App\Console\Commands\SendExpiringLicenseNotifications
|
||||
*/
|
||||
public function scopeExpiringLicenses($query, $days = 60)
|
||||
{
|
||||
return $query// The termination date is null or within range
|
||||
->where(function ($query) use ($days) {
|
||||
$query->whereNull('termination_date')
|
||||
->orWhereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
})
|
||||
->where(function ($query) use ($days) {
|
||||
$query->whereNotNull('expiration_date')
|
||||
// Handle expiring licenses without termination dates
|
||||
->where(function ($query) use ($days) {
|
||||
$query->whereNull('termination_date')
|
||||
->whereBetween('expiration_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
})
|
||||
return $query->whereNull('licenses.deleted_at')
|
||||
|
||||
// Handle expiring licenses with termination dates in the future
|
||||
->orWhere(function ($query) use ($days) {
|
||||
$query->whereBetween('termination_date', [Carbon::now(), Carbon::now()->addDays($days)]);
|
||||
});
|
||||
// The termination date is null or within range
|
||||
->where(function ($query) {
|
||||
$query->whereNull('termination_date')
|
||||
->orWhereDate('termination_date', '<=', [Carbon::now()]);
|
||||
})
|
||||
->orWhere(function ($query) {
|
||||
$query->whereNull('expiration_date')
|
||||
->orWhereDate('expiration_date', '<=', [Carbon::now()]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Traits\Acceptable;
|
||||
use App\Models\Traits\CompanyableChildTrait;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Notifications\CheckinLicenseNotification;
|
||||
use App\Notifications\CheckoutLicenseNotification;
|
||||
use App\Presenters\Presentable;
|
||||
@@ -76,7 +75,7 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
protected function displayName(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value) => $this->license?->name,
|
||||
get: fn(mixed $value) => $this->license->name,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,31 +134,7 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Get the list of checkouts for this License
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function checkouts()
|
||||
{
|
||||
return $this->assetlog()->where('action_type', '=', 'checkout')
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the license -> action logs relationship
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v3.0]
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function assetlog()
|
||||
{
|
||||
return $this->hasMany(Actionlog::class, 'item_id')->where('item_type', self::class)->orderBy('created_at', 'desc')->withTrashed();
|
||||
}
|
||||
/**
|
||||
* Query builder scope to order on department
|
||||
*
|
||||
@@ -177,24 +152,17 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||
}
|
||||
|
||||
|
||||
public function scopeOrderCompany($query, $order)
|
||||
{
|
||||
|
||||
|
||||
return $query->leftJoin('users as license_seat_users', 'license_seats.assigned_to', '=', 'license_seat_users.id')
|
||||
->leftJoin('companies as license_user_company', 'license_user_company.id', '=', 'license_seat_users.company_id')
|
||||
->whereNotNull('license_seats.assigned_to')
|
||||
->orderBy('license_user_company.name', $order);
|
||||
}
|
||||
|
||||
|
||||
public function scopeByAssigned($query)
|
||||
{
|
||||
|
||||
return $query->where(
|
||||
function ($query) {
|
||||
$query->whereNotNull('assigned_to')
|
||||
->orWhereNotNull('asset_id');
|
||||
->orWhere(
|
||||
function ($query) {
|
||||
$query->whereNotNull('asset_id');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Models;
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\CompanyableTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits;
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Actionlog;
|
||||
use App\Models\Asset;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Notifications\AuditNotification;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -5,7 +5,6 @@ namespace App\Models;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Traits\CompanyableChildTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Loggable;
|
||||
use App\Models\Traits\Searchable;
|
||||
use App\Presenters\Presentable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -39,7 +38,6 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
'completion_date' => 'date_format:Y-m-d|nullable|after_or_equal:start_date',
|
||||
'notes' => 'string|nullable',
|
||||
'cost' => 'numeric|nullable|gte:0|max:99999999999999999.99',
|
||||
'url' => 'nullable|url|max:255',
|
||||
];
|
||||
|
||||
|
||||
@@ -59,7 +57,6 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
'asset_maintenance_time',
|
||||
'notes',
|
||||
'cost',
|
||||
'url',
|
||||
];
|
||||
|
||||
use Searchable;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits;
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Models\User;
|
||||
|
||||
// $asset->requests
|
||||
// $asset->isRequestedBy($user)
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -20,37 +19,6 @@ class SnipeModel extends Model
|
||||
$this->attributes['purchase_date'] = $value;
|
||||
}
|
||||
|
||||
|
||||
protected function purchaseDateFormatted(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => $attributes['purchase_date'] ? Helper::getFormattedDateObject(Carbon::parse($attributes['purchase_date']), 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
protected function expiresDiffInDays(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Carbon::now()->diffInDays($attributes['expiration_date']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
protected function expiresDiffForHumans(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Carbon::parse($attributes['expiration_date'])->diffForHumans() : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function expiresFormattedDate(): Attribute
|
||||
{
|
||||
return Attribute:: make(
|
||||
get: fn(mixed $value, array $attributes) => array_key_exists('expiration_date', $attributes) ? Helper::getFormattedDateObject($attributes['expiration_date'], 'date', false) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*/
|
||||
@@ -212,7 +180,6 @@ class SnipeModel extends Model
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function getEula()
|
||||
{
|
||||
|
||||
|
||||
@@ -3,18 +3,15 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Http\Traits\UniqueUndeletedTrait;
|
||||
use App\Models\Traits\HasUploads;
|
||||
use App\Models\Traits\Searchable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Watson\Validating\ValidatingTrait;
|
||||
use \Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use App\Models\Traits\Loggable;
|
||||
class Supplier extends SnipeModel
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
use HasUploads;
|
||||
|
||||
protected $table = 'suppliers';
|
||||
|
||||
@@ -45,7 +42,6 @@ class Supplier extends SnipeModel
|
||||
use ValidatingTrait;
|
||||
use UniqueUndeletedTrait;
|
||||
use Searchable;
|
||||
use Loggable;
|
||||
|
||||
/**
|
||||
* The attributes that should be included when searching the model.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user