Files
star-erp/app/Modules/Integration/Actions/SyncVendingOrderAction.php
sky121113 2f30a78118
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Has been skipped
Koori-ERP-Deploy-System / deploy-production (push) Successful in 56s
feat(integration): 實作並測試 POS 與販賣機訂單同步 API
主要變更:
- 實作 POS 與販賣機訂單同步邏輯,支援多租戶與 Sanctum 驗證。
- 修正多租戶識別中間件與 Sanctum 驗證順序問題。
- 切換快取驅動至 Redis 以支援 Tenancy 標籤功能。
- 新增商品同步 API (Upsert) 及相關單元測試。
- 新增手動測試腳本 tests/manual/test_integration_api.sh。
- 前端新增銷售訂單來源篩選與欄位顯示。
2026-02-23 13:27:12 +08:00

153 lines
5.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Modules\Integration\Actions;
use App\Modules\Integration\Models\SalesOrder;
use App\Modules\Integration\Models\SalesOrderItem;
use App\Modules\Inventory\Contracts\InventoryServiceInterface;
use App\Modules\Inventory\Contracts\ProductServiceInterface;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\ValidationException;
class SyncVendingOrderAction
{
protected $inventoryService;
protected $productService;
public function __construct(
InventoryServiceInterface $inventoryService,
ProductServiceInterface $productService
) {
$this->inventoryService = $inventoryService;
$this->productService = $productService;
}
/**
* 執行販賣機訂單同步
*
* @param array $data
* @return array 包含訂單建立結果的資訊
* @throws ValidationException
* @throws \Exception
*/
public function execute(array $data)
{
$externalOrderId = $data['external_order_id'];
// 使用 Cache::lock 防護高併發
$lock = Cache::lock("sync_order_{$externalOrderId}", 10);
if (!$lock->get()) {
throw ValidationException::withMessages([
'external_order_id' => ["The order {$externalOrderId} is currently being processed by another transaction. Please try again later."]
]);
}
try {
// 冪等性處理:若訂單已存在,回傳已建立的訂單資訊
$existingOrder = SalesOrder::where('external_order_id', $externalOrderId)->first();
if ($existingOrder) {
return [
'status' => 'exists',
'message' => 'Order already exists',
'order_id' => $existingOrder->id,
];
}
// --- 預檢:以 ERP 商品代碼查詢 ---
$items = $data['items'];
$productCodes = array_column($items, 'product_code');
// 一次性查出所有相關的 Product以 code 查詢)
$products = $this->productService->findByCodes($productCodes)->keyBy('code');
$missingCodes = [];
foreach ($productCodes as $code) {
if (!$products->has($code)) {
$missingCodes[] = $code;
}
}
if (!empty($missingCodes)) {
throw ValidationException::withMessages([
'items' => ["The following products are not found by code: " . implode(', ', $missingCodes) . ". Please ensure these products exist in the system."]
]);
}
// --- 執行寫入交易 ---
$result = DB::transaction(function () use ($data, $items, $products) {
// 1. 建立訂單
$order = SalesOrder::create([
'external_order_id' => $data['external_order_id'],
'status' => 'completed',
'payment_method' => $data['payment_method'] ?? 'electronic',
'total_amount' => 0,
'sold_at' => $data['sold_at'] ?? now(),
'raw_payload' => $data,
'source' => 'vending',
'source_label' => $data['machine_id'] ?? null,
]);
// 2. 查找或建立倉庫
$warehouseId = $data['warehouse_id'] ?? null;
if (empty($warehouseId)) {
$warehouseName = $data['warehouse'] ?? '販賣機倉庫';
$warehouse = $this->inventoryService->findOrCreateWarehouseByName($warehouseName);
$warehouseId = $warehouse->id;
}
$totalAmount = 0;
// 3. 處理訂單明細
$orderItemsData = [];
foreach ($items as $itemData) {
$product = $products->get($itemData['product_code']);
$qty = $itemData['qty'];
$price = $itemData['price'];
$lineTotal = $qty * $price;
$totalAmount += $lineTotal;
$orderItemsData[] = [
'sales_order_id' => $order->id,
'product_id' => $product->id,
'product_name' => $product->name,
'quantity' => $qty,
'price' => $price,
'total' => $lineTotal,
'created_at' => now(),
'updated_at' => now(),
];
// 4. 扣除庫存(強制模式,允許負庫存)
$this->inventoryService->decreaseStock(
$product->id,
$warehouseId,
$qty,
"Vending Order: " . $order->external_order_id,
true
);
}
// Batch insert order items
SalesOrderItem::insert($orderItemsData);
$order->update(['total_amount' => $totalAmount]);
return [
'status' => 'created',
'message' => 'Vending order synced and stock deducted successfully',
'order_id' => $order->id,
];
});
return $result;
} finally {
$lock->release();
}
}
}