- Removed legacy stock management components including PurchaseForm, RestockForm, StockOpnameForm, and StockTransferForm. - Updated routes to reflect new structure under 'studio.stock' namespace for better organization. - Adjusted view files for stock management to align with the new routing and component structure. - Modified action routes in the ViewServiceProvider to ensure proper access control and navigation. - Cleaned up related test files to remove references to deleted components.
286 lines
8.8 KiB
PHP
286 lines
8.8 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Studio\Stock\Purchase\Create;
|
|
use App\Livewire\Studio\Stock\Purchase\Index;
|
|
use App\Models\Bottle;
|
|
use App\Models\Perfume;
|
|
use App\Models\Product;
|
|
use App\Models\Purchase;
|
|
use App\Models\PurchaseItem;
|
|
use App\Models\Warehouse;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Livewire\Livewire;
|
|
use Spatie\Permission\Models\Permission;
|
|
use Spatie\Permission\Models\Role;
|
|
|
|
beforeEach(function () {
|
|
$this->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();
|
|
});
|