- Add RestockIndex component for displaying and managing restocks. - Create RestockCardRow component for rendering individual restock items. - Implement RestockItemSubRow component for displaying detailed item information. - Define routes for restock management in web.php. - Create RestockTest to cover various scenarios for restock creation, updating, and deletion. - Ensure proper handling of permissions for restock actions. - Add validation for restock data and ensure correct relationships are maintained.
839 lines
26 KiB
PHP
839 lines
26 KiB
PHP
<?php
|
|
|
|
use App\Enums\PriceType;
|
|
use App\Enums\ProductStockQuality;
|
|
use App\Models\ProductVariant;
|
|
use App\Models\Restock;
|
|
use App\Models\RestockItem;
|
|
use App\Models\User;
|
|
use Database\Seeders\RolePermissionSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| HELPERS
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
function giveRestockPermissions(User $user): void
|
|
{
|
|
$seeder = new RolePermissionSeeder;
|
|
$seeder->run();
|
|
|
|
$user->givePermissionTo([
|
|
'restock.view',
|
|
'restock.create',
|
|
'restock.update',
|
|
'restock.delete',
|
|
]);
|
|
}
|
|
|
|
function makeRestockVariant(array $overrides = []): ProductVariant
|
|
{
|
|
$variant = ProductVariant::factory()->create(array_merge([
|
|
'stock' => 10,
|
|
'reject_stock' => 5,
|
|
], $overrides));
|
|
|
|
$variant->productPrices()->create([
|
|
'type' => PriceType::CAPITAL,
|
|
'price' => 50000,
|
|
]);
|
|
|
|
return $variant;
|
|
}
|
|
|
|
function makeValidRestockPayload(array $overrides = []): array
|
|
{
|
|
$variant = makeRestockVariant();
|
|
|
|
return array_merge([
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => 'Restock test',
|
|
], $overrides);
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| AUTHENTICATION
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('guests are redirected to the login page', function () {
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guests are redirected when visiting create page', function () {
|
|
$response = $this->get(route('admin.manage.restocks.create'));
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guests are redirected when visiting edit page', function () {
|
|
$restock = Restock::factory()->create();
|
|
$response = $this->get(route('admin.manage.restocks.edit', $restock));
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guest cannot create restock', function () {
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload());
|
|
$response->assertRedirect(route('login'));
|
|
$this->assertDatabaseCount('restocks', 0);
|
|
});
|
|
|
|
test('guest cannot update restock', function () {
|
|
$restock = Restock::factory()->create();
|
|
$response = $this->put(route('admin.manage.restocks.update', $restock), makeValidRestockPayload());
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guest cannot delete restock', function () {
|
|
$restock = Restock::factory()->create();
|
|
$response = $this->delete(route('admin.manage.restocks.destroy', $restock));
|
|
$response->assertRedirect(route('login'));
|
|
$this->assertDatabaseHas('restocks', ['id' => $restock->id, 'deleted_at' => null]);
|
|
});
|
|
|
|
test('authenticated users can visit the restock index page', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertOk();
|
|
});
|
|
|
|
test('authenticated users can visit the restock create page', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->get(route('admin.manage.restocks.create'));
|
|
$response->assertOk();
|
|
});
|
|
|
|
test('authenticated users can visit the restock edit page', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$restock = Restock::factory()->create();
|
|
|
|
$response = $this->get(route('admin.manage.restocks.edit', $restock));
|
|
$response->assertOk();
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| INDEX PAGE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('restock index page displays restocks', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
Restock::factory()->count(3)->create();
|
|
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('admin/manage/restock/index')
|
|
->has('restocks.data', 3)
|
|
->where('restocks.total', 3)
|
|
->where('restocks.current_page', 1)
|
|
->where('restocks.per_page', 25)
|
|
);
|
|
});
|
|
|
|
test('index page works with zero restocks', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('admin/manage/restock/index')
|
|
->has('restocks.data', 0)
|
|
->where('restocks.total', 0)
|
|
);
|
|
});
|
|
|
|
test('index page does not display soft-deleted restocks', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
Restock::factory()->create(['notes' => 'Active Restock']);
|
|
Restock::factory()->create(['notes' => 'Deleted Restock'])->delete();
|
|
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('admin/manage/restock/index')
|
|
->has('restocks.data', 1)
|
|
->where('restocks.total', 1)
|
|
);
|
|
});
|
|
|
|
test('index page includes restock items with nested data', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload());
|
|
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->has('restocks.data.0.restock_items', 1)
|
|
->has('restocks.data.0.restock_items.0.product_variant')
|
|
->has('restocks.data.0.restock_items.0.product_variant.product')
|
|
);
|
|
});
|
|
|
|
test('index page returns correct restock item data', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['name' => 'Hitam M']);
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => 'Restock test',
|
|
]);
|
|
|
|
$response = $this->get(route('admin.manage.restocks.index'));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->where('restocks.data.0.stock_type', 'good')
|
|
->where('restocks.data.0.restock_items.0.quantity', 10)
|
|
->where('restocks.data.0.restock_items.0.unit_price', 50000)
|
|
->where('restocks.data.0.restock_items.0.subtotal', 500000)
|
|
->where('restocks.data.0.restock_items.0.product_variant.name', 'Hitam M')
|
|
->where('restocks.data.0.restock_items.0.product_variant.product.name', $variant->product->name)
|
|
);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| CREATE / STORE - BASIC
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('restock can be created', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload());
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect(route('admin.manage.restocks.index'));
|
|
|
|
$this->assertDatabaseCount('restocks', 1);
|
|
$this->assertDatabaseCount('restock_items', 1);
|
|
});
|
|
|
|
test('restock increases product variant stock for good quality', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['stock' => 10, 'reject_stock' => 5]);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 7],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$variant->refresh();
|
|
expect($variant->stock)->toBe(17);
|
|
expect($variant->reject_stock)->toBe(5);
|
|
});
|
|
|
|
test('restock increases product variant reject stock for reject quality', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['stock' => 10, 'reject_stock' => 5]);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'reject',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 7],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$variant->refresh();
|
|
expect($variant->stock)->toBe(10);
|
|
expect($variant->reject_stock)->toBe(12);
|
|
});
|
|
|
|
test('restock uses capital price for unit price and subtotal', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant();
|
|
$variant->productPrices()->updateOrCreate(
|
|
['type' => PriceType::CAPITAL],
|
|
['price' => 60000],
|
|
);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->subtotal)->toBe(600000);
|
|
expect($restock->total)->toBe(600000);
|
|
|
|
$item = RestockItem::first();
|
|
expect($item->unit_price)->toBe(60000);
|
|
expect($item->subtotal)->toBe(600000);
|
|
});
|
|
|
|
test('restock calculates subtotal across multiple items', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variantA = makeRestockVariant();
|
|
$variantB = makeRestockVariant();
|
|
$variantB->productPrices()->updateOrCreate(
|
|
['type' => PriceType::CAPITAL],
|
|
['price' => 70000],
|
|
);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variantA->id, 'quantity' => 10],
|
|
['product_variant_id' => $variantB->id, 'quantity' => 5],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->subtotal)->toBe(850000); // (50000*10) + (70000*5)
|
|
expect($restock->total)->toBe(850000);
|
|
});
|
|
|
|
test('restock sets created_by_id to current user', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload());
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->created_by_id)->toBe($user->id);
|
|
});
|
|
|
|
test('restock can be created with notes', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'notes' => 'Restock tambahan untuk toko',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->notes)->toBe('Restock tambahan untuk toko');
|
|
});
|
|
|
|
test('restock can be created without notes', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'notes' => null,
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->notes)->toBeNull();
|
|
});
|
|
|
|
test('restock creates items with correct relationships', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload());
|
|
|
|
$restock = Restock::first();
|
|
$item = RestockItem::first();
|
|
|
|
expect($item->restock_id)->toBe($restock->id);
|
|
expect($item->user_id)->toBe($user->id);
|
|
expect($item->product_variant_id)->not->toBeNull();
|
|
expect($item->quantity)->toBe(10);
|
|
expect($item->unit_price)->toBe(50000);
|
|
expect($item->subtotal)->toBe(500000);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| STORE VALIDATION
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('restock items are required', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'items' => [],
|
|
]));
|
|
$response->assertSessionHasErrors('items');
|
|
});
|
|
|
|
test('restock item quantity must be at least 1', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'items' => [
|
|
['product_variant_id' => ProductVariant::factory()->create()->id, 'quantity' => 0],
|
|
],
|
|
]));
|
|
$response->assertSessionHasErrors('items.0.quantity');
|
|
});
|
|
|
|
test('restock item product variant must exist', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'items' => [
|
|
['product_variant_id' => 999999, 'quantity' => 5],
|
|
],
|
|
]));
|
|
$response->assertSessionHasErrors('items.0.product_variant_id');
|
|
});
|
|
|
|
test('restock stock_type must be valid', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'stock_type' => 'invalid',
|
|
]));
|
|
$response->assertSessionHasErrors('stock_type');
|
|
});
|
|
|
|
test('restock notes must not exceed 100 characters', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'notes' => str_repeat('a', 101),
|
|
]));
|
|
$response->assertSessionHasErrors('notes');
|
|
});
|
|
|
|
test('restock photo_key must not exceed 500 characters', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => str_repeat('a', 501),
|
|
]));
|
|
$response->assertSessionHasErrors('photo_key');
|
|
});
|
|
|
|
test('restock photo is stored on create', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => 'restock/test-photo.jpg',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->getFirstMedia('photos')?->file_name)
|
|
->toBe('restock/test-photo.jpg');
|
|
});
|
|
|
|
test('restock photo is returned on edit page', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => 'restock/test-photo.jpg',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
|
|
$response = $this->get(route('admin.manage.restocks.edit', $restock));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->where('restock.photo_key', 'restock/test-photo.jpg')
|
|
);
|
|
});
|
|
|
|
test('restock photo is replaced on update', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => 'restock/old-photo.jpg',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
|
|
$this->put(route('admin.manage.restocks.update', $restock), makeValidRestockPayload([
|
|
'photo_key' => 'restock/new-photo.jpg',
|
|
]));
|
|
|
|
$media = $restock->fresh()->getFirstMedia('photos');
|
|
expect($media?->file_name)->toBe('restock/new-photo.jpg');
|
|
expect($restock->getMedia('photos'))->toHaveCount(1);
|
|
});
|
|
|
|
test('restock photo is removed when photo_key is null', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => 'restock/test-photo.jpg',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
|
|
$this->put(route('admin.manage.restocks.update', $restock), makeValidRestockPayload([
|
|
'photo_key' => null,
|
|
]));
|
|
|
|
expect($restock->fresh()->getMedia('photos'))->toHaveCount(0);
|
|
});
|
|
|
|
test('restock photo is kept when photo_key is unchanged', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => 'restock/same-photo.jpg',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
|
|
$this->put(route('admin.manage.restocks.update', $restock), makeValidRestockPayload([
|
|
'photo_key' => 'restock/same-photo.jpg',
|
|
]));
|
|
|
|
$media = $restock->fresh()->getFirstMedia('photos');
|
|
expect($media?->file_name)->toBe('restock/same-photo.jpg');
|
|
expect($restock->getMedia('photos'))->toHaveCount(1);
|
|
});
|
|
|
|
test('restock stock quality enum has labels', function () {
|
|
expect(ProductStockQuality::GOOD->label())->toBe('Bagus');
|
|
expect(ProductStockQuality::REJECT->label())->toBe('Reject');
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| EDIT PAGE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('edit page shows restock data', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['name' => 'Hitam M']);
|
|
$restock = Restock::factory()->create([
|
|
'stock_type' => 'good',
|
|
'notes' => 'Test notes',
|
|
]);
|
|
RestockItem::factory()->create([
|
|
'restock_id' => $restock->id,
|
|
'product_variant_id' => $variant->id,
|
|
'quantity' => 10,
|
|
'unit_price' => 50000,
|
|
'subtotal' => 500000,
|
|
]);
|
|
|
|
$response = $this->get(route('admin.manage.restocks.edit', $restock));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('admin/manage/restock/edit')
|
|
->where('restock.id', $restock->id)
|
|
->where('restock.stock_type', 'good')
|
|
->where('restock.notes', 'Test notes')
|
|
->has('restock.items', 1)
|
|
->where('restock.items.0.product_variant_id', $variant->id)
|
|
->where('restock.items.0.quantity', 10)
|
|
->has('data.products')
|
|
);
|
|
});
|
|
|
|
test('create page returns products with capital price', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['name' => 'Hitam M']);
|
|
|
|
$response = $this->get(route('admin.manage.restocks.create'));
|
|
$response->assertOk();
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->has('data.products', 1)
|
|
->has('data.products.0.product_variants', 1)
|
|
->where('data.products.0.product_variants.0.id', $variant->id)
|
|
->where('data.products.0.product_variants.0.capital_price', 50000)
|
|
);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| UPDATE / PUT
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('restock can be updated', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload());
|
|
|
|
$restock = Restock::first();
|
|
|
|
$variant = makeRestockVariant();
|
|
$response = $this->put(route('admin.manage.restocks.update', $restock), [
|
|
'stock_type' => 'reject',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 4],
|
|
],
|
|
'notes' => 'Updated notes',
|
|
]);
|
|
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect();
|
|
|
|
$restock->refresh();
|
|
expect($restock->stock_type->value)->toBe('reject');
|
|
expect($restock->notes)->toBe('Updated notes');
|
|
expect($restock->subtotal)->toBe(200000);
|
|
expect(RestockItem::where('restock_id', $restock->id)->count())->toBe(1);
|
|
expect(RestockItem::first()->product_variant_id)->toBe($variant->id);
|
|
});
|
|
|
|
test('restock update adjusts stock by difference', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['stock' => 10, 'reject_stock' => 5]);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
expect($variant->fresh()->stock)->toBe(20);
|
|
|
|
$restock = Restock::first();
|
|
|
|
$this->put(route('admin.manage.restocks.update', $restock), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 4],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
expect($variant->fresh()->stock)->toBe(14);
|
|
expect($variant->fresh()->reject_stock)->toBe(5);
|
|
});
|
|
|
|
test('restock update moves stock when quality changes', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['stock' => 10, 'reject_stock' => 5]);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
expect($variant->fresh()->stock)->toBe(20);
|
|
|
|
$restock = Restock::first();
|
|
|
|
$this->put(route('admin.manage.restocks.update', $restock), [
|
|
'stock_type' => 'reject',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$variant->refresh();
|
|
expect($variant->stock)->toBe(10);
|
|
expect($variant->reject_stock)->toBe(15);
|
|
});
|
|
|
|
test('updating non-existent restock returns 404', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.manage.restocks.update', 999999), makeValidRestockPayload());
|
|
$response->assertStatus(404);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| DELETE / DESTROY
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('restock can be deleted and reduces product variant stock', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['stock' => 10, 'reject_stock' => 5]);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$restock = Restock::first();
|
|
expect($variant->fresh()->stock)->toBe(20);
|
|
|
|
$response = $this->delete(route('admin.manage.restocks.destroy', $restock));
|
|
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect(route('admin.manage.restocks.index'));
|
|
|
|
$this->assertSoftDeleted('restocks', ['id' => $restock->id]);
|
|
expect($variant->fresh()->stock)->toBe(10);
|
|
expect($variant->fresh()->reject_stock)->toBe(5);
|
|
});
|
|
|
|
test('restock delete reduces reject stock for reject restocks', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant(['stock' => 10, 'reject_stock' => 5]);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'reject',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 7],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$restock = Restock::first();
|
|
expect($variant->fresh()->reject_stock)->toBe(12);
|
|
|
|
$this->delete(route('admin.manage.restocks.destroy', $restock));
|
|
|
|
$this->assertSoftDeleted('restocks', ['id' => $restock->id]);
|
|
expect($variant->fresh()->reject_stock)->toBe(5);
|
|
});
|
|
|
|
test('delete cascades to restock items', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$variant = makeRestockVariant();
|
|
$this->post(route('admin.manage.restocks.store'), [
|
|
'stock_type' => 'good',
|
|
'items' => [
|
|
['product_variant_id' => $variant->id, 'quantity' => 10],
|
|
],
|
|
'notes' => null,
|
|
]);
|
|
|
|
$restock = Restock::first();
|
|
expect(RestockItem::where('restock_id', $restock->id)->count())->toBe(1);
|
|
|
|
$this->delete(route('admin.manage.restocks.destroy', $restock));
|
|
|
|
expect(RestockItem::where('restock_id', $restock->id)->count())->toBe(0);
|
|
});
|
|
|
|
test('restock delete removes attached photo', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$this->post(route('admin.manage.restocks.store'), makeValidRestockPayload([
|
|
'photo_key' => 'restock/delete-photo.jpg',
|
|
]));
|
|
|
|
$restock = Restock::first();
|
|
expect($restock->getFirstMedia('photos')?->file_name)
|
|
->toBe('restock/delete-photo.jpg');
|
|
|
|
$this->delete(route('admin.manage.restocks.destroy', $restock));
|
|
|
|
expect($restock->fresh()->getMedia('photos'))->toHaveCount(0);
|
|
});
|
|
|
|
test('deleting non-existent restock returns 404', function () {
|
|
$user = User::factory()->create();
|
|
giveRestockPermissions($user);
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->delete(route('admin.manage.restocks.destroy', 999999));
|
|
$response->assertStatus(404);
|
|
});
|