setupUser(); $role = Role::firstOrCreate(['name' => 'Owner']); Permission::firstOrCreate(['name' => 'view purchase']); Permission::firstOrCreate(['name' => 'create purchase']); Permission::firstOrCreate(['name' => 'delete purchase']); $role->givePermissionTo(['view purchase', 'create purchase', 'delete purchase']); $this->user->assignRole($role); $this->warehouse = Warehouse::factory()->create(); }); it('renders the purchase index page', function () { $this->actingAs($this->user) ->get(route('studio.stock.purchase.index')) ->assertOk() ->assertSeeLivewire(Index::class); }); it('renders the purchase create page', function () { $this->actingAs($this->user) ->get(route('studio.stock.purchase.create')) ->assertOk() ->assertSeeLivewire(Create::class); }); it('can add a perfume to the cart', function () { $perfume = Perfume::factory()->create(['cost_price' => 50000]); Livewire::actingAs($this->user) ->test(Create::class) ->set('form.perfume_id', $perfume->id) ->set('form.quantity_perfume', '10') ->call('addItem', 'parfum') ->assertHasNoErrors(); $this->assertDatabaseHas('purchase_items', [ 'user_id' => $this->user->id, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, 'quantity' => 10, 'unit_price' => 50000, 'purchase_id' => null, ]); }); it('can add a product to the cart', function () { $product = Product::factory()->create(['cost_price' => 30000]); Livewire::actingAs($this->user) ->test(Create::class) ->set('form.product_id', $product->id) ->set('form.quantity_product', '5') ->call('addItem', 'produk') ->assertHasNoErrors(); $this->assertDatabaseHas('purchase_items', [ 'user_id' => $this->user->id, 'purchasable_type' => Product::class, 'purchasable_id' => $product->id, 'quantity' => 5, 'unit_price' => 30000, 'purchase_id' => null, ]); }); it('can add a bottle to the cart', function () { $bottle = Bottle::factory()->create(['cost_price' => 5000]); Livewire::actingAs($this->user) ->test(Create::class) ->set('form.bottle_id', $bottle->id) ->set('form.quantity_bottle', '100') ->call('addItem', 'botol') ->assertHasNoErrors(); $this->assertDatabaseHas('purchase_items', [ 'user_id' => $this->user->id, 'purchasable_type' => Bottle::class, 'purchasable_id' => $bottle->id, 'quantity' => 100, 'unit_price' => 5000, 'purchase_id' => null, ]); }); it('can update item quantity in the cart', function () { $perfume = Perfume::factory()->create(['cost_price' => 50000]); $item = PurchaseItem::factory()->create([ 'user_id' => $this->user->id, 'purchase_id' => null, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, 'quantity' => 10, 'unit_price' => 50000, 'total_price' => 500000, ]); Livewire::actingAs($this->user) ->test(Create::class) ->set('form.item_id', $item->id) ->set('form.quantity_edit', '15') ->call('updateItem') ->assertHasNoErrors(); $this->assertDatabaseHas('purchase_items', [ 'id' => $item->id, 'quantity' => 15, 'total_price' => 750000, ]); }); it('can delete an item from the cart', function () { $perfume = Perfume::factory()->create(); $item = PurchaseItem::factory()->create([ 'user_id' => $this->user->id, 'purchase_id' => null, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, ]); Livewire::actingAs($this->user) ->test(Create::class) ->call('deleteItem', $item->id) ->assertHasNoErrors(); $this->assertSoftDeleted('purchase_items', ['id' => $item->id]); }); it('can store a purchase and update warehouse stock', function () { $perfume = Perfume::factory()->create(['cost_price' => 100000]); PurchaseItem::factory()->create([ 'user_id' => $this->user->id, 'purchase_id' => null, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, 'quantity' => 10, 'unit_price' => 100000, 'total_price' => 1000000, ]); Livewire::actingAs($this->user) ->test(Create::class) ->set('form.warehouse_id', $this->warehouse->id) ->set('form.purchase_date', now()->format('Y-m-d')) ->set('form.invoice_number', 'INV-001') ->call('save') ->assertHasNoErrors() ->assertRedirect(route('studio.stock.purchase.index', [ 'notification' => 'Belanja berhasil ditambahkan.', ])); $this->assertDatabaseHas('purchases', [ 'warehouse_id' => $this->warehouse->id, 'invoice_number' => 'INV-001', 'total' => 1000000, ]); // Check stock update $this->assertDatabaseHas('perfume_warehouse', [ 'warehouse_id' => $this->warehouse->id, 'perfume_id' => $perfume->id, 'stock' => 10, ]); }); it('can store a purchase with image', function () { $perfume = Perfume::factory()->create(['cost_price' => 100000]); PurchaseItem::factory()->create([ 'user_id' => $this->user->id, 'purchase_id' => null, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, 'quantity' => 10, 'unit_price' => 100000, 'total_price' => 1000000, ]); Storage::fake('public'); $image = \Illuminate\Http\UploadedFile::fake()->image('invoice.jpg'); Livewire::actingAs($this->user) ->test(Create::class) ->set('form.warehouse_id', $this->warehouse->id) ->set('form.purchase_date', now()->format('Y-m-d')) ->set('form.image', [[ 'name' => 'invoice.jpg', 'path' => Storage::disk('public')->path($image->store('tmp', 'public')), 'size' => $image->getSize(), 'type' => $image->getMimeType(), ]]) ->call('save') ->assertHasNoErrors(); $purchase = Purchase::latest()->first(); expect($purchase->getFirstMediaUrl('image'))->not->toBeEmpty(); }); it('cannot store a purchase with empty cart', function () { Livewire::actingAs($this->user) ->test(Create::class) ->set('form.warehouse_id', $this->warehouse->id) ->set('form.purchase_date', now()->format('Y-m-d')) ->call('save') ->assertHasNoErrors(); // It shows a toast, doesn't redirect $this->assertDatabaseCount('purchases', 0); }); it('validates required fields', function () { $perfume = Perfume::factory()->create(); PurchaseItem::factory()->create([ 'user_id' => $this->user->id, 'purchase_id' => null, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, ]); Livewire::actingAs($this->user) ->test(Create::class) ->call('save') ->assertHasErrors([ 'form.warehouse_id' => 'required', 'form.purchase_date' => 'required', ]); }); it('can delete a purchase', function () { $perfume = Perfume::factory()->create(); $purchase = Purchase::factory()->create([ 'warehouse_id' => $this->warehouse->id, ]); $item = PurchaseItem::factory()->create([ 'purchase_id' => $purchase->id, 'purchasable_type' => Perfume::class, 'purchasable_id' => $perfume->id, 'quantity' => 10, ]); // Initial stock (assuming it was added when purchase was created) $this->warehouse->perfumes()->attach($perfume->id, ['stock' => 10]); Livewire::actingAs($this->user) ->test(Index::class) ->call('delete', $purchase->id) ->assertHasNoErrors(); $this->assertSoftDeleted('purchases', ['id' => $purchase->id]); // Check stock was decreased $this->assertDatabaseMissing('perfume_warehouse', [ 'warehouse_id' => $this->warehouse->id, 'perfume_id' => $perfume->id, 'stock' => 10, ]); }); it('cannot access purchase without permission', function () { $this->user->roles()->detach(); $this->user->permissions()->detach(); $this->actingAs($this->user) ->get(route('studio.stock.purchase.index')) ->assertForbidden(); $this->actingAs($this->user) ->get(route('studio.stock.purchase.create')) ->assertForbidden(); });