# 領藥單 / 批價單取藥模組 — 設計文件(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 資料模型**:上述 migration;Order 加常數/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)。