295 lines
9.4 KiB
PHP
295 lines
9.4 KiB
PHP
<?php
|
|
|
|
use App\Enums\OrderChannel;
|
|
use App\Enums\OrderStatus;
|
|
use App\Enums\PaymentMethod;
|
|
use App\Enums\PriceType;
|
|
use App\Models\Order;
|
|
use App\Models\OrderItem;
|
|
use App\Models\Product;
|
|
|
|
use function Pest\Laravel\actingAs;
|
|
use function Pest\Laravel\assertDatabaseHas;
|
|
use function Pest\Laravel\assertSoftDeleted;
|
|
use function Pest\Laravel\deleteJson;
|
|
use function Pest\Laravel\get;
|
|
use function Pest\Laravel\patchJson;
|
|
use function Pest\Laravel\postJson;
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Order Module Tests
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
describe('Order Module - Authorization', function () {
|
|
it('redirects to login when accessing order index unauthenticated', function () {
|
|
get(route('order.index'))
|
|
->assertRedirect(route('login'));
|
|
});
|
|
|
|
it('returns 403 when user has no permission to view orders', function () {
|
|
actingAs(createUnauthorizedUser())
|
|
->get(route('order.index'))
|
|
->assertStatus(403);
|
|
});
|
|
});
|
|
|
|
describe('Order Module - Authorized Actions', function () {
|
|
beforeEach(function () {
|
|
$user = createAuthorizedUser([
|
|
'View:Order',
|
|
'Create:Order',
|
|
'Edit:Order',
|
|
'Delete:Order',
|
|
'DeleteAny:Order',
|
|
]);
|
|
actingAs($user);
|
|
});
|
|
|
|
it('can access order index page', function () {
|
|
Order::factory()->create();
|
|
|
|
get(route('order.index'))
|
|
->assertOk()
|
|
->assertInertia(fn ($page) => $page
|
|
->component('admin/manage/order/index')
|
|
->has('orders', fn ($page) => $page
|
|
->has('0.user')
|
|
->has('0.cogs_formatted')
|
|
->has('0.payment_formatted')
|
|
->has('0.created_at_formatted')
|
|
->etc()
|
|
)
|
|
);
|
|
});
|
|
|
|
it('can access order create page', function () {
|
|
get(route('order.create'))
|
|
->assertOk()
|
|
->assertInertia(fn ($page) => $page
|
|
->component('admin/manage/order/create')
|
|
->has('products')
|
|
->has('cartItems')
|
|
);
|
|
});
|
|
|
|
it('can manage cart items', function () {
|
|
$product = Product::factory()->create(['stock' => 10]);
|
|
|
|
// 1. Add to cart
|
|
postJson(route('order.addToCart'), [
|
|
'product_id' => $product->id,
|
|
'qty' => 2,
|
|
'price' => 10000,
|
|
'price_type' => PriceType::RETAIL->value,
|
|
])->assertRedirect();
|
|
|
|
assertDatabaseHas('order_items', [
|
|
'user_id' => auth()->id(),
|
|
'order_id' => null,
|
|
'product_id' => $product->id,
|
|
'qty' => 2,
|
|
]);
|
|
|
|
$cartItem = OrderItem::where('product_id', $product->id)->whereNull('order_id')->first();
|
|
|
|
// 2. Update cart item
|
|
patchJson(route('order.updateCartItem', $cartItem), [
|
|
'product_id' => $product->id,
|
|
'qty' => 5,
|
|
'price' => 10000,
|
|
'price_type' => PriceType::RETAIL->value,
|
|
])->assertRedirect();
|
|
|
|
expect($cartItem->fresh()->qty)->toBe(5);
|
|
|
|
// 3. Remove from cart
|
|
deleteJson(route('order.removeFromCart', $cartItem))->assertRedirect();
|
|
assertSoftDeleted('order_items', ['id' => $cartItem->id]);
|
|
});
|
|
|
|
it('can store a new order', function () {
|
|
$product = Product::factory()->create(['stock' => 10]);
|
|
|
|
// Update existing PURCHASE price from factory to ensure COGS calculation is predictable
|
|
$product->prices()->where('price_type', PriceType::PURCHASE->value)->update([
|
|
'price' => 8000,
|
|
]);
|
|
|
|
$data = [
|
|
'customer_name' => 'John Doe',
|
|
'subtotal' => 20000,
|
|
'discount' => 2000,
|
|
'payment' => 18000,
|
|
'payment_method' => PaymentMethod::CASH->value,
|
|
'order_status' => OrderStatus::PENDING->value,
|
|
'order_channel' => OrderChannel::STORE->value,
|
|
'items' => [
|
|
[
|
|
'product_id' => $product->id,
|
|
'qty' => 2,
|
|
'price' => 10000,
|
|
'total' => 20000,
|
|
'price_type' => PriceType::RETAIL->value,
|
|
],
|
|
],
|
|
];
|
|
|
|
postJson(route('order.store'), $data)
|
|
->assertRedirect(route('order.index'))
|
|
->assertSessionHas('success');
|
|
|
|
assertDatabaseHas('orders', [
|
|
'customer_name' => 'John Doe',
|
|
'subtotal' => 20000,
|
|
'discount' => 2000,
|
|
'total' => 18000,
|
|
'payment' => 18000,
|
|
'payment_method' => PaymentMethod::CASH->value,
|
|
'order_status' => OrderStatus::PENDING->value,
|
|
'order_channel' => OrderChannel::STORE->value,
|
|
'cogs' => 16000, // 8000 * 2
|
|
]);
|
|
|
|
// Check stock decrement
|
|
expect($product->fresh()->stock)->toBe(8);
|
|
|
|
// Check order items
|
|
assertDatabaseHas('order_items', [
|
|
'product_id' => $product->id,
|
|
'qty' => 2,
|
|
'price' => 10000,
|
|
]);
|
|
});
|
|
|
|
it('validates order creation', function () {
|
|
postJson(route('order.store'), [])
|
|
->assertStatus(422)
|
|
->assertJsonValidationErrors(['customer_name', 'items']);
|
|
});
|
|
|
|
it('can update an order', function () {
|
|
$product1 = Product::factory()->create(['stock' => 10]);
|
|
$product2 = Product::factory()->create(['stock' => 10]);
|
|
|
|
$order = Order::create([
|
|
'customer_name' => 'Old Customer',
|
|
'cogs' => 0,
|
|
'subtotal' => 10000,
|
|
'discount' => 0,
|
|
'payment' => 10000,
|
|
'total' => 10000,
|
|
'payment_method' => PaymentMethod::CASH->value,
|
|
'order_status' => OrderStatus::PENDING->value,
|
|
'order_channel' => OrderChannel::STORE->value,
|
|
]);
|
|
|
|
$order->items()->create([
|
|
'user_id' => auth()->id(),
|
|
'product_id' => $product1->id,
|
|
'qty' => 1,
|
|
'price' => 10000,
|
|
'total' => 10000,
|
|
'price_type' => PriceType::RETAIL->value,
|
|
]);
|
|
|
|
// Initial stock check
|
|
$product1->decrement('stock', 1);
|
|
|
|
$newData = [
|
|
'customer_name' => 'Updated Customer',
|
|
'subtotal' => 15000,
|
|
'discount' => 0,
|
|
'payment' => 15000,
|
|
'payment_method' => PaymentMethod::CASH->value,
|
|
'order_status' => OrderStatus::PROCESSING->value,
|
|
'order_channel' => OrderChannel::STORE->value,
|
|
'items' => [
|
|
[
|
|
'product_id' => $product2->id,
|
|
'qty' => 3,
|
|
'price' => 5000,
|
|
'total' => 15000,
|
|
'price_type' => PriceType::RETAIL->value,
|
|
],
|
|
],
|
|
];
|
|
|
|
patchJson(route('order.update', $order), $newData)
|
|
->assertRedirect(route('order.index'))
|
|
->assertSessionHas('success');
|
|
|
|
// Check data updated
|
|
assertDatabaseHas('orders', [
|
|
'id' => $order->id,
|
|
'customer_name' => 'Updated Customer',
|
|
'order_status' => OrderStatus::PROCESSING->value,
|
|
'payment' => 15000,
|
|
'total' => 15000,
|
|
]);
|
|
|
|
// Check stock: product1 should be restored (10), product2 should be decremented (7)
|
|
expect($product1->fresh()->stock)->toBe(10);
|
|
expect($product2->fresh()->stock)->toBe(7);
|
|
});
|
|
|
|
it('can delete an order and restore stock', function () {
|
|
$product = Product::factory()->create(['stock' => 5]);
|
|
$order = Order::factory()->create();
|
|
$order->items()->create([
|
|
'user_id' => auth()->id(),
|
|
'product_id' => $product->id,
|
|
'qty' => 2,
|
|
'price' => 1000,
|
|
'total' => 2000,
|
|
'price_type' => PriceType::RETAIL->value,
|
|
]);
|
|
|
|
deleteJson(route('order.destroy', $order))
|
|
->assertRedirect()
|
|
->assertSessionHas('success');
|
|
|
|
assertSoftDeleted('orders', ['id' => $order->id]);
|
|
|
|
// Stock should be restored (5 + 2 = 7)
|
|
expect($product->fresh()->stock)->toBe(7);
|
|
});
|
|
|
|
it('can delete orders in bulk', function () {
|
|
$orders = Order::factory()->count(3)->create();
|
|
$ids = $orders->pluck('id')->toArray();
|
|
|
|
deleteJson(route('order.bulkDestroy'), ['ids' => $ids])
|
|
->assertRedirect()
|
|
->assertSessionHas('success');
|
|
|
|
foreach ($ids as $id) {
|
|
assertSoftDeleted('orders', ['id' => $id]);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Order Module - Unauthorized Actions', function () {
|
|
beforeEach(function () {
|
|
actingAs(createUnauthorizedUser());
|
|
});
|
|
|
|
it('cannot store an order without permission', function () {
|
|
postJson(route('order.store'), ['customer_name' => 'Unauthorized'])
|
|
->assertStatus(403);
|
|
});
|
|
|
|
it('cannot update an order without permission', function () {
|
|
$order = Order::factory()->create();
|
|
patchJson(route('order.update', $order), ['customer_name' => 'Unauthorized'])
|
|
->assertStatus(403);
|
|
});
|
|
|
|
it('cannot delete an order without permission', function () {
|
|
$order = Order::factory()->create();
|
|
deleteJson(route('order.destroy', $order))
|
|
->assertStatus(403);
|
|
});
|
|
});
|