store/tests/Feature/Admin/Manage/StockTest.php
Yoga Pangestu 289fc4dc2c
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (8.3) (push) Waiting to run
tests / ci (8.4) (push) Waiting to run
tests / ci (8.5) (push) Waiting to run
feat: implement stock quality categorization in StokOpname; enhance validation rules and update frontend components for improved inventory tracking and reporting
2026-07-05 14:06:47 +07:00

198 lines
6.2 KiB
PHP

<?php
use App\Enums\CuttingStatus;
use App\Enums\Permission as PermissionEnum;
use App\Models\Cutting;
use App\Models\CuttingMaterial;
use App\Models\CuttingResult;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\RawMaterial;
use App\Models\RawMaterialPrice;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
});
// ─── Helper ───────────────────────────────────────────────
function createStockUserWithPermission(PermissionEnum ...$permissions): User
{
$user = User::factory()->create();
$user->givePermissionTo(
array_merge(
[PermissionEnum::DASHBOARD_VIEW->value],
array_map(fn (PermissionEnum $p) => $p->value, $permissions)
)
);
$user->forgetCachedPermissions();
return $user;
}
function createCompletedCuttingSetup(): array
{
$product = Product::factory()->create();
$variant = ProductVariant::factory()->create([
'product_id' => $product->id,
'stock' => 10,
'reject_stock' => 5,
]);
$rawMaterial = RawMaterial::factory()->create();
$rawMaterialPrice = RawMaterialPrice::factory()->create([
'raw_material_id' => $rawMaterial->id,
]);
$cutting = Cutting::factory()->create([
'status' => CuttingStatus::COMPLETED,
]);
CuttingMaterial::factory()->create([
'cutting_id' => $cutting->id,
'raw_material_price_id' => $rawMaterialPrice->id,
]);
CuttingResult::factory()->create([
'cutting_id' => $cutting->id,
'product_variant_id' => $variant->id,
'cutting_result' => 7,
'sample' => 5, // Good stock result
'original_outside_sample' => 2, // Reject stock result
]);
return [
'cutting' => $cutting,
'variant' => $variant,
'raw_material_price' => $rawMaterialPrice,
];
}
// ─── Tests ────────────────────────────────────────────────
describe('Stock Warehouse Management', function () {
test('authenticated user with permission can view stocks index', function () {
$user = createStockUserWithPermission(PermissionEnum::STOCKS_VIEW);
$this->actingAs($user)
->get(route('admin.manage.stocks.index'))
->assertOk();
});
test('user with permission can submit verification for completed cutting', function () {
$user = createStockUserWithPermission(
PermissionEnum::STOCKS_VIEW,
PermissionEnum::CUTTINGS_VERIFY
);
$setup = createCompletedCuttingSetup();
$cutting = $setup['cutting'];
$payload = [
'verification_note' => 'Verification notes',
'results' => [
[
'product_variant_id' => $setup['variant']->id,
'good' => 6, // changed from 5 to 6
'reject' => 1, // changed from 2 to 1
]
],
'result_prices' => [
[
'product_variant_id' => $setup['variant']->id,
'prices' => [
[
'type' => 'harga_modal',
'price' => 50000,
],
[
'type' => 'retail',
'price' => 75000,
]
]
]
],
];
$this->actingAs($user)
->post(route('admin.manage.stocks.verify', $cutting), $payload)
->assertRedirect(route('admin.manage.stocks.index'));
$cutting->refresh();
$this->assertEquals(CuttingStatus::PENDING_VERIFICATION, $cutting->status);
// Verify cutting results are updated
$this->assertDatabaseHas('cutting_results', [
'cutting_id' => $cutting->id,
'product_variant_id' => $setup['variant']->id,
'sample' => 6,
'original_outside_sample' => 1,
]);
});
test('user with permission can approve verification and increase stock', function () {
$user = createStockUserWithPermission(
PermissionEnum::STOCKS_VIEW,
PermissionEnum::OWNER_VERIFICATIONS_VERIFY
);
$setup = createCompletedCuttingSetup();
$cutting = $setup['cutting'];
$cutting->status = CuttingStatus::PENDING_VERIFICATION;
$cutting->save();
$this->actingAs($user)
->post(route('admin.manage.owner_verifications.approve', $cutting), [
'approval_note' => 'Approved by owner',
])
->assertRedirect();
$cutting->refresh();
$setup['variant']->refresh();
$this->assertEquals(CuttingStatus::VERIFIED, $cutting->status);
// Stock (good) should increment by 5 (10 + 5 = 15)
$this->assertEquals(15, $setup['variant']->stock);
// Reject stock should increment by 2 (5 + 2 = 7)
$this->assertEquals(7, $setup['variant']->reject_stock);
});
test('user with permission can reject verification', function () {
$user = createStockUserWithPermission(
PermissionEnum::STOCKS_VIEW,
PermissionEnum::OWNER_VERIFICATIONS_REJECT
);
$setup = createCompletedCuttingSetup();
$cutting = $setup['cutting'];
$cutting->status = CuttingStatus::PENDING_VERIFICATION;
$cutting->save();
$this->actingAs($user)
->post(route('admin.manage.owner_verifications.reject', $cutting), [
'reason' => 'Invalid results',
])
->assertRedirect();
$cutting->refresh();
// Status should revert to COMPLETED
$this->assertEquals(CuttingStatus::COMPLETED, $cutting->status);
$this->assertDatabaseHas('rejections', [
'rejectable_id' => $cutting->id,
'rejectable_type' => Cutting::class,
'reason' => 'Invalid results',
]);
});
});