355 lines
14 KiB
PHP
355 lines
14 KiB
PHP
<?php
|
|
|
|
use App\Enums\CuttingStatus;
|
|
use App\Enums\OwnerVerificationAction;
|
|
use App\Enums\OwnerVerificationStatus;
|
|
use App\Enums\Permission as PermissionEnum;
|
|
use App\Enums\Role as RoleEnum;
|
|
use App\Models\Cutting;
|
|
use App\Models\OwnerVerificationRequest;
|
|
use App\Models\Product;
|
|
use App\Models\Purchase;
|
|
use App\Models\User;
|
|
use App\Support\ActivityLog\ModelLabel;
|
|
use Database\Seeders\RolePermissionSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$this->seed(RolePermissionSeeder::class);
|
|
});
|
|
|
|
function createOwnerVerificationRequestFor(User $submitter, ?Product $product = null): OwnerVerificationRequest
|
|
{
|
|
$product ??= Product::factory()->create(['is_active' => false]);
|
|
|
|
return OwnerVerificationRequest::query()->create([
|
|
'action' => OwnerVerificationAction::CREATE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => Product::class,
|
|
'subject_id' => $product->id,
|
|
'submitted_by_id' => $submitter->id,
|
|
'payload' => ['old' => null, 'new' => ['name' => $product->name]],
|
|
]);
|
|
}
|
|
|
|
function createOwnerVerifierUser(): User
|
|
{
|
|
$user = User::factory()->create();
|
|
$user->assignRole(RoleEnum::OWNER->value);
|
|
$user->forgetCachedPermissions();
|
|
|
|
return $user;
|
|
}
|
|
|
|
function createPendingCutting(?User $submitter = null): Cutting
|
|
{
|
|
$submitter ??= User::factory()->create();
|
|
|
|
return Cutting::factory()->create([
|
|
'status' => CuttingStatus::PENDING_VERIFICATION,
|
|
'submitted_by_id' => $submitter->id,
|
|
'created_by_id' => $submitter->id,
|
|
'description' => 'Cutting test pending',
|
|
]);
|
|
}
|
|
|
|
describe('Owner Verification Index', function () {
|
|
test('submitter with view permission only sees own verification requests', function () {
|
|
$submitter = User::factory()->create();
|
|
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
|
$submitter->forgetCachedPermissions();
|
|
|
|
$otherUser = User::factory()->create();
|
|
$otherUser->assignRole(RoleEnum::ADMIN_TOKO->value);
|
|
$otherUser->forgetCachedPermissions();
|
|
|
|
$ownRequest = createOwnerVerificationRequestFor($submitter);
|
|
createOwnerVerificationRequestFor($otherUser);
|
|
|
|
$response = $this->actingAs($submitter)
|
|
->get(route('admin.manage.owner_verifications.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->component('admin/manage/owner-verifications/Index')
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.id', $ownRequest->id)
|
|
->where('verificationRequests.data.0.source', 'request')
|
|
);
|
|
});
|
|
|
|
test('verifier sees all verification requests', function () {
|
|
$submitter = User::factory()->create();
|
|
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
|
$submitter->forgetCachedPermissions();
|
|
|
|
$verifier = createOwnerVerifierUser();
|
|
|
|
createOwnerVerificationRequestFor($submitter);
|
|
createOwnerVerificationRequestFor($submitter);
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->component('admin/manage/owner-verifications/Index')
|
|
->has('verificationRequests.data', 2)
|
|
);
|
|
});
|
|
|
|
test('submitter cannot approve verification request', function () {
|
|
$submitter = User::factory()->create();
|
|
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
|
$submitter->forgetCachedPermissions();
|
|
|
|
$request = createOwnerVerificationRequestFor($submitter);
|
|
|
|
$this->actingAs($submitter)
|
|
->post(route('admin.manage.owner_verifications.approve_request', $request))
|
|
->assertForbidden();
|
|
});
|
|
|
|
test('admin toko role has owner verification view permission', function () {
|
|
$user = User::factory()->create();
|
|
$user->assignRole(RoleEnum::ADMIN_TOKO->value);
|
|
$user->forgetCachedPermissions();
|
|
|
|
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VIEW->value))->toBeTrue();
|
|
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value))->toBeFalse();
|
|
});
|
|
|
|
test('admin bahan baku role has owner verification view permission', function () {
|
|
$user = User::factory()->create();
|
|
$user->assignRole(RoleEnum::ADMIN_BAHAN_BAKU->value);
|
|
$user->forgetCachedPermissions();
|
|
|
|
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VIEW->value))->toBeTrue();
|
|
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value))->toBeFalse();
|
|
});
|
|
});
|
|
|
|
describe('Owner Verification Unified Cutting List', function () {
|
|
test('verifier sees pending cuttings in the same verification table', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
$cutting = createPendingCutting();
|
|
$request = createOwnerVerificationRequestFor(User::factory()->create());
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->component('admin/manage/owner-verifications/Index')
|
|
->has('verificationRequests.data', 2)
|
|
->where('verificationRequests.data.0.source', 'cutting')
|
|
->where('verificationRequests.data.0.id', $cutting->id)
|
|
->where('verificationRequests.data.0.action', OwnerVerificationAction::STOCK_VERIFY->value)
|
|
->where('verificationRequests.data.0.subject_type', Cutting::class)
|
|
->where('verificationRequests.data.1.source', 'request')
|
|
->where('verificationRequests.data.1.id', $request->id)
|
|
);
|
|
});
|
|
|
|
test('submitter does not see pending cuttings in verification table', function () {
|
|
$submitter = User::factory()->create();
|
|
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
|
$submitter->forgetCachedPermissions();
|
|
|
|
createPendingCutting();
|
|
createOwnerVerificationRequestFor($submitter);
|
|
|
|
$response = $this->actingAs($submitter)
|
|
->get(route('admin.manage.owner_verifications.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->component('admin/manage/owner-verifications/Index')
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.source', 'request')
|
|
);
|
|
});
|
|
|
|
test('filtering by cutting module shows only pending cuttings', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
$cutting = createPendingCutting();
|
|
createOwnerVerificationRequestFor(User::factory()->create());
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index', [
|
|
'subject_type' => Cutting::class,
|
|
]));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.source', 'cutting')
|
|
->where('verificationRequests.data.0.id', $cutting->id)
|
|
);
|
|
});
|
|
|
|
test('filtering by stock verify action shows only pending cuttings', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
$cutting = createPendingCutting();
|
|
createOwnerVerificationRequestFor(User::factory()->create());
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index', [
|
|
'action' => OwnerVerificationAction::STOCK_VERIFY->value,
|
|
]));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.source', 'cutting')
|
|
->where('verificationRequests.data.0.id', $cutting->id)
|
|
);
|
|
});
|
|
|
|
test('filtering by approved status hides pending cuttings', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
createPendingCutting();
|
|
|
|
OwnerVerificationRequest::query()->create([
|
|
'action' => OwnerVerificationAction::CREATE,
|
|
'status' => OwnerVerificationStatus::APPROVED,
|
|
'subject_type' => Product::class,
|
|
'subject_id' => Product::factory()->create()->id,
|
|
'submitted_by_id' => User::factory()->create()->id,
|
|
'payload' => ['old' => null, 'new' => ['name' => 'Approved Product']],
|
|
'verified_by_id' => $verifier->id,
|
|
'verified_at' => now(),
|
|
]);
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index', [
|
|
'status' => OwnerVerificationStatus::APPROVED->value,
|
|
]));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.source', 'request')
|
|
->where('verificationRequests.data.0.status', OwnerVerificationStatus::APPROVED->value)
|
|
);
|
|
});
|
|
|
|
test('subject options include cutting when pending cuttings exist', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
createPendingCutting();
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index'));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->where('subjectOptions', fn ($options) => collect($options)->contains(
|
|
fn (array $option) => $option['value'] === Cutting::class
|
|
&& $option['label'] === ModelLabel::for(Cutting::class),
|
|
))
|
|
);
|
|
});
|
|
|
|
test('verifier can approve pending cutting from verification page', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
$cutting = createPendingCutting();
|
|
|
|
$this->actingAs($verifier)
|
|
->post(route('admin.manage.owner_verifications.approve', $cutting))
|
|
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
|
|
|
expect($cutting->fresh()->status)->toBe(CuttingStatus::VERIFIED);
|
|
});
|
|
|
|
test('verifier can reject pending cutting from verification page', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
$cutting = createPendingCutting();
|
|
|
|
$this->actingAs($verifier)
|
|
->post(route('admin.manage.owner_verifications.reject', $cutting), [
|
|
'reason' => 'Data tidak sesuai',
|
|
])
|
|
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
|
|
|
$cutting->refresh();
|
|
|
|
expect($cutting->status)->toBe(CuttingStatus::COMPLETED);
|
|
expect($cutting->rejection?->reason)->toBe('Data tidak sesuai');
|
|
expect($cutting->rejection?->rejected_by_id)->toBe($verifier->id);
|
|
});
|
|
});
|
|
|
|
describe('Owner Verification Purchase Module', function () {
|
|
test('verifier sees purchase verification requests in index', function () {
|
|
$verifier = createOwnerVerifierUser();
|
|
$submitter = User::factory()->create();
|
|
$purchase = Purchase::factory()->create();
|
|
|
|
OwnerVerificationRequest::query()->create([
|
|
'action' => OwnerVerificationAction::CREATE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => Purchase::class,
|
|
'subject_id' => $purchase->id,
|
|
'submitted_by_id' => $submitter->id,
|
|
'payload' => [
|
|
'old' => null,
|
|
'new' => ['supplier_name' => $purchase->supplier?->name ?? 'Supplier'],
|
|
],
|
|
]);
|
|
|
|
$response = $this->actingAs($verifier)
|
|
->get(route('admin.manage.owner_verifications.index', [
|
|
'subject_type' => Purchase::class,
|
|
]));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.source', 'request')
|
|
->where('verificationRequests.data.0.subject_type', Purchase::class)
|
|
->where('verificationRequests.data.0.subject_id', $purchase->id)
|
|
);
|
|
});
|
|
|
|
test('purchase submitter sees only own purchase verification requests', function () {
|
|
$submitter = User::factory()->create();
|
|
$submitter->assignRole(RoleEnum::ADMIN_BAHAN_BAKU->value);
|
|
$submitter->forgetCachedPermissions();
|
|
|
|
$otherUser = User::factory()->create();
|
|
$ownPurchase = Purchase::factory()->create(['created_by_id' => $submitter->id]);
|
|
$otherPurchase = Purchase::factory()->create(['created_by_id' => $otherUser->id]);
|
|
|
|
OwnerVerificationRequest::query()->create([
|
|
'action' => OwnerVerificationAction::CREATE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => Purchase::class,
|
|
'subject_id' => $ownPurchase->id,
|
|
'submitted_by_id' => $submitter->id,
|
|
'payload' => ['old' => null, 'new' => ['supplier_name' => 'Own']],
|
|
]);
|
|
|
|
OwnerVerificationRequest::query()->create([
|
|
'action' => OwnerVerificationAction::CREATE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => Purchase::class,
|
|
'subject_id' => $otherPurchase->id,
|
|
'submitted_by_id' => $otherUser->id,
|
|
'payload' => ['old' => null, 'new' => ['supplier_name' => 'Other']],
|
|
]);
|
|
|
|
$response = $this->actingAs($submitter)
|
|
->get(route('admin.manage.owner_verifications.index', [
|
|
'subject_type' => Purchase::class,
|
|
]));
|
|
|
|
$response->assertOk();
|
|
$response->assertInertia(fn ($page) => $page
|
|
->has('verificationRequests.data', 1)
|
|
->where('verificationRequests.data.0.subject_id', $ownPurchase->id)
|
|
);
|
|
});
|
|
});
|