[FEAT] 銷售訂單管理:補齊欄位、即時搜尋、篩選與來源自動判定

This commit is contained in:
2026-03-19 15:00:33 +08:00
parent 0b4aeacb55
commit 60f5f00a9e
11 changed files with 269 additions and 63 deletions

View File

@@ -171,7 +171,11 @@ class PosApiTest extends TestCase
$payload = [
'external_order_id' => 'ORD-001',
'name' => '測試訂單一號',
'warehouse_code' => 'MAIN',
'payment_method' => 'cash',
'total_amount' => 500, // 5 * 100
'total_qty' => 5,
'sold_at' => now()->toIso8601String(),
'items' => [
[
@@ -252,7 +256,11 @@ class PosApiTest extends TestCase
// 1. Test deducting from SPECIFIC batch
$payloadA = [
'external_order_id' => 'ORD-BATCH-A',
'name' => '測試訂單A',
'warehouse_code' => 'MAIN',
'payment_method' => 'cash',
'total_amount' => 300, // 3 * 100
'total_qty' => 3,
'items' => [
[
'product_id' => $product->id,
@@ -270,7 +278,11 @@ class PosApiTest extends TestCase
// 2. Test deducting from NULL batch (default)
$payloadNull = [
'external_order_id' => 'ORD-BATCH-NULL',
'name' => '無批號測試',
'warehouse_code' => 'MAIN',
'payment_method' => 'cash',
'total_amount' => 200, // 2 * 100
'total_qty' => 2,
'items' => [
[
'product_id' => $product->id,
@@ -423,7 +435,8 @@ class PosApiTest extends TestCase
{
tenancy()->initialize($this->tenant);
$product = Product::where('code', 'P-001')->first();
$warehouse = \App\Modules\Inventory\Models\Warehouse::where('code', 'MAIN')->first();
// 確保倉庫存在
$warehouse = \App\Modules\Inventory\Models\Warehouse::firstOrCreate(['code' => 'MAIN'], ['name' => 'Main Warehouse']);
// 清空該商品的現有庫存以利測試負數
\App\Modules\Inventory\Models\Inventory::where('product_id', $product->id)->delete();
@@ -434,7 +447,11 @@ class PosApiTest extends TestCase
$payload = [
'external_order_id' => 'ORD-NEGATIVE-TEST',
'name' => '負數庫存測試',
'warehouse_code' => 'MAIN',
'payment_method' => 'cash',
'total_amount' => 1000,
'total_qty' => 10,
'items' => [
[
'product_id' => $product->id,
@@ -461,4 +478,60 @@ class PosApiTest extends TestCase
tenancy()->end();
}
public function test_order_source_automation_based_on_warehouse_type()
{
tenancy()->initialize($this->tenant);
$product = Product::where('code', 'P-001')->first();
// 1. Create a VENDING warehouse
$vendingWarehouse = \App\Modules\Inventory\Models\Warehouse::create([
'name' => 'Vending Machine 01',
'code' => 'VEND-01',
'type' => \App\Enums\WarehouseType::VENDING,
]);
// 2. Create a STANDARD warehouse
$standardWarehouse = \App\Modules\Inventory\Models\Warehouse::create([
'name' => 'General Warehouse',
'code' => 'ST-01',
'type' => \App\Enums\WarehouseType::STANDARD,
]);
tenancy()->end();
\Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']);
// Case A: Vending Warehouse -> Source should be 'vending'
$payloadVending = [
'external_order_id' => 'ORD-VEND',
'name' => 'Vending Order',
'warehouse_code' => 'VEND-01',
'total_amount' => 100,
'total_qty' => 1,
'items' => [['product_id' => $product->id, 'qty' => 1, 'price' => 100]]
];
$this->withHeaders(['X-Tenant-Domain' => $this->domain])
->postJson('/api/v1/integration/orders', $payloadVending)
->assertStatus(201);
// Case B: Standard Warehouse -> Source should be 'pos'
$payloadPos = [
'external_order_id' => 'ORD-POS',
'name' => 'POS Order',
'warehouse_code' => 'ST-01',
'total_amount' => 100,
'total_qty' => 1,
'items' => [['product_id' => $product->id, 'qty' => 1, 'price' => 100]]
];
$this->withHeaders(['X-Tenant-Domain' => $this->domain])
->postJson('/api/v1/integration/orders', $payloadPos)
->assertStatus(201);
tenancy()->initialize($this->tenant);
$this->assertDatabaseHas('sales_orders', ['external_order_id' => 'ORD-VEND', 'source' => 'vending']);
$this->assertDatabaseHas('sales_orders', ['external_order_id' => 'ORD-POS', 'source' => 'pos']);
tenancy()->end();
}
}