star-cloud/PHARMACY_PICKUP_DESIGN.md
terrylee 8977e7eb0f feat(pharmacy-pickup): 領藥單模組(後台建單→QR→機台B660掃碼→出貨回報)
- 機台系統設定:取物單模式(pickup_sheet)下新增「領藥單」開關 pharmacy_pickup_enabled,B014 下發 FunctionSet.PharmacyPickup
- 權限:銷售管理→領藥單 menu.sales.pharmacy-pickup(角色/帳號可勾選授權,租戶模板預設關)
- 資料模型:orders 加 order_type/pricing_slip_no/created_by(領藥單序號=flow_id)、pickup_codes.slot_no 可空
- 後台:PharmacyPickupController + PharmacyPickupService(以商品為單位建單、庫存不預扣)+ 建單/列表/列印頁(QR內嵌,不顯姓名)
- B660:領藥單回多貨道 items[](保留 slot_no 向後相容)+ 模式 gate + 標記已領
- 出貨回報:finalizePharmacyDispense(獨立於銷售/發票/閉環)+ flow_id 終態冪等 + 庫存只扣一次
- 修正 PickupCode::isValid() 對 null expires_at 的 NPE、status 補入 fillable

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:54:04 +08:00

62 lines
5.9 KiB
Markdown
Raw Permalink 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.

