diff --git a/app/Http/Controllers/AssetModelsController.php b/app/Http/Controllers/AssetModelsController.php index 86ed8cfad2..324486ad1d 100755 --- a/app/Http/Controllers/AssetModelsController.php +++ b/app/Http/Controllers/AssetModelsController.php @@ -9,7 +9,6 @@ use App\Models\Actionlog; use App\Models\AssetModel; use App\Models\CustomField; use App\Models\SnipeModel; -use App\Models\User; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 9ba25401f2..9707cea182 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -26,6 +26,8 @@ namespace App\Http\Controllers; use App\Models\Accessory; use App\Models\Asset; use App\Models\AssetModel; +use App\Models\Department; +use App\Models\Company; use App\Models\Component; use App\Models\Consumable; use App\Models\License; @@ -46,6 +48,8 @@ abstract class Controller extends BaseController public static $map_object_type = [ 'accessories' => Accessory::class, + 'companies' => Company::class, + 'departments' => Department::class, 'maintenances' => Maintenance::class, 'assets' => Asset::class, 'audits' => Asset::class, @@ -64,6 +68,8 @@ abstract class Controller extends BaseController 'maintenances' => 'private_uploads/maintenances/', 'assets' => 'private_uploads/assets/', 'audits' => 'private_uploads/audits/', + 'departments' => 'private_uploads/departments/', + 'companies' => 'private_uploads/companies/', 'components' => 'private_uploads/components/', 'consumables' => 'private_uploads/consumables/', 'hardware' => 'private_uploads/assets/', @@ -79,6 +85,8 @@ abstract class Controller extends BaseController 'maintenances' => 'maintenance', 'assets' => 'asset', 'audits' => 'audits', + 'companies' => 'company', + 'departments' => 'department', 'components' => 'component', 'consumables' => 'consumable', 'hardware' => 'asset', diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php index d0cf5e3ec3..b2f15e9a68 100755 --- a/app/Models/Actionlog.php +++ b/app/Models/Actionlog.php @@ -423,40 +423,23 @@ class Actionlog extends SnipeModel /** * Calculate the date of the next audit * - * @author [A. Gianotto] [] + * @return Datetime | string * * @since [v4.0] * - * @return \Datetime + * @author [A. Gianotto] [] */ public function calcNextAuditDate($monthInterval = 12, $asset = null) { $last_audit_date = Carbon::parse($this->created_at); // If there is an asset-specific next date already given, if (($asset) && ($asset->next_audit_date)) { - return \Carbon::parse($asset->next_audit_date); + return Carbon::parse($asset->next_audit_date); } - return \Carbon::parse($last_audit_date)->addMonths($monthInterval)->toDateString(); - } - - /** - * Gets action logs in chronological order, excluding uploads - * - * @author Vincent Sposato - * - * @since v1.0 - * - * @return Collection - */ - public function getListingOfActionLogsChronologicalOrder() - { - return $this->all() - ->where('action_type', '!=', 'uploaded') - ->orderBy('item_id', 'asc') - ->orderBy('created_at', 'asc') - ->get(); + return Carbon::parse($last_audit_date)->addMonths($monthInterval)->toDateString(); } + /** * Determines what the type of request is so we can log it to the action_log @@ -553,8 +536,12 @@ class Actionlog extends SnipeModel return 'private_uploads/assets/'.$this->filename; case AssetModel::class: return 'private_uploads/models/'.$this->filename; + case Company::class: + return 'private_uploads/companies/' . $this->filename; case Consumable::class: return 'private_uploads/consumables/'.$this->filename; + case Department::class: + return 'private_uploads/departments/' . $this->filename; case Component::class: return 'private_uploads/components/'.$this->filename; case License::class: diff --git a/app/Models/Company.php b/app/Models/Company.php index 4381ec7a74..2ac6fc1d9f 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -3,10 +3,13 @@ namespace App\Models; use App\Models\Traits\CompanyableTrait; +use App\Models\Traits\HasUploads; +use App\Models\Traits\Loggable; use App\Models\Traits\Searchable; use App\Presenters\CompanyPresenter; use App\Presenters\Presentable; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Log; @@ -22,6 +25,9 @@ final class Company extends SnipeModel { use CompanyableTrait; use HasFactory; + use HasUploads; + use SoftDeletes; + use Loggable; protected $table = 'companies'; diff --git a/app/Models/Department.php b/app/Models/Department.php index 8e47fc17f8..e8a698ad80 100644 --- a/app/Models/Department.php +++ b/app/Models/Department.php @@ -4,11 +4,14 @@ 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\DepartmentPresenter; use App\Presenters\Presentable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\Relation; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; use Illuminate\Support\Facades\Gate; use Watson\Validating\ValidatingTrait; @@ -17,6 +20,9 @@ class Department extends SnipeModel { use CompanyableTrait; use HasFactory; + use HasUploads; + use Loggable; + use SoftDeletes; /** * Whether the model should inject it's identifier to the unique diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php index 2877c8b494..c8a42beeeb 100644 --- a/app/Providers/SettingsServiceProvider.php +++ b/app/Providers/SettingsServiceProvider.php @@ -112,6 +112,24 @@ class SettingsServiceProvider extends ServiceProvider return 'locations/'; }); + // Companies + app()->singleton('companies_upload_path', function () { + return 'companies/'; + }); + + app()->singleton('companies_upload_url', function () { + return 'companies/'; + }); + + // Departments + app()->singleton('departments_upload_path', function () { + return 'departments/'; + }); + + app()->singleton('departments_upload_url', function () { + return 'departments/'; + }); + // Users app()->singleton('users_upload_path', function () { return 'avatars/'; @@ -157,6 +175,7 @@ class SettingsServiceProvider extends ServiceProvider return 'companies/'; }); + // Accessories paths and URLs app()->singleton('accessories_upload_path', function () { return 'accessories/'; diff --git a/database/migrations/2026_03_26_150316_add_deleted_at_to_companies.php b/database/migrations/2026_03_26_150316_add_deleted_at_to_companies.php new file mode 100644 index 0000000000..bbe07aacb2 --- /dev/null +++ b/database/migrations/2026_03_26_150316_add_deleted_at_to_companies.php @@ -0,0 +1,27 @@ +softDeletes(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + + } +}; diff --git a/resources/views/companies/view.blade.php b/resources/views/companies/view.blade.php index 2dff80a401..22614cde59 100644 --- a/resources/views/companies/view.blade.php +++ b/resources/views/companies/view.blade.php @@ -16,15 +16,22 @@ + - + + + + + + + @@ -55,11 +62,11 @@ - - - + + + - + @@ -81,6 +88,11 @@ + @can('update', Company::class) + @section('moar_scripts') + @include ('modals.upload-file', ['item_type' => 'companies', 'item_id' => $company->id]) + @endsection + @endcan @stop @section('moar_scripts') diff --git a/resources/views/departments/view.blade.php b/resources/views/departments/view.blade.php index 09aa693bb6..0f970bc8ae 100644 --- a/resources/views/departments/view.blade.php +++ b/resources/views/departments/view.blade.php @@ -16,42 +16,51 @@ @section('content') - + + + + + + - - - + + + + + + - - + + + + + + + + + - - + - - + + - - + @can('update', Department::class) + @section('moar_scripts') + @include ('modals.upload-file', ['item_type' => 'departments', 'item_id' => $department->id]) + @endsection + @endcan + @stop @section('moar_scripts') diff --git a/resources/views/locations/view.blade.php b/resources/views/locations/view.blade.php index a0b1ae0d8a..ecbd71c5a2 100644 --- a/resources/views/locations/view.blade.php +++ b/resources/views/locations/view.blade.php @@ -109,7 +109,7 @@ - + diff --git a/routes/api.php b/routes/api.php index 840ab202e6..58e2177b1a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1352,7 +1352,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'api-throttle:api']], fu 'index', ] )->name('api.files.index') - ->where(['object_type' => 'accessories|audits|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users']); + ->where(['object_type' => 'accessories|audits|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users|companies|departments']); // Get a file Route::get('{object_type}/{id}/files/{file_id}', @@ -1361,7 +1361,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'api-throttle:api']], fu 'show', ] )->name('api.files.show') - ->where(['object_type' => 'accessories|audits|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users']); + ->where(['object_type' => 'accessories|audits|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users|companies|departments']); // Upload files(s) Route::post('{object_type}/{id}/files', @@ -1370,7 +1370,7 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'api-throttle:api']], fu 'store', ] )->name('api.files.store') - ->where(['object_type' => 'accessories|audits|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users']); + ->where(['object_type' => 'accessories|audits|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users|companies|departments']); // Delete files(s) Route::delete('{object_type}/{id}/files/{file_id}/delete', @@ -1379,6 +1379,6 @@ Route::group(['prefix' => 'v1', 'middleware' => ['api', 'api-throttle:api']], fu 'destroy', ] )->name('api.files.destroy') - ->where(['object_type' => 'accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users']); + ->where(['object_type' => 'accessories|assets|components|consumables|hardware|licenses|locations|maintenances|models|suppliers|users|companies|departments']); }); // end API routes diff --git a/routes/web.php b/routes/web.php index ae5b83b356..5b0b6a9b9f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -738,7 +738,7 @@ Route::group(['middleware' => 'web'], function () { 'show' ] )->name('ui.files.show') - ->where(['object_type' => 'assets|audits|maintenances|hardware|models|users|locations|accessories|consumables|licenses|suppliers|components']); + ->where(['object_type' => 'assets|audits|maintenances|hardware|models|users|locations|accessories|consumables|licenses|suppliers|components|companies|departments']); // Upload files(s) Route::post('{object_type}/{id}/files', @@ -747,7 +747,7 @@ Route::group(['middleware' => 'web'], function () { 'store' ] )->name('ui.files.store') - ->where(['object_type' => 'assets|audits|maintenances|hardware|models|users|locations|accessories|consumables|licenses|suppliers|components']); + ->where(['object_type' => 'assets|audits|maintenances|hardware|models|users|locations|accessories|consumables|licenses|suppliers|components|companies|departments']); // Delete files(s) Route::delete('{object_type}/{id}/files/{file_id}/delete', @@ -756,7 +756,7 @@ Route::group(['middleware' => 'web'], function () { 'destroy' ] )->name('ui.files.destroy') - ->where(['object_type' => 'assets|maintenances|hardware|models|users|locations|accessories|consumables|licenses|suppliers|components']); + ->where(['object_type' => 'assets|maintenances|hardware|models|users|locations|accessories|consumables|licenses|suppliers|components|companies|departments']); }); diff --git a/storage/private_uploads/companies/.gitignore b/storage/private_uploads/companies/.gitignore new file mode 100755 index 0000000000..c96a04f008 --- /dev/null +++ b/storage/private_uploads/companies/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/storage/private_uploads/departments/.gitignore b/storage/private_uploads/departments/.gitignore new file mode 100755 index 0000000000..c96a04f008 --- /dev/null +++ b/storage/private_uploads/departments/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/tests/Feature/Companies/Api/DeleteCompaniesTest.php b/tests/Feature/Companies/Api/DeleteCompaniesTest.php index 99823ce016..03c8714c46 100644 --- a/tests/Feature/Companies/Api/DeleteCompaniesTest.php +++ b/tests/Feature/Companies/Api/DeleteCompaniesTest.php @@ -51,7 +51,7 @@ class DeleteCompaniesTest extends TestCase implements TestsPermissionsRequiremen ->deleteJson(route('api.companies.destroy', $company)) ->assertStatusMessageIs('success'); - $this->assertDatabaseMissing('companies', ['id' => $company->id]); + $this->assertSoftDeleted($company); } public function test_adheres_to_full_multiple_companies_support_scoping() diff --git a/tests/Feature/Departments/Api/DeleteDepartmentsTest.php b/tests/Feature/Departments/Api/DeleteDepartmentsTest.php index 4df13d096e..2d0c9c5a5e 100644 --- a/tests/Feature/Departments/Api/DeleteDepartmentsTest.php +++ b/tests/Feature/Departments/Api/DeleteDepartmentsTest.php @@ -50,7 +50,7 @@ class DeleteDepartmentsTest extends TestCase implements TestsFullMultipleCompani $this->assertDatabaseHas('departments', ['id' => $departmentA->id]); $this->assertDatabaseHas('departments', ['id' => $departmentB->id]); - $this->assertDatabaseMissing('departments', ['id' => $departmentC->id]); + $this->assertSoftDeleted($departmentC); } public function test_cannot_delete_department_that_still_has_users() @@ -72,6 +72,6 @@ class DeleteDepartmentsTest extends TestCase implements TestsFullMultipleCompani ->deleteJson(route('api.departments.destroy', $department)) ->assertStatusMessageIs('success'); - $this->assertDatabaseMissing('departments', ['id' => $department->id]); + $this->assertSoftDeleted($department); } }