From 8d380e49025a04ad676c2c38282fd2a9abc42304 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Fri, 29 May 2026 12:17:36 +0800 Subject: [PATCH] =?UTF-8?q?[FEAT]=20=E5=A2=9E=E5=8A=A0=E8=A8=82=E5=96=AE?= =?UTF-8?q?=E5=87=BA=E8=B2=A8=E4=BA=A4=E4=BB=98=E7=8B=80=E6=85=8B(delivery?= =?UTF-8?q?=5Fstatus)=E8=88=87=E5=A4=9A=E8=AA=9E=E7=B3=BB=E6=94=AF?= =?UTF-8?q?=E6=8F=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增資料庫遷移:在 資料表增加 (出貨狀態:0 失敗、1 成功、2 部分成功),預設值為 1 並允許 NULL 以完美向下相容舊機台。 2. 升級 Model:加入 到可填充與轉型,並宣告常量與 取得本地化文字的 Accessor。 3. 更新 邏輯:在 中自動解析並記錄 。新增 輔助函式,當 Payload 未指定訂單狀態時,可動態從個別貨道出貨紀錄 ( 陣列) 計算出整筆訂單的出貨交付結果。 4. 銷售紀錄列表 (tab-orders.blade.php) UI 優化:將原先「狀態」欄位重命名為「付款狀態」,並在右側新增「出貨狀態」欄位,使用極簡奢華風的彩色 Badge 呈現出貨結果;桌機版空狀態 修正為 8 防止破版;手機版卡片同步支援。 5. 側邊明細面板 (order-detail-panel.blade.php) UI 優化:將基本狀態卡片升級為 3 欄網格,將付款狀態、支付類型與新增的出貨狀態 Badge 完美並排。 6. 報表匯出功能 (SalesController.php) 整合:在 Excel 及 CSV 匯出中同步修正「付款狀態」欄位標題與轉換標籤,並新增「出貨狀態」欄位以匯出 。 7. 多語系精準對齊:新增 , , , 及 的繁、英、日翻譯對照,並進行字母升冪排序 (ksort)。 --- .../Controllers/Admin/SalesController.php | 16 +++++++- app/Models/Transaction/Order.php | 27 ++++++++++++ .../Transaction/TransactionService.php | 33 +++++++++++++++ ...58_add_delivery_status_to_orders_table.php | 28 +++++++++++++ lang/en.json | 2 + lang/ja.json | 2 + lang/zh_TW.json | 2 + .../partials/order-detail-panel.blade.php | 18 +++++++- .../admin/sales/partials/tab-orders.blade.php | 41 ++++++++++++++++--- 9 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 database/migrations/2026_05_29_114858_add_delivery_status_to_orders_table.php diff --git a/app/Http/Controllers/Admin/SalesController.php b/app/Http/Controllers/Admin/SalesController.php index 1454fad..bd59d09 100644 --- a/app/Http/Controllers/Admin/SalesController.php +++ b/app/Http/Controllers/Admin/SalesController.php @@ -882,7 +882,8 @@ class SalesController extends Controller __('Discount Amount'), __('Payment Amount'), __('Payment Type'), - __('Status'), + __('Payment Status'), + __('Dispense Status'), __('Invoice Number'), __('Staff Name'), __('Staff ID'), @@ -922,6 +923,16 @@ class SalesController extends Controller $totalQty = $order->items->sum('quantity'); + // 轉換訂單狀態標籤,與前端 tab-orders.blade.php / detail 面板一致 + $statusLabelMap = [ + 'pending' => __('Pending'), + 'paid' => __('Paid'), + 'completed' => __('Completed'), + 'cancelled' => __('Cancelled'), + 'refunded' => __('Refunded'), + ]; + $paymentStatusLabel = $statusLabelMap[$order->status] ?? $order->status; + $row = [ $order->order_no, $order->created_at->format('Y-m-d H:i:s'), @@ -933,7 +944,8 @@ class SalesController extends Controller number_format($order->discount_amount, 2, '.', ''), number_format($order->pay_amount, 2, '.', ''), $order->payment_type_label, - __($order->status), + $paymentStatusLabel, + $order->delivery_status_label, $order->invoice->invoice_no ?? '---', $order->staffCardLog->staffCard->name ?? '---', $order->staffCardLog->staffCard->employee_id ?? '---', diff --git a/app/Models/Transaction/Order.php b/app/Models/Transaction/Order.php index 6a52fb6..23d87fb 100644 --- a/app/Models/Transaction/Order.php +++ b/app/Models/Transaction/Order.php @@ -17,6 +17,11 @@ class Order extends Model public const PAYMENT_STATUS_FAILED = 0; public const PAYMENT_STATUS_SUCCESS = 1; + // 出貨狀態 + public const DELIVERY_STATUS_FAILED = 0; + public const DELIVERY_STATUS_SUCCESS = 1; + public const DELIVERY_STATUS_PARTIAL = 2; + protected $fillable = [ 'company_id', 'flow_id', @@ -41,6 +46,7 @@ class Order extends Model 'machine_time', 'status', 'metadata', + 'delivery_status', ]; protected $casts = [ @@ -53,6 +59,7 @@ class Order extends Model 'machine_time' => 'datetime', 'metadata' => 'array', 'payment_type' => 'integer', + 'delivery_status' => 'integer', ]; /** @@ -148,4 +155,24 @@ class Order extends Model { return $this->belongsTo(WelcomeGift::class); } + + /** + * 取得所有出貨狀態的對照標籤 + */ + public static function getDeliveryStatusLabels(): array + { + return [ + self::DELIVERY_STATUS_FAILED => __('Dispense Failed'), + self::DELIVERY_STATUS_SUCCESS => __('Dispense Success'), + self::DELIVERY_STATUS_PARTIAL => __('Partial Dispense Success'), + ]; + } + + /** + * 取得目前出貨狀態標籤 (Accessor) + */ + public function getDeliveryStatusLabelAttribute(): string + { + return self::getDeliveryStatusLabels()[$this->delivery_status] ?? __('Unknown'); + } } diff --git a/app/Services/Transaction/TransactionService.php b/app/Services/Transaction/TransactionService.php index 53dbc14..637fd57 100644 --- a/app/Services/Transaction/TransactionService.php +++ b/app/Services/Transaction/TransactionService.php @@ -71,6 +71,7 @@ class TransactionService 'payment_at' => now(), 'status' => 'completed', 'metadata' => $data['metadata'] ?? null, + 'delivery_status' => (int) ($data['delivery_status'] ?? Order::DELIVERY_STATUS_SUCCESS), ]); // Create Order Items @@ -250,6 +251,9 @@ class TransactionService // 確保提取 payment_type 與 flow_id (可能在 root 或 order 內) $orderData['flow_id'] = $flowId; + if (!isset($orderData['delivery_status']) && !empty($data['dispense'])) { + $orderData['delivery_status'] = $this->resolveDeliveryStatusFromDispense($data['dispense']); + } if (!isset($orderData['payment_type']) && isset($data['payment_type'])) { $orderData['payment_type'] = $data['payment_type']; } @@ -385,4 +389,33 @@ class TransactionService return $order; }); } + + /** + * Resolve order-level delivery status from item-level dispense results. + */ + protected function resolveDeliveryStatusFromDispense(array $dispense): int + { + $dispenseList = isset($dispense[0]) ? $dispense : [$dispense]; + $statuses = collect($dispenseList) + ->filter(fn ($item) => is_array($item) && array_key_exists('dispense_status', $item)) + ->map(fn ($item) => (int) $item['dispense_status']); + + if ($statuses->isEmpty()) { + return Order::DELIVERY_STATUS_SUCCESS; + } + + $successCount = $statuses + ->filter(fn ($status) => $status === Order::DELIVERY_STATUS_SUCCESS) + ->count(); + + if ($successCount === $statuses->count()) { + return Order::DELIVERY_STATUS_SUCCESS; + } + + if ($successCount === 0) { + return Order::DELIVERY_STATUS_FAILED; + } + + return Order::DELIVERY_STATUS_PARTIAL; + } } diff --git a/database/migrations/2026_05_29_114858_add_delivery_status_to_orders_table.php b/database/migrations/2026_05_29_114858_add_delivery_status_to_orders_table.php new file mode 100644 index 0000000..cc3854d --- /dev/null +++ b/database/migrations/2026_05_29_114858_add_delivery_status_to_orders_table.php @@ -0,0 +1,28 @@ +tinyInteger('delivery_status')->default(1)->comment('出貨狀態: 0:出貨失敗, 1:出貨成功, 2:部分出貨成功')->after('payment_status'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('orders', function (Blueprint $table) { + $table->dropColumn('delivery_status'); + }); + } +}; diff --git a/lang/en.json b/lang/en.json index b96e9d8..e76b31f 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1341,6 +1341,7 @@ "Page 7": "Page 7", "Page Lock Status": "Page Lock Status", "Parameters": "Parameters", + "Partial Dispense Success": "Partial Dispense Success", "Pass Code": "Pass Code", "Pass Code (8 Digits)": "Pass Code (8 Digits)", "Pass Code Management": "Pass Code Management", @@ -1363,6 +1364,7 @@ "Payment Configuration deleted successfully.": "Payment Configuration deleted successfully.", "Payment Configuration updated successfully.": "Payment Configuration updated successfully.", "Payment Selection": "Payment Selection", + "Payment Status": "Payment Status", "Payment Type": "Payment Type", "Pending": "Pending", "Per Page": "Per Page", diff --git a/lang/ja.json b/lang/ja.json index 9a77c32..0d8a840 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -1341,6 +1341,7 @@ "Page 7": "ページ 7 (ロックページ)", "Page Lock Status": "ページロックステータス", "Parameters": "パラメータ設定", + "Partial Dispense Success": "一部出庫成功", "Pass Code": "パスコード", "Pass Code (8 Digits)": "パスコード (8桁)", "Pass Code Management": "パスコード管理", @@ -1363,6 +1364,7 @@ "Payment Configuration deleted successfully.": "決済設定が正常に削除されました。", "Payment Configuration updated successfully.": "決済設定が正常に更新されました。", "Payment Selection": "支払選択", + "Payment Status": "決済ステータス", "Payment Type": "支払方法", "Pending": "保留中", "Per Page": "1ページあたり表示", diff --git a/lang/zh_TW.json b/lang/zh_TW.json index aa45c32..ad7dbc9 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -1341,6 +1341,7 @@ "Page 7": "鎖定頁", "Page Lock Status": "頁面鎖定狀態", "Parameters": "參數設定", + "Partial Dispense Success": "部分出貨成功", "Pass Code": "通行碼", "Pass Code (8 Digits)": "通行碼 (8 位數)", "Pass Code Management": "通行碼管理", @@ -1363,6 +1364,7 @@ "Payment Configuration deleted successfully.": "金流設定已成功刪除。", "Payment Configuration updated successfully.": "金流設定已成功更新。", "Payment Selection": "付款選擇", + "Payment Status": "付款狀態", "Payment Type": "支付方式", "Pending": "待處理", "Per Page": "每頁顯示", diff --git a/resources/views/admin/sales/partials/order-detail-panel.blade.php b/resources/views/admin/sales/partials/order-detail-panel.blade.php index b6a7776..a201394 100644 --- a/resources/views/admin/sales/partials/order-detail-panel.blade.php +++ b/resources/views/admin/sales/partials/order-detail-panel.blade.php @@ -25,9 +25,9 @@
{{-- 基本狀態卡片 --}} -
+
-

