diff --git a/app/Http/Controllers/Accessories/AccessoriesController.php b/app/Http/Controllers/Accessories/AccessoriesController.php index 44bd891ce..bc1ac56fc 100755 --- a/app/Http/Controllers/Accessories/AccessoriesController.php +++ b/app/Http/Controllers/Accessories/AccessoriesController.php @@ -184,15 +184,15 @@ class AccessoriesController extends Controller */ public function destroy($accessoryId) : RedirectResponse { - if (is_null($accessory = Accessory::find($accessoryId))) { + if (is_null($accessory = Accessory::withCount('checkouts as checkouts_count')->find($accessoryId))) { return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.not_found')); } $this->authorize($accessory); - if ($accessory->hasUsers() > 0) { - return redirect()->route('accessories.index')->with('error', trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()])); + if ($accessory->checkouts_count > 0) { + return redirect()->route('accessories.index')->with('error', trans('admin/accessories/general.delete_disabled')); } if ($accessory->image) { diff --git a/app/Http/Controllers/Api/AccessoriesController.php b/app/Http/Controllers/Api/AccessoriesController.php index f7df59eb5..90486b40f 100644 --- a/app/Http/Controllers/Api/AccessoriesController.php +++ b/app/Http/Controllers/Api/AccessoriesController.php @@ -249,11 +249,11 @@ class AccessoriesController extends Controller public function destroy($id) { $this->authorize('delete', Accessory::class); - $accessory = Accessory::findOrFail($id); + $accessory = Accessory::withCount('checkouts as checkouts_count')->findOrFail($id); $this->authorize($accessory); - if ($accessory->hasUsers() > 0) { - return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/message.assoc_users', ['count'=> $accessory->hasUsers()]))); + if ($accessory->checkouts_count > 0) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/accessories/general.delete_disabled'))); } $accessory->delete(); diff --git a/app/Http/Transformers/AccessoriesTransformer.php b/app/Http/Transformers/AccessoriesTransformer.php index f8099bfe2..b50249088 100644 --- a/app/Http/Transformers/AccessoriesTransformer.php +++ b/app/Http/Transformers/AccessoriesTransformer.php @@ -55,7 +55,7 @@ class AccessoriesTransformer 'checkout' => Gate::allows('checkout', Accessory::class), 'checkin' => false, 'update' => Gate::allows('update', Accessory::class), - 'delete' => Gate::allows('delete', Accessory::class), + 'delete' => $accessory->checkouts_count === 0 && Gate::allows('delete', Accessory::class), 'clone' => Gate::allows('create', Accessory::class), ]; diff --git a/database/factories/AccessoryFactory.php b/database/factories/AccessoryFactory.php index bdcffd50e..f2c01dc8f 100644 --- a/database/factories/AccessoryFactory.php +++ b/database/factories/AccessoryFactory.php @@ -3,6 +3,7 @@ namespace Database\Factories; use App\Models\Accessory; +use App\Models\Asset; use App\Models\Category; use App\Models\Location; use App\Models\Manufacturer; @@ -170,4 +171,30 @@ class AccessoryFactory extends Factory } }); } + + public function checkedOutToAsset(Asset $asset = null) + { + return $this->afterCreating(function (Accessory $accessory) use ($asset) { + $accessory->checkouts()->create([ + 'accessory_id' => $accessory->id, + 'created_at' => Carbon::now(), + 'created_by' => 1, + 'assigned_to' => $asset->id ?? Asset::factory()->create()->id, + 'assigned_type' => Asset::class, + ]); + }); + } + + public function checkedOutToLocation(Location $location = null) + { + return $this->afterCreating(function (Accessory $accessory) use ($location) { + $accessory->checkouts()->create([ + 'accessory_id' => $accessory->id, + 'created_at' => Carbon::now(), + 'created_by' => 1, + 'assigned_to' => $location->id ?? Location::factory()->create()->id, + 'assigned_type' => Location::class, + ]); + }); + } } diff --git a/tests/Feature/Accessories/Api/DeleteAccessoriesTest.php b/tests/Feature/Accessories/Api/DeleteAccessoriesTest.php index f6a15118f..e82d0de48 100644 --- a/tests/Feature/Accessories/Api/DeleteAccessoriesTest.php +++ b/tests/Feature/Accessories/Api/DeleteAccessoriesTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Accessories\Api; use App\Models\Accessory; use App\Models\Company; use App\Models\User; +use PHPUnit\Framework\Attributes\DataProvider; use Tests\Concerns\TestsFullMultipleCompaniesSupport; use Tests\Concerns\TestsPermissionsRequirement; use Tests\TestCase; @@ -53,9 +54,17 @@ class DeleteAccessoriesTest extends TestCase implements TestsFullMultipleCompani $this->assertSoftDeleted($accessoryC); } - public function testCannotDeleteAccessoryThatHasCheckouts() + public static function checkedOutAccessories() { - $accessory = Accessory::factory()->checkedOutToUser()->create(); + yield 'checked out to user' => [fn() => Accessory::factory()->checkedOutToUser()->create()]; + yield 'checked out to asset' => [fn() => Accessory::factory()->checkedOutToAsset()->create()]; + yield 'checked out to location' => [fn() => Accessory::factory()->checkedOutToLocation()->create()]; + } + + #[DataProvider('checkedOutAccessories')] + public function testCannotDeleteAccessoryThatHasCheckouts($data) + { + $accessory = $data(); $this->actingAsForApi(User::factory()->deleteAccessories()->create()) ->deleteJson(route('api.accessories.destroy', $accessory)) diff --git a/tests/Feature/Accessories/Ui/DeleteAccessoryTest.php b/tests/Feature/Accessories/Ui/DeleteAccessoryTest.php index 80cf96091..45ebbf71f 100644 --- a/tests/Feature/Accessories/Ui/DeleteAccessoryTest.php +++ b/tests/Feature/Accessories/Ui/DeleteAccessoryTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Accessories\Ui; use App\Models\Accessory; use App\Models\Company; use App\Models\User; +use PHPUnit\Framework\Attributes\DataProvider; use Tests\TestCase; class DeleteAccessoryTest extends TestCase @@ -29,9 +30,17 @@ class DeleteAccessoryTest extends TestCase $this->assertFalse($accessoryForCompanyA->refresh()->trashed(), 'Accessory should not be deleted'); } - public function testCannotDeleteAccessoryThatHasCheckouts() + public static function checkedOutAccessories() { - $accessory = Accessory::factory()->checkedOutToUser()->create(); + yield 'checked out to user' => [fn() => Accessory::factory()->checkedOutToUser()->create()]; + yield 'checked out to asset' => [fn() => Accessory::factory()->checkedOutToAsset()->create()]; + yield 'checked out to location' => [fn() => Accessory::factory()->checkedOutToLocation()->create()]; + } + + #[DataProvider('checkedOutAccessories')] + public function testCannotDeleteAccessoryThatHasCheckouts($data) + { + $accessory = $data(); $this->actingAs(User::factory()->deleteAccessories()->create()) ->delete(route('accessories.destroy', $accessory->id))