Merge pull request #17417 from marcusmoore/snipe-it-17073-asset-requests-are-not-deleted-when-asset-is-deleted
Fixed #17073 - delete old checkout requests
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CheckoutRequest;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CleanOldCheckoutRequests extends Command
|
||||
{
|
||||
private int $deletions = 0;
|
||||
private int $skips = 0;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'snipeit:clean-old-checkout-requests';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Removes checkout requests that reference deleted assets or users.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$requests = CheckoutRequest::with([
|
||||
'user' => function ($query) {
|
||||
$query->withTrashed();
|
||||
},
|
||||
'requestedItem' => function ($query) {
|
||||
$query->withTrashed();
|
||||
},
|
||||
])->get();
|
||||
|
||||
$this->info("Processing {$requests->count()} checkout requests");
|
||||
|
||||
$this->withProgressBar($requests, function ($request) {
|
||||
if ($this->shouldForceDelete($request)) {
|
||||
$request->forceDelete();
|
||||
$this->deletions++;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->shouldSoftDelete($request)) {
|
||||
$request->delete();
|
||||
$this->deletions++;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->skips++;
|
||||
});
|
||||
|
||||
$this->info("Final deletion count: $this->deletions, and skip count: $this->skips");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function shouldForceDelete(CheckoutRequest $request)
|
||||
{
|
||||
// check if the requestable or user relationship is null
|
||||
return !$request->requestable || !$request->user;
|
||||
}
|
||||
|
||||
private function shouldSoftDelete(CheckoutRequest $request)
|
||||
{
|
||||
return $request->requestable->trashed() || $request->user->trashed();
|
||||
}
|
||||
}
|
||||
@@ -206,6 +206,17 @@ class Asset extends Depreciable
|
||||
'model.manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::forceDeleted(function (Asset $asset) {
|
||||
$asset->requests()->forceDelete();
|
||||
});
|
||||
|
||||
static::softDeleted(function (Asset $asset) {
|
||||
$asset->requests()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
// To properly set the expected checkin as Y-m-d
|
||||
public function setExpectedCheckinAttribute($value)
|
||||
{
|
||||
|
||||
@@ -98,8 +98,16 @@ class AssetModel extends SnipeModel
|
||||
'manufacturer' => ['name'],
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::forceDeleted(function (AssetModel $assetModel) {
|
||||
$assetModel->requests()->forceDelete();
|
||||
});
|
||||
|
||||
|
||||
static::softDeleted(function (AssetModel $assetModel) {
|
||||
$assetModel->requests()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the model -> assets relationship
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class CheckoutRequest extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
protected $fillable = ['user_id'];
|
||||
protected $table = 'checkout_requests';
|
||||
|
||||
@@ -183,6 +183,17 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
||||
);
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::forceDeleted(function (User $user) {
|
||||
CheckoutRequest::where(['user_id' => $user->id])->forceDelete();
|
||||
});
|
||||
|
||||
static::softDeleted(function (User $user) {
|
||||
CheckoutRequest::where(['user_id' => $user->id])->delete();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function isAvatarExternal()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class CheckoutRequestFactory extends Factory
|
||||
{
|
||||
protected $model = CheckoutRequest::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'requestable_id' => Asset::factory(),
|
||||
'requestable_type' => Asset::class,
|
||||
'quantity' => 1,
|
||||
'user_id' => User::factory(),
|
||||
];
|
||||
}
|
||||
|
||||
public function forAsset()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'requestable_id' => Asset::factory(),
|
||||
'requestable_type' => Asset::class,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function forAssetModel()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'requestable_id' => AssetModel::factory(),
|
||||
'requestable_type' => AssetModel::class,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Console;
|
||||
|
||||
use App\Models\CheckoutRequest;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CleanOldCheckoutRequestsTest extends TestCase
|
||||
{
|
||||
private CheckoutRequest $validRequest;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->validRequest = CheckoutRequest::factory()->forAsset()->create();
|
||||
}
|
||||
|
||||
public function test_clean_old_checkout_requests_command_for_soft_deleted_asset()
|
||||
{
|
||||
$requestForSoftDeletedAsset = CheckoutRequest::factory()->forAsset()->create();
|
||||
Model::withoutEvents(fn() => $requestForSoftDeletedAsset->requestedItem->delete());
|
||||
|
||||
$this->artisan('snipeit:clean-old-checkout-requests')->assertExitCode(0);
|
||||
|
||||
$this->assertNotSoftDeleted($this->validRequest);
|
||||
$this->assertSoftDeleted($requestForSoftDeletedAsset->fresh());
|
||||
}
|
||||
|
||||
public function test_clean_old_checkout_requests_command_for_missing_asset()
|
||||
{
|
||||
$requestForMissingAsset = CheckoutRequest::factory()->forAsset()->create(['requestable_id' => 99999999]);
|
||||
|
||||
$this->artisan('snipeit:clean-old-checkout-requests')->assertExitCode(0);
|
||||
|
||||
$this->assertNotSoftDeleted($this->validRequest);
|
||||
$this->assertDatabaseMissing('checkout_requests', ['requestable_id' => $requestForMissingAsset->requestable_id]);
|
||||
}
|
||||
|
||||
public function test_clean_old_checkout_requests_command_for_soft_deleted_model()
|
||||
{
|
||||
$requestForSoftDeletedAssetModel = CheckoutRequest::factory()->forAssetModel()->create();
|
||||
Model::withoutEvents(fn() => $requestForSoftDeletedAssetModel->requestedItem->delete());
|
||||
|
||||
$this->artisan('snipeit:clean-old-checkout-requests')->assertExitCode(0);
|
||||
|
||||
$this->assertNotSoftDeleted($this->validRequest);
|
||||
$this->assertSoftDeleted($requestForSoftDeletedAssetModel->fresh());
|
||||
}
|
||||
|
||||
public function test_clean_old_checkout_requests_command_for_missing_model()
|
||||
{
|
||||
$requestForMissingModel = CheckoutRequest::factory()->forAssetModel()->create(['requestable_id' => 99999999]);
|
||||
|
||||
$this->artisan('snipeit:clean-old-checkout-requests')->assertExitCode(0);
|
||||
|
||||
$this->assertNotSoftDeleted($this->validRequest);
|
||||
$this->assertDatabaseMissing('checkout_requests', ['requestable_id' => $requestForMissingModel->requestable_id]);
|
||||
}
|
||||
|
||||
public function test_clean_old_checkout_requests_command_for_soft_deleted_user()
|
||||
{
|
||||
$requestForSoftDeletedUser = CheckoutRequest::factory()->forAsset()->create();
|
||||
Model::withoutEvents(fn() => $requestForSoftDeletedUser->user->delete());
|
||||
|
||||
$this->artisan('snipeit:clean-old-checkout-requests')->assertExitCode(0);
|
||||
|
||||
$this->assertNotSoftDeleted($this->validRequest);
|
||||
$this->assertSoftDeleted($requestForSoftDeletedUser->fresh());
|
||||
}
|
||||
|
||||
public function test_clean_old_checkout_requests_command_for_missing_user()
|
||||
{
|
||||
$requestForMissingUser = CheckoutRequest::factory()->forAsset()->create(['user_id' => 99999999]);
|
||||
|
||||
$this->artisan('snipeit:clean-old-checkout-requests')->assertExitCode(0);
|
||||
|
||||
$this->assertNotSoftDeleted($this->validRequest);
|
||||
$this->assertDatabaseMissing('checkout_requests', ['user_id' => $requestForMissingUser->user_id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Requests\Ui;
|
||||
|
||||
use App\Models\CheckoutRequest;
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AssetRequestIndexTest extends TestCase
|
||||
{
|
||||
public function test_requires_permission_to_view_asset_request_index()
|
||||
{
|
||||
$this->actingAs(User::factory()->create())
|
||||
->get(route('assets.requested'))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_can_view_request_asset_request_index()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->create();
|
||||
|
||||
$this->actingAs(User::factory()->viewAssets()->create())
|
||||
->get(route('assets.requested'))
|
||||
->assertOk()
|
||||
->assertViewHas('requestedItems')
|
||||
->assertSeeText($checkoutRequest->requestedItem->asset_tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Models;
|
||||
|
||||
use App\Models\CheckoutRequest;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CheckoutRequestTest extends TestCase
|
||||
{
|
||||
public function test_checkout_request_soft_deleted_when_requested_asset_soft_deleted()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->forAsset()->create();
|
||||
|
||||
$requestedAsset = $checkoutRequest->requestedItem;
|
||||
|
||||
$requestedAsset->delete();
|
||||
|
||||
$this->assertSoftDeleted($checkoutRequest->fresh());
|
||||
}
|
||||
|
||||
public function test_checkout_request_deleted_when_requested_asset_force_deleted()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->forAsset()->create();
|
||||
|
||||
$requestedAsset = $checkoutRequest->requestedItem;
|
||||
|
||||
$requestedAsset->forceDelete();
|
||||
|
||||
$this->assertDatabaseMissing('checkout_requests', ['id' => $checkoutRequest->id]);
|
||||
}
|
||||
|
||||
public function test_checkout_request_soft_deleted_when_requested_model_soft_deleted()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->forAssetModel()->create();
|
||||
|
||||
$requestedAssetModel = $checkoutRequest->requestedItem;
|
||||
|
||||
$requestedAssetModel->delete();
|
||||
|
||||
$this->assertSoftDeleted($checkoutRequest->fresh());
|
||||
}
|
||||
|
||||
public function test_checkout_request_deleted_when_requested_model_force_deleted()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->forAssetModel()->create();
|
||||
|
||||
$requestedAsset = $checkoutRequest->requestedItem;
|
||||
|
||||
$requestedAsset->forceDelete();
|
||||
|
||||
$this->assertDatabaseMissing('checkout_requests', ['id' => $checkoutRequest->id]);
|
||||
}
|
||||
|
||||
public function test_checkout_request_soft_deleted_when_requesting_user_soft_deleted()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->forAsset()->create();
|
||||
|
||||
$requestingUser = $checkoutRequest->user;
|
||||
|
||||
$requestingUser->delete();
|
||||
|
||||
$this->assertSoftDeleted($checkoutRequest->fresh());
|
||||
}
|
||||
|
||||
public function test_checkout_request_deleted_when_requesting_user_force_deleted()
|
||||
{
|
||||
$checkoutRequest = CheckoutRequest::factory()->forAsset()->create();
|
||||
|
||||
$requestingUser = $checkoutRequest->user;
|
||||
|
||||
$requestingUser->forceDelete();
|
||||
|
||||
$this->assertDatabaseMissing('checkout_requests', ['id' => $checkoutRequest->id]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user