{{ __('Order Status') }}

+

{{ __('Payment Status') }}

@php $statusMap = [ @@ -56,6 +56,20 @@ {{ $order->payment_type_label }}

+
+

{{ __('Dispense Status') }}

+
+ @php + $deliveryStatusMap = [ + 0 => ['label' => __('Dispense Failed'), 'color' => 'rose'], + 1 => ['label' => __('Dispense Success'), 'color' => 'emerald'], + 2 => ['label' => __('Partial Dispense Success'), 'color' => 'amber'], + ]; + $ds = $deliveryStatusMap[$order->delivery_status] ?? ['label' => __('Unknown'), 'color' => 'slate']; + @endphp + +
+
{{-- 交易資訊 --}} diff --git a/resources/views/admin/sales/partials/tab-orders.blade.php b/resources/views/admin/sales/partials/tab-orders.blade.php index 22b6152..0463c11 100644 --- a/resources/views/admin/sales/partials/tab-orders.blade.php +++ b/resources/views/admin/sales/partials/tab-orders.blade.php @@ -169,7 +169,11 @@ - {{ __('Status') }} + {{ __('Payment Status') }} + + + {{ __('Dispense Status') }} @@ -263,13 +267,25 @@ 'refunded' => ['label' => __('Refunded'), 'color' => 'rose'], ]; $s = $statusMap[$order->status] ?? ['label' => $order->status, 'color' => 'slate']; - - $invoiceSearchValue = $order->invoice ? $order->invoice->invoice_no : $order->order_no; - $targetUrl = route('admin.sales.index', ['tab' => 'invoices', 'search' => $invoiceSearchValue]); @endphp + @php + $deliveryStatusMap = [ + 0 => ['label' => __('Dispense Failed'), 'color' => 'rose'], + 1 => ['label' => __('Dispense Success'), 'color' => 'emerald'], + 2 => ['label' => __('Partial Dispense Success'), 'color' => 'amber'], + ]; + $ds = $deliveryStatusMap[$order->delivery_status] ?? ['label' => __('Unknown'), 'color' => 'slate']; + @endphp + + + + @php + $invoiceSearchValue = $order->invoice ? $order->invoice->invoice_no : $order->order_no; + $targetUrl = route('admin.sales.index', ['tab' => 'invoices', 'search' => $invoiceSearchValue]); + @endphp @if($order->invoice)
@empty - - + + @endforelse @@ -413,6 +429,19 @@ {{ $order->invoice->invoice_no ?? '---' }}

+
+

{{ + __('Dispense Status') }}

+ @php + $deliveryStatusMap = [ + 0 => ['label' => __('Dispense Failed'), 'color' => 'rose'], + 1 => ['label' => __('Dispense Success'), 'color' => 'emerald'], + 2 => ['label' => __('Partial Dispense Success'), 'color' => 'amber'], + ]; + $ds = $deliveryStatusMap[$order->delivery_status] ?? ['label' => __('Unknown'), 'color' => 'slate']; + @endphp + +
{{-- Action Buttons --}}