[RELEASE] 晉升交易交付狀態與收件人姓名遮罩功能至正式環境
1. 增加訂單出貨交付狀態(delivery_status)與多語系支援 2. 新增訂單收件人姓名遮罩與商品詞彙通用化調整
This commit is contained in:
commit
52da77a88f
@ -174,7 +174,7 @@ Mqtt3Client client = MqttClient.builder()
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **0416** | Microwave delivery door opening | info | 正在打開微波爐進貨口門 |
|
||||
| **0417** | Microwave delivery door open error | error | 微波爐進貨口門打開錯誤 |
|
||||
| **0418** | Pushing bento into microwave | info | 正在將便當推入微波爐 |
|
||||
| **0418** | Pushing product into microwave | info | 正在將商品推入微波爐 |
|
||||
| **0419** | Microwave delivery door closing | info | 正在關閉微波爐進貨口門 |
|
||||
| **0420** | Microwave delivery door close error | error | 微波爐進貨口門關閉錯誤 |
|
||||
|
||||
@ -182,9 +182,9 @@ Mqtt3Client client = MqttClient.builder()
|
||||
|
||||
| 代碼 | 英文 Key (i18n) | 級別 | 範例繁中翻譯 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **0421** | Bento not detected in microwave | error | 微波爐內沒檢測到便當 |
|
||||
| **0422** | Bento heating | info | 便當正在加熱 |
|
||||
| **0423** | Bento heating remaining time | info | 便當加熱剩餘時間秒 |
|
||||
| **0421** | Product not detected in microwave | error | 微波爐內未檢測到商品 |
|
||||
| **0422** | Product heating | info | 商品正在加熱 |
|
||||
| **0423** | Product heating remaining time | info | 商品加熱剩餘時間秒 |
|
||||
| **0424** | Purchase successful | info | ✅ 購買成功 |
|
||||
| **0431** | Microwave push rod push error | error | 微波爐內推桿推出錯誤 |
|
||||
| **0432** | Microwave push rod retract error | error | 微波爐內推桿收回錯誤 |
|
||||
@ -212,7 +212,7 @@ Mqtt3Client client = MqttClient.builder()
|
||||
| **0434** | Pickup door closed (cart mode) | info | 取貨口門關閉成功 (購物車) |
|
||||
| **0435** | Liquid dispense paused | warning | 暫停出液 (液體機) |
|
||||
| **0441** | Pickup door unlocked | info | 取貨口門開鎖上報 |
|
||||
| **04FF** | Bento purchase terminated | error | 便當購買終止 |
|
||||
| **04FF** | Purchase terminated | error | 購買終止 |
|
||||
|
||||
### 3.3 交易生命週期上報 (Transaction) - `machine/{serial_no}/transaction`
|
||||
|
||||
|
||||
@ -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 ?? '---',
|
||||
|
||||
@ -99,11 +99,12 @@ class MachineLog extends Model
|
||||
{
|
||||
$context = $this->context;
|
||||
|
||||
// 若 context 中已有翻譯標籤 (B013 封裝),則進行動態重組
|
||||
// B013 封裝:優先用目前代碼表重組,讓舊日誌也能吃到最新文案。
|
||||
if (isset($context['translated_label'])) {
|
||||
$label = __($context['translated_label']);
|
||||
$code = $context['raw_code'] ?? $context['error_code'] ?? '0000';
|
||||
$mapping = \App\Services\Machine\MachineService::ERROR_CODE_MAP[$code] ?? null;
|
||||
$label = __($mapping['label'] ?? $context['translated_label']);
|
||||
$tid = $context['tid'] ?? null;
|
||||
$code = $context['raw_code'] ?? '0000';
|
||||
|
||||
if ($tid) {
|
||||
return __('Slot') . " {$tid}: {$label} (Code: {$code})";
|
||||
|
||||
@ -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',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -114,6 +121,47 @@ class Order extends Model
|
||||
return self::getPaymentTypeLabels()[$this->payment_type] ?? __('Unknown');
|
||||
}
|
||||
|
||||
public function getMaskedPickupRecipientAttribute(): ?string
|
||||
{
|
||||
if (empty($this->member_barcode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::maskPickupRecipientName($this->member_barcode);
|
||||
}
|
||||
|
||||
public static function maskPickupRecipientName(string $value): string
|
||||
{
|
||||
$value = trim($value);
|
||||
|
||||
if ($value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (preg_match('/^([^\s((]+)(.*)$/us', $value, $matches) !== 1) {
|
||||
return self::maskName($value);
|
||||
}
|
||||
|
||||
return self::maskName($matches[1]) . ($matches[2] ?? '');
|
||||
}
|
||||
|
||||
private static function maskName(string $name): string
|
||||
{
|
||||
$length = mb_strlen($name);
|
||||
|
||||
if ($length <= 1) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if ($length === 2) {
|
||||
return mb_substr($name, 0, 1) . 'O';
|
||||
}
|
||||
|
||||
return mb_substr($name, 0, 1)
|
||||
. str_repeat('O', $length - 2)
|
||||
. mb_substr($name, -1);
|
||||
}
|
||||
|
||||
public function machine()
|
||||
{
|
||||
return $this->belongsTo(Machine::class);
|
||||
@ -148,4 +196,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');
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ class MachineService
|
||||
* B013: 硬體狀態代碼對照表 (Hardware Status Code Mapping)
|
||||
*
|
||||
* 代碼格式:Command 0x04 + Status Byte,例如 0x04+0x03 = '0403'
|
||||
* 出貨結果判定:僅 0402 (出貨成功) 與 0424 (便當取出成功) 視為成功,
|
||||
* 出貨結果判定:僅 0402 (出貨成功) 與 0424 (商品取出成功) 視為成功,
|
||||
* 其餘出貨結果碼均應觸發退款。
|
||||
*
|
||||
* 參考文件:廠商硬體通訊協議 Command(0x04) 狀態定義
|
||||
@ -57,15 +57,15 @@ class MachineService
|
||||
// --- 微波爐進貨口門 ---
|
||||
'0416' => ['label' => 'Microwave delivery door opening', 'level' => 'info'], // 正在打開微波爐進貨口門
|
||||
'0417' => ['label' => 'Microwave delivery door open error', 'level' => 'error'], // 微波爐進貨口門打開錯誤
|
||||
'0418' => ['label' => 'Pushing bento into microwave', 'level' => 'info'], // 正在將便當推入微波爐
|
||||
'0418' => ['label' => 'Pushing product into microwave', 'level' => 'info'], // 正在將商品推入微波爐
|
||||
'0419' => ['label' => 'Microwave delivery door closing', 'level' => 'info'], // 正在關閉微波爐進貨口門
|
||||
'0420' => ['label' => 'Microwave delivery door close error', 'level' => 'error'], // 微波爐進貨口門關閉錯誤
|
||||
|
||||
// --- 微波爐內部 ---
|
||||
'0421' => ['label' => 'Bento not detected in microwave', 'level' => 'error'], // 微波爐內沒檢測到便當
|
||||
'0422' => ['label' => 'Bento heating', 'level' => 'info'], // 便當正在加熱
|
||||
'0423' => ['label' => 'Bento heating remaining time', 'level' => 'info'], // 便當加熱剩餘時間 (貨道號=秒數)
|
||||
'0424' => ['label' => 'Purchase successful', 'level' => 'info'], // ✅ 購買成功 (原 Bento ready for pickup)
|
||||
'0421' => ['label' => 'Product not detected in microwave', 'level' => 'error'], // 微波爐內未檢測到商品
|
||||
'0422' => ['label' => 'Product heating', 'level' => 'info'], // 商品正在加熱
|
||||
'0423' => ['label' => 'Product heating remaining time', 'level' => 'info'], // 商品加熱剩餘時間 (貨道號=秒數)
|
||||
'0424' => ['label' => 'Purchase successful', 'level' => 'info'], // ✅ 購買成功
|
||||
'0431' => ['label' => 'Microwave push rod push error', 'level' => 'error'], // 微波爐內推桿推出錯誤
|
||||
'0432' => ['label' => 'Microwave push rod retract error', 'level' => 'error'], // 微波爐內推桿收回錯誤
|
||||
|
||||
@ -87,7 +87,7 @@ class MachineService
|
||||
'0435' => ['label' => 'Liquid dispense paused', 'level' => 'warning'], // 暫停出液
|
||||
|
||||
// --- 終止 ---
|
||||
'04FF' => ['label' => 'Bento purchase terminated', 'level' => 'error'], // 便當購買終止
|
||||
'04FF' => ['label' => 'Purchase terminated', 'level' => 'error'], // 購買終止
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// 貨道與馬達狀態類 (Prefix: 02 - SLOT_STATUS)
|
||||
@ -116,7 +116,7 @@ class MachineService
|
||||
*
|
||||
* 僅以下代碼視為出貨成功,其餘出貨結果碼均應觸發退款。
|
||||
* - 0402: 一般出貨成功
|
||||
* - 0424: 便當取出成功 (微波爐機型)
|
||||
* - 0424: 商品取出成功 (微波爐機型)
|
||||
*/
|
||||
public const DISPENSE_SUCCESS_CODES = ['0402', '0424'];
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('orders', function (Blueprint $table) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
};
|
||||
12
lang/en.json
12
lang/en.json
@ -219,10 +219,10 @@
|
||||
"Before Qty": "Before Qty",
|
||||
"Belongs To": "Belongs To",
|
||||
"Belongs To Company": "Belongs To Company",
|
||||
"Bento heating": "Bento heating",
|
||||
"Bento heating remaining time": "Bento heating remaining time",
|
||||
"Bento not detected in microwave": "Bento not detected in microwave",
|
||||
"Bento purchase terminated": "Bento purchase terminated",
|
||||
"Product heating": "Product heating",
|
||||
"Product heating remaining time": "Product heating remaining time",
|
||||
"Product not detected in microwave": "Product not detected in microwave",
|
||||
"Purchase terminated": "Purchase terminated",
|
||||
"Bill Acceptor": "Bill Acceptor",
|
||||
"Bracelet drop not detected": "Bracelet drop not detected",
|
||||
"Bracelet scan failed": "Bracelet scan failed",
|
||||
@ -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",
|
||||
@ -1488,7 +1490,7 @@
|
||||
"Purchasing": "Purchasing",
|
||||
"Purpose": "Purpose",
|
||||
"Push Status": "Push Status",
|
||||
"Pushing bento into microwave": "Pushing bento into microwave",
|
||||
"Pushing product into microwave": "Pushing product into microwave",
|
||||
"QR": "QR",
|
||||
"QR CODE": "QR CODE",
|
||||
"QR Code": "QR Code",
|
||||
|
||||
12
lang/ja.json
12
lang/ja.json
@ -219,10 +219,10 @@
|
||||
"Before Qty": "変動前数量",
|
||||
"Belongs To": "会社名",
|
||||
"Belongs To Company": "所属会社",
|
||||
"Bento heating": "お弁当加熱中",
|
||||
"Bento heating remaining time": "お弁当加熱残り時間",
|
||||
"Bento not detected in microwave": "電子レンジ内お弁当未検出",
|
||||
"Bento purchase terminated": "お弁当購入中止",
|
||||
"Product heating": "商品加熱中",
|
||||
"Product heating remaining time": "商品加熱残り時間",
|
||||
"Product not detected in microwave": "電子レンジ内商品未検出",
|
||||
"Purchase terminated": "購入中止",
|
||||
"Bill Acceptor": "紙幣識別機",
|
||||
"Bracelet drop not detected": "ブレスレット落下未検出",
|
||||
"Bracelet scan failed": "ブレスレットスキャン失敗",
|
||||
@ -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ページあたり表示",
|
||||
@ -1488,7 +1490,7 @@
|
||||
"Purchasing": "購入中",
|
||||
"Purpose": "用途",
|
||||
"Push Status": "アラート状態",
|
||||
"Pushing bento into microwave": "お弁当を電子レンジに投入中",
|
||||
"Pushing product into microwave": "商品を電子レンジに投入中",
|
||||
"QR": "QR",
|
||||
"QR CODE": "QRコード",
|
||||
"QR Code": "QRコード",
|
||||
|
||||
@ -219,10 +219,10 @@
|
||||
"Before Qty": "異動前數量",
|
||||
"Belongs To": "公司名稱",
|
||||
"Belongs To Company": "公司名稱",
|
||||
"Bento heating": "便當正在加熱",
|
||||
"Bento heating remaining time": "便當加熱剩餘時間",
|
||||
"Bento not detected in microwave": "微波爐內沒檢測到便當",
|
||||
"Bento purchase terminated": "便當購買終止",
|
||||
"Product heating": "商品正在加熱",
|
||||
"Product heating remaining time": "商品加熱剩餘時間",
|
||||
"Product not detected in microwave": "微波爐內未檢測到商品",
|
||||
"Purchase terminated": "購買終止",
|
||||
"Bill Acceptor": "紙鈔機",
|
||||
"Bracelet drop not detected": "沒檢測到手環掉入取貨斗",
|
||||
"Bracelet scan failed": "沒掃碼到手環",
|
||||
@ -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": "每頁顯示",
|
||||
@ -1488,7 +1490,7 @@
|
||||
"Purchasing": "購買中",
|
||||
"Purpose": "用途",
|
||||
"Push Status": "告警狀態",
|
||||
"Pushing bento into microwave": "正在將便當推入微波爐",
|
||||
"Pushing product into microwave": "正在將商品推入微波爐",
|
||||
"QR": "二維碼",
|
||||
"QR CODE": "二維碼",
|
||||
"QR Code": "QR Code",
|
||||
|
||||
@ -25,9 +25,9 @@
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-6 sm:p-8 space-y-10">
|
||||
{{-- 基本狀態卡片 --}}
|
||||
<div class="grid grid-cols-2 gap-6 p-6 bg-slate-50/50 dark:bg-slate-800/30 rounded-[2rem] border border-slate-100 dark:border-slate-800">
|
||||
<div class="grid grid-cols-3 gap-6 p-6 bg-slate-50/50 dark:bg-slate-800/30 rounded-[2rem] border border-slate-100 dark:border-slate-800">
|
||||
<div class="space-y-2">
|
||||
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Order Status') }}</p>
|
||||
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Payment Status') }}</p>
|
||||
<div>
|
||||
@php
|
||||
$statusMap = [
|
||||
@ -56,6 +56,20 @@
|
||||
{{ $order->payment_type_label }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Dispense Status') }}</p>
|
||||
<div>
|
||||
@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
|
||||
<x-status-badge :color="$ds['color']" :label="$ds['label']" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 交易資訊 --}}
|
||||
@ -123,7 +137,7 @@
|
||||
<div class="grid grid-cols-2 gap-8 p-8 bg-slate-50/50 dark:bg-slate-800/30 rounded-[2rem] border border-slate-100 dark:border-slate-800">
|
||||
<div class="space-y-1 col-span-2">
|
||||
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Pickup Recipient (Prescription Slip)') }}</p>
|
||||
<p class="text-sm font-extrabold text-slate-700 dark:text-slate-200">{{ $order->member_barcode }}</p>
|
||||
<p class="text-sm font-extrabold text-slate-700 dark:text-slate-200">{{ $order->masked_pickup_recipient }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -169,7 +169,11 @@
|
||||
</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
|
||||
{{ __('Status') }}
|
||||
{{ __('Payment Status') }}
|
||||
</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
|
||||
{{ __('Dispense Status') }}
|
||||
</th>
|
||||
<th
|
||||
class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">
|
||||
@ -248,7 +252,7 @@
|
||||
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 002.25-2.25V6.75A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25v10.5A2.25 2.25 0 004.5 19.5zm6-10.125a1.875 1.875 0 11-3.75 0 1.875 1.875 0 013.75 0zm1.294 6.336a6.721 6.721 0 01-3.17.789 6.721 6.721 0 01-3.168-.789 3.376 3.376 0 016.338 0z" />
|
||||
</svg>
|
||||
{{ $order->member_barcode }}
|
||||
{{ $order->masked_pickup_recipient }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@ -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
|
||||
<x-status-badge :color="$s['color']" :label="$s['label']" size="xs" />
|
||||
</td>
|
||||
<td class="px-6 py-6 whitespace-nowrap">
|
||||
@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
|
||||
<x-status-badge :color="$ds['color']" :label="$ds['label']" size="xs" />
|
||||
</td>
|
||||
<td class="px-6 py-6 whitespace-nowrap">
|
||||
@php
|
||||
$invoiceSearchValue = $order->invoice ? $order->invoice->invoice_no : $order->order_no;
|
||||
$targetUrl = route('admin.sales.index', ['tab' => 'invoices', 'search' => $invoiceSearchValue]);
|
||||
@endphp
|
||||
@if($order->invoice)
|
||||
<div @click.stop="switchTab('invoices', '{{ $targetUrl }}')"
|
||||
class="flex items-center gap-1.5 text-sm font-extrabold text-slate-600 dark:text-slate-400 cursor-pointer hover:text-cyan-500 transition-colors group/invoice"
|
||||
@ -302,8 +318,8 @@
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="py-20 text-center">
|
||||
<x-empty-state mode="table" colspan="7" :message="__('No transaction orders found')" />
|
||||
<td colspan="8" class="py-20 text-center">
|
||||
<x-empty-state mode="table" colspan="8" :message="__('No transaction orders found')" />
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
@ -393,7 +409,7 @@
|
||||
<svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 002.25-2.25V6.75A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25v10.5A2.25 2.25 0 004.5 19.5zm6-10.125a1.875 1.875 0 11-3.75 0 1.875 1.875 0 013.75 0zm1.294 6.336a6.721 6.721 0 01-3.17.789 6.721 6.721 0 01-3.168-.789 3.376 3.376 0 016.338 0z" />
|
||||
</svg>
|
||||
{{ $order->member_barcode }}
|
||||
{{ $order->masked_pickup_recipient }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@ -413,6 +429,19 @@
|
||||
{{ $order->invoice->invoice_no ?? '---' }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest mb-1">{{
|
||||
__('Dispense Status') }}</p>
|
||||
@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
|
||||
<x-status-badge :color="$ds['color']" :label="$ds['label']" size="xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Action Buttons --}}
|
||||
|
||||
22
tests/Unit/OrderPickupRecipientMaskTest.php
Normal file
22
tests/Unit/OrderPickupRecipientMaskTest.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Transaction\Order;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class OrderPickupRecipientMaskTest extends TestCase
|
||||
{
|
||||
public function test_it_masks_pickup_recipient_names(): void
|
||||
{
|
||||
$this->assertSame('鄭O', Order::maskPickupRecipientName('鄭英'));
|
||||
$this->assertSame('鄭O英', Order::maskPickupRecipientName('鄭小英'));
|
||||
$this->assertSame('鄭OO英', Order::maskPickupRecipientName('鄭曉小英'));
|
||||
}
|
||||
|
||||
public function test_it_keeps_pickup_recipient_suffix(): void
|
||||
{
|
||||
$this->assertSame('黃O美 (2593)', Order::maskPickupRecipientName('黃麗美 (2593)'));
|
||||
$this->assertSame("黃O美\n(2593)", Order::maskPickupRecipientName("黃麗美\n(2593)"));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user