assertRedirect(route('login')); }); it('returns 403 when user has no permission to view purchases', function () { actingAs(createUnauthorizedUser()) ->get(route('purchase.index')) ->assertStatus(403); }); }); describe('Purchase Module - Authorized Actions', function () { beforeEach(function () { $user = createAuthorizedUser([ 'View:Purchase', 'Create:Purchase', 'Edit:Purchase', 'Delete:Purchase', 'DeleteAny:Purchase', ]); actingAs($user); }); it('can access purchase index page', function () { get(route('purchase.index')) ->assertOk() ->assertInertia(fn ($page) => $page ->component('admin/manage/purchase/index') ->has('purchases', fn ($page) => $page ->has('data') ->etc() ) ); }); it('can access purchase create page', function () { get(route('purchase.create')) ->assertOk() ->assertInertia(fn ($page) => $page ->component('admin/manage/purchase/create') ->has('products') ->has('cartItems') ); }); it('can manage cart items', function () { $product = Product::factory()->create(); // 1. Add to cart postJson(route('purchase.addToCart'), [ 'product_id' => $product->id, 'quantity' => 5, 'unit_price' => 5000, ])->assertRedirect(); assertDatabaseHas('purchase_items', [ 'user_id' => auth()->id(), 'purchase_id' => null, 'product_id' => $product->id, 'quantity' => 5, ]); $cartItem = PurchaseItem::where('product_id', $product->id)->whereNull('purchase_id')->first(); // 2. Update cart item patchJson(route('purchase.updateCartItem', $cartItem), [ 'product_id' => $product->id, 'quantity' => 10, 'unit_price' => 5000, ])->assertRedirect(); expect($cartItem->fresh()->quantity)->toBe(10); // 3. Remove from cart deleteJson(route('purchase.removeFromCart', $cartItem))->assertRedirect(); assertSoftDeleted('purchase_items', ['id' => $cartItem->id]); }); it('can store a new purchase and increase stock', function () { $product = Product::factory()->create(['stock' => 10]); $data = [ 'purchase_date' => now()->format('Y-m-d'), 'note' => 'Test Purchase', 'items' => [ [ 'product_id' => $product->id, 'quantity' => 20, 'unit_price' => 5000, ], ], ]; postJson(route('purchase.store'), $data) ->assertRedirect(route('purchase.index')) ->assertSessionHas('success'); assertDatabaseHas('purchases', [ 'note' => 'Test Purchase', 'total' => 100000, ]); // Check stock increment (10 + 20 = 30) expect($product->fresh()->stock)->toBe(30); // Check purchase items assertDatabaseHas('purchase_items', [ 'product_id' => $product->id, 'quantity' => 20, 'unit_price' => 5000, ]); }); it('validates purchase creation', function () { postJson(route('purchase.store'), []) ->assertStatus(422) ->assertJsonValidationErrors(['purchase_date', 'items']); }); it('returns flash error when stock is insufficient on purchase update', function () { $product = Product::factory()->create(['stock' => 0]); $purchase = Purchase::create([ 'purchase_date' => now()->format('Y-m-d'), 'note' => 'Old Note', 'total' => 5000, ]); $purchase->items()->create([ 'user_id' => auth()->id(), 'product_id' => $product->id, 'quantity' => 5, 'unit_price' => 1000, 'total_price' => 5000, ]); $newData = [ 'purchase_date' => now()->format('Y-m-d'), 'note' => 'Updated Note', 'items' => [ [ 'product_id' => $product->id, 'quantity' => 10, 'unit_price' => 2000, ], ], ]; patchJson(route('purchase.update', $purchase), $newData) ->assertRedirect() ->assertSessionHas('error'); expect($purchase->fresh()->note)->toBe('Old Note'); }); it('returns flash error when stock is insufficient on purchase delete', function () { $product = Product::factory()->create(['stock' => 0]); $purchase = Purchase::factory()->create(); $purchase->items()->create([ 'user_id' => auth()->id(), 'product_id' => $product->id, 'quantity' => 5, 'unit_price' => 1000, 'total_price' => 5000, ]); deleteJson(route('purchase.destroy', $purchase)) ->assertRedirect() ->assertSessionHas('error'); expect(Purchase::find($purchase->id))->not->toBeNull(); }); it('can access purchase edit page', function () { $purchase = Purchase::factory()->create(); get(route('purchase.edit', $purchase)) ->assertOk() ->assertInertia(fn ($page) => $page ->component('admin/manage/purchase/edit') ->has('purchase') ->has('products') ); }); it('can update a purchase and adjust stock', function () { $product1 = Product::factory()->create(['stock' => 10]); $product2 = Product::factory()->create(['stock' => 10]); $purchase = Purchase::create([ 'purchase_date' => now()->format('Y-m-d'), 'note' => 'Old Note', 'total' => 5000, ]); $purchase->items()->create([ 'user_id' => auth()->id(), 'product_id' => $product1->id, 'quantity' => 5, 'unit_price' => 1000, 'total_price' => 5000, ]); // Manually adjust stock as if it was stored $product1->increment('stock', 5); $newData = [ 'purchase_date' => now()->format('Y-m-d'), 'note' => 'Updated Note', 'items' => [ [ 'product_id' => $product2->id, 'quantity' => 15, 'unit_price' => 2000, ], ], ]; patchJson(route('purchase.update', $purchase), $newData) ->assertRedirect(route('purchase.index')) ->assertSessionHas('success'); // Check data updated assertDatabaseHas('purchases', [ 'id' => $purchase->id, 'note' => 'Updated Note', ]); // Check stock: product1 should be decremented back (15 - 5 = 10), product2 should be incremented (10 + 15 = 25) expect($product1->fresh()->stock)->toBe(10); expect($product2->fresh()->stock)->toBe(25); }); it('can delete a purchase and decrease stock', function () { $product = Product::factory()->create(['stock' => 20]); $purchase = Purchase::factory()->create(); $purchase->items()->create([ 'user_id' => auth()->id(), 'product_id' => $product->id, 'quantity' => 10, 'unit_price' => 1000, 'total_price' => 10000, ]); // Initial stock check // expect($product->fresh()->stock)->toBe(20); deleteJson(route('purchase.destroy', $purchase)) ->assertRedirect() ->assertSessionHas('success'); assertSoftDeleted('purchases', ['id' => $purchase->id]); // Stock should be decreased (20 - 10 = 10) expect($product->fresh()->stock)->toBe(10); }); it('can delete purchases in bulk', function () { $purchases = Purchase::factory()->count(3)->create(); $ids = $purchases->pluck('id')->toArray(); deleteJson(route('purchase.bulkDestroy'), ['ids' => $ids]) ->assertRedirect() ->assertSessionHas('success'); foreach ($ids as $id) { assertSoftDeleted('purchases', ['id' => $id]); } }); }); describe('Purchase Module - Unauthorized Actions', function () { beforeEach(function () { actingAs(createUnauthorizedUser()); }); it('cannot store a purchase without permission', function () { postJson(route('purchase.store'), ['purchase_date' => now()->format('Y-m-d')]) ->assertStatus(403); }); it('cannot update a purchase without permission', function () { $purchase = Purchase::factory()->create(); patchJson(route('purchase.update', $purchase), ['purchase_date' => now()->format('Y-m-d')]) ->assertStatus(403); }); it('cannot delete a purchase without permission', function () { $purchase = Purchase::factory()->create(); deleteJson(route('purchase.destroy', $purchase)) ->assertStatus(403); }); });