[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

@@ -87,19 +87,7 @@ class SyncOrderAction
// --- 執行寫入交易 ---
$result = DB::transaction(function () use ($data, $items, $resolvedProducts) {
// 1. 建立訂單
$order = SalesOrder::create([
'external_order_id' => $data['external_order_id'],
'status' => 'completed',
'payment_method' => $data['payment_method'] ?? 'cash',
'total_amount' => 0,
'sold_at' => $data['sold_at'] ?? now(),
'raw_payload' => $data,
'source' => $data['source'] ?? 'pos',
'source_label' => $data['source_label'] ?? null,
]);
// 2. 查找倉庫
// 1. 查找倉庫(提前至建立訂單前,以便判定來源)
$warehouseCode = $data['warehouse_code'];
$warehouses = $this->inventoryService->getWarehousesByCodes([$warehouseCode]);
@@ -108,7 +96,25 @@ class SyncOrderAction
'warehouse_code' => ["Warehouse with code {$warehouseCode} not found."]
]);
}
$warehouseId = $warehouses->first()->id;
$warehouse = $warehouses->first();
$warehouseId = $warehouse->id;
// 2. 自動判定來源:若是販賣機倉庫則標記為 vending其餘為 pos
$source = ($warehouse->type === \App\Enums\WarehouseType::VENDING) ? 'vending' : 'pos';
// 3. 建立訂單
$order = SalesOrder::create([
'external_order_id' => $data['external_order_id'],
'name' => $data['name'],
'status' => 'completed',
'payment_method' => $data['payment_method'] ?? 'cash',
'total_amount' => $data['total_amount'],
'total_qty' => $data['total_qty'],
'sold_at' => $data['sold_at'] ?? now(),
'raw_payload' => $data,
'source' => $source,
'source_label' => $data['source_label'] ?? null,
]);
$totalAmount = 0;

View File

@@ -18,7 +18,10 @@ class SalesOrderController extends Controller
// 搜尋篩選 (外部訂單號)
if ($request->filled('search')) {
$query->where('external_order_id', 'like', '%' . $request->search . '%');
$query->where(function ($q) use ($request) {
$q->where('external_order_id', 'like', '%' . $request->search . '%')
->orWhere('name', 'like', '%' . $request->search . '%');
});
}
// 來源篩選
@@ -26,6 +29,11 @@ class SalesOrderController extends Controller
$query->where('source', $request->source);
}
// 付款方式篩選
if ($request->filled('payment_method')) {
$query->where('payment_method', $request->payment_method);
}
// 排序
$query->orderBy('sold_at', 'desc');
@@ -40,7 +48,7 @@ class SalesOrderController extends Controller
return Inertia::render('Integration/SalesOrders/Index', [
'orders' => $orders,
'filters' => $request->only(['search', 'per_page', 'source']),
'filters' => $request->only(['search', 'per_page', 'source', 'status', 'payment_method']),
]);
}

View File

@@ -11,9 +11,11 @@ class SalesOrder extends Model
protected $fillable = [
'external_order_id',
'name',
'status',
'payment_method',
'total_amount',
'total_qty',
'sold_at',
'raw_payload',
'source',
@@ -24,6 +26,7 @@ class SalesOrder extends Model
'sold_at' => 'datetime',
'raw_payload' => 'array',
'total_amount' => 'decimal:4',
'total_qty' => 'decimal:4',
];
public function items(): HasMany

View File

@@ -23,8 +23,11 @@ class SyncOrderRequest extends FormRequest
{
return [
'external_order_id' => 'required|string',
'name' => 'required|string|max:255',
'warehouse_code' => 'required|string',
'payment_method' => 'nullable|string|in:cash,credit_card,line_pay,ecpay,transfer,other',
'total_amount' => 'required|numeric|min:0',
'total_qty' => 'required|numeric|min:0',
'sold_at' => 'nullable|date',
'items' => 'required|array|min:1',
'items.*.product_id' => 'required|integer',

View File

@@ -190,10 +190,10 @@ class ProductService implements ProductServiceInterface
{
$product = null;
if (!empty($barcode)) {
$product = Product::query()->where('barcode', $barcode)->first();
$product = Product::where('barcode', $barcode)->first();
}
if (!$product && !empty($code)) {
$product = Product::query()->where('code', $code)->first();
$product = Product::where('code', $code)->first();
}
return $product;
}
@@ -207,7 +207,6 @@ class ProductService implements ProductServiceInterface
*/
public function searchProducts(array $filters, int $perPage = 50)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Product::query()
->with(['category', 'baseUnit'])
->where('is_active', true);
@@ -226,12 +225,16 @@ class ProductService implements ProductServiceInterface
$query->where('external_pos_id', $filters['external_pos_id']);
}
// 3. 分類過濾
// 3. 分類過濾 (優先使用 ID若傳入字串則按名稱)
if (!empty($filters['category'])) {
$categoryName = $filters['category'];
$query->whereHas('category', function ($q) use ($categoryName) {
$q->where('name', $categoryName);
});
$categoryVal = $filters['category'];
if (is_numeric($categoryVal)) {
$query->where('category_id', $categoryVal);
} else {
$query->whereHas('category', function ($q) use ($categoryVal) {
$q->where('name', $categoryVal);
});
}
}
// 4. 增量同步 (Updated After)