# 領藥單 / 批價單取藥模組 — 設計文件v2已納入交叉檢查修正
分支:`feat/pharmacy-pickup`(自 `dev` 切出)。
`star-cloud` 是自動販賣機智能機雲端後台Laravel 12 + Sail + MySQL + Redis + EMQX + Go mqtt-gateway
本模組為**可授權功能**,只開放給部分客戶/機台:藥師依病人「批價單」人工建立「領藥單」→ 系統產序號 + 一張 QR → 病人到智能機掃碼 → 雲端驗證回多貨道 → 一次出全部 → 出貨結果回傳後台。不串 HIS、不存病人姓名病歷比照中國醫取物單僅存編號
---
## 一、目標流程
1. 病人拿批價單到櫃台。
2. 藥師在後台**人工**勾選該機台貨道上的藥品 + 數量,建立「領藥單」;可選填**批價單號**當依據。
3. 領藥單序號系統自動產生(`RX` + 日期 + 流水,如 `RX26061800001`),並**同時作為 `orders.flow_id`**(見修正②)。
4. 系統產生**一張 QR 領藥單**,列印給病人。
5. 病人到智能機掃 QR → 機台呼叫 **B660** → 雲端確認該機台為 `pickup_sheet` 模式且 `pharmacy_pickup_enabled` 開啟 → 回傳該單**所有貨道清單** → 機台一次出全部。
6. 出貨結果經 MQTT `transaction/finalize`(帶 `dispense[]`)回傳 → 彙整更新領藥單出貨狀態。
---
## 二、模組授權機制(定案:取物單模式下的開關)
領藥單**不是**獨立的 shopping_mode而是既有「取物單(pickup_sheet)」模式**底下的一個開關** `pharmacy_pickup_enabled`
- `machines.settings.shopping_mode` 維持三種:`basic` / `employee_card` / `pickup_sheet`(取物單,中國醫物料編號領藥)。
- 在「機台系統設定 → 購物方式」選 `pickup_sheet` 時,下方出現「**領藥單**」開關 `settings.pharmacy_pickup_enabled`
- 授權 = 機台為 `pickup_sheet` 模式且 `pharmacy_pickup_enabled=true`;後台「領藥單」選單僅在「公司有任一機台符合此條件」時顯示。
- B014 `getSettings``FunctionSet` 新增 `PharmacyPickup` 旗標下發給機台;`ShoppingMode` 仍為 pickup_sheet。
- 控制器強制:非 pickup_sheet 模式時 `pharmacy_pickup_enabled` 一律存 false。
- (後續可選)公司層級 license gate由系統管理員授權哪些公司可開此開關。
### ✅ Phase 1 已完成(本次)
- `machines/index.blade.php`選「取物單」時下方顯示「領藥單」開關checkbox `settings[pharmacy_pickup_enabled]`+ Alpine 資料。
- `partials/tab-system-settings.blade.php`:取物單模式且開關開時,摘要加顯示 `領藥單`
- `MachineSettingController.php``shopping_mode` 白名單(basic/employee_card/pickup_sheet);新增持久化 `pharmacy_pickup_enabled`(僅 pickup_sheet 模式可為 true
- `MachineController.php`B014`FunctionSet.PharmacyPickup` 下發。
- `lang/{en,zh_TW,ja}.json`:新增 `Pharmacy Pickup (Rx)`zh_TW=「領藥單」)+ 開關說明。
- 驗證2 controller PHP lint 通過、view:cache 成功、3 JSON 合法、登入頁 200。已部署測試機 192.168.0.130。
---
## 三、交叉檢查修正(必須遵守)
- **修正①(旗標)**:領藥單做成「取物單(pickup_sheet)模式下的開關 `pharmacy_pickup_enabled`」,並完整接上 B014 `FunctionSet.PharmacyPickup` 下發(非孤立旗標)。✅ 已採用。
- **修正②(冪等/扣庫存)**:領藥單必須有 `flow_id`(用領藥單序號),讓出貨上報套用既有 `finalizeTransaction` 終態冪等保護;**庫存只在 finalize 扣一次**,建單不預扣(避免雙重扣減 / 重送 MQTT 重複扣)。
- **修正③B660 相容)**:保留回傳 `slot_no`(領藥單填第一筆或 null**additive 新增 `items:[{slot_no,product_name,qty}]`**;用 `order_type` 分流;靠 `shopping_mode=pharmacy_pickup` gate 只放行新版機台。
- **修正④(報表/發票隔離)**`orders` 加 `order_type``SalesController` 銷售分頁與 CSV 匯出預設 `where order_type='sale'`,領藥單獨立頁;確認領藥 finalize 上報**不帶 invoice 區塊**(不會誤開發票)。
- **修正⑤(既有脆弱點)**:先修 `PickupCode::isValid()` 對 null `expires_at` 的 NPE`$this->expires_at?->isFuture() ?? true`);把 `status` 補進 PickupCode `$fillable`;確認 `order_items` 實際欄位(`sku` vs `barcode`)。
- **修正⑥(建單必填)**`orders.payment_status` / `payment_type` 為 NOT NULL建單須明確給值領藥單給 0 / 特定碼);多貨道「一次出全部」需與機台端對齊(既有 B055/dispatchDispense 為單貨道)。
---
## 四、資料模型變更
- `orders` 新增:`order_type ENUM('sale','pharmacy_pickup') default 'sale'`、`pricing_slip_no VARCHAR NULL`、`created_by BIGINT NULL`。序號存 `order_no` 且**等於 `flow_id`**。
- `pickup_codes.slot_no` 改 nullable領藥單多貨道由 order_items×machine_slots 解析;一般取貨碼仍走 slot_no以 order_type 區分讀取)。
- 一張領藥單 = 1 Order(`pharmacy_pickup`) + N OrderItem + 1 PickupCode付款欄位給定值`delivery_status` 初始 0。
---
## 五、後續任務(修正後)
- **Phase 2 資料模型**:上述 migrationOrder 加常數/scope先修正⑤的 PickupCode 脆弱點。
- **Phase 3 後台建單 + 列印**`PharmacyPickupService::create()`(產序號=flow_id、庫存檢查、交易內建單、`Admin/PharmacyPickupController`、勾選頁 + 列印頁QR、不顯示姓名。建單僅限 `pharmacy_pickup` 模式機台。
- **Phase 4 B660**additive items[] + order_type 分流 + 模式 gate + 標記碼已領。
- **Phase 5 回報**finalize 帶 dispense[] 一次彙整(復用 `resolveDeliveryStatusFromDispense`),靠 flow_id 終態冪等;庫存只扣一次;領藥單詳情顯示逐道結果。
- **Phase 6 Android另 repo**`shopping_mode=pharmacy_pickup` 啟用領藥 SaleFlow沿用中國醫 TakeMedicineDialog/DispatchDialog資料來源換 B660