線稿索引所有 spec最後更新 2026-07-02
specs/equipment-inventory.md

裝備庫存彙總與警示

一句話:登山模組下「裝備品項 / 庫存 / 梯次裝備需求」的彙總與庫存警示資料模型;已落地(主檔 race-safe 庫存 + per-order 租借 + 跨梯次需求彙總),本檔描述現行 schema 與演算法。

🟩 登山模組 — 受 模組化 的「登山」開關 gate(裝備租借是登山模組專屬子集之一)。

裝備有兩條正交並存的線:per-order 租借(旅客實際借出,掛 order、走金流)與 per-departure 需求調查 + 跨梯次彙總(線控盤點哪梯次要借多少、會不會超收)。兩者共用同一份裝備主檔 equipment_items,庫存以主檔的 race-safe out_count 為唯一真相。

  • 關聯出團前準備(裝備需求調查 = departure-prep 的「裝備」軸;UI 真相在 apps/wireframes/app/admin/equipment)· 團控分房(住宿與裝備在 GS 同「住宿裝備資料連動」模式)· 金流模型(租金 / 押金經 order_charge_lines 接金流)

1. 問題

GS「裝備住宿-裝備」分頁把各行程的裝備需求以 睡墊*3 登山杖*2 字串記錄,跨日期窗用 SUMPRODUCT + REGEXEXTRACT 彙總各品項總需求,再比庫存上限:庫存 − 需求 < 0 → 超收(缺)剩餘 ≤ 2 → 提示剩餘 N(庫存警示),另算現場付現金額(品項 × 每日價目表)。GS 7 類裝備、各有租借上限與每日價目(見 §2)。

ERP 把這個模型還原為兩條線:旅客真借的 per-order 租借單(race-safe 扣庫存 + 金流),以及線控盤梯次需求的 per-departure 需求調查 + 跨梯次彙總警示。庫存真相落在主檔 equipment_itemsout_count(在租量),不再靠字串 parse。

2. 裝備主檔 equipment_items

全站級品項主檔(DB per tenant,無 tenant_id)。關鍵欄位(精確型別看 packages/core/src/schema/business.ts):

  • name / category / spec:品名、分類、規格。GS 7 類 seed(scripts/seed.ts seedEquipmentItems,以 name 為自然鍵 idempotent):

    品項分類庫存上限 total_count每日租金 rent_price_twd
    登山杖行進7150
    睡墊睡眠19100
    頭燈照明13100
    睡袋睡眠8200
    岩盔雪攀15200
    冰爪雪攀16200
    冰斧雪攀14300
  • rent_price_twd / deposit_twd:每日租金 / 押金(整數 TWD)。

  • 庫存三量total_count(庫存上限)、out_count(在租,race-safe 扣減的真相,不可手改)、maintenance_count(維修中)。可租 = total − out − maintenance(派生)。

  • DB CHECK out_count + maintenance_count <= total_count 為最後兜底;建檔 out_count 一律從 0 起(在租量只由租借單扣減)。

3. per-order 租借(equipment_rentals + equipment_rental_lines

旅客實際借出,掛 order、可選掛 departure。明細存成交租金 / 押金快照(改主檔不回寫單)。

  • race-safe 借出(同 seat-reservation / lodging-inventory 模式):每個 line 走條件式 UPDATE equipment_items SET out_count = out_count + N WHERE id = .. AND out_count + maintenance_count + N <= total_count;影響 0 列 → EquipmentStockError,整個 tx rollback。
  • 接金流order-money-model SSOT,比照 recalculateOrderRoomUpgrade):租金 append type='equipment_rental' charge line、押金 append type='equipment_deposit'(代收暫收)charge line,並為新增應收開 adjustment payment_schedule + intent,鎖 order 重算 payment_state / refund_state —— 全在 createRental 同一 tx。
  • 歸還驗收:回補 out_count、設 return_state='returned';有破損賠償 → recon_state='deposit_pending'。歸還動 charge line、不自建出款 / 退款;押金退款一律走既有 refunds flow(會計事後手動發起,沖正 charge line + 出款);破損保留不退的部分 = 該 equipment_deposit charge 留著轉收入。
  • 逾期掃描markOverdueRentals(per-tenant cron)把過租期末日且仍 rented 的單轉 overdue(不動庫存)。

4. per-departure 需求調查 + 跨梯次彙總

線控在某梯次盤點要借多少(未必等於旅客已真借),並看同日期窗其他梯次會不會把庫存吃光。

  • departure_equipment_needs:每梯次每品項一筆需求(UNIQUE departure_id + equipment_item_id,再加同品項 = 覆寫數量非累加;quantity 正整數)。repo 在 packages/core/src/departure-prep/repo.tslistEquipmentNeeds / upsertEquipmentNeed / deleteEquipmentNeed)。
  • 跨梯次彙總警示 summarizeEquipmentDemand({ start, endExclusive }):把日期窗 [start, endExclusive) 內(依梯次 departure_date 落窗)各梯次對每個 item 的需求加總,對比主檔可租庫存:
    • totalDemand = Σ quantity(窗內各梯次該 item 需求)
    • availableCount = total − out − maintenance(主檔可租)
    • surplus = availableCount − totalDemand
    • shortage = surplus < 0(需求 > 庫存 → 缺,還原 GS「超收」)
    • lowStock(剩餘 ≤ 2)surplus >= 0 && surplus <= 2,還原 GS「剩餘 ≤2 才提示」的低水位警示(Alert 黃燈)。shortage(紅)與 lowStock(黃)互斥。
    • 排序:缺口大 → 小。
  • 日期窗由呼叫端決定(例:本梯次 departure_date ± N 天,或某月)。用途:某梯次調查需求時提醒同窗其他梯次也借同款導致超收 / 低水位。

5. 現場付現估算與計價規則

GS 有「現場付」偵測(品項 × 每日價目表)。GS 價目表以「晚數」計,不是含頭尾天數:1 日與 2 日同價(= base)、3 日 ×2、4 日 ×3,即

租金 = rent_price_twd × max(1, 含頭尾天數 − 1) × 數量

(玉山 2/27–28 = 2 天 1 夜 → ×1 = base;雪季 3 天 2 夜 → ×2。原始 GS「裝備住宿-裝備」價目表佐證見 荒野旅人ERP溝通範本.xlsx。)

已落地(估算側):per-departure 裝備需求調查(出團前準備「裝備」tab)顯示每列 + 合計的現場付現估算 = 需求量 × max(1, 含頭尾天數 − 1) × rent_price_twd,其中含頭尾天數 = 梯次 departure_date → return_datelistEquipmentNeedsrentPriceTwd、日數於 page 由梯次日期算、估算在 UI 派生(不落 DB)。此為線控盤點用的現場付現參考。

per-order 租借側計價(決策:按天數計費,2026-07-01):旅客實際租金一律套本節 GS 規則 rent_price_twd × max(1, 含頭尾天數 − 1) × 數量,與估算側同公式。rent_price_twd_snapshot 語意 = 快照「成交每日單價 × 計價日數」的單位總價,使 rentTotalTwd = Σ snapshot × qty 維持成立,同步 rentTotalTwd / depositTotalTwd reducer 與測試。兩條線(估算 / 真借)並存不重複扣款。

已落地createRental 按天數計費 —— 租金 = rent_price_twd × max(1, 含頭尾天數 − 1) × 數量(含頭尾天數 = 租期 start_date → end_date),rent_price_twd_snapshot 存快照後的單位總價使 rentTotalTwd = Σ snapshot × qty 成立;押金不乘天數deposit_twd × 數量,一次性代收暫收)。與估算側同公式、兩條線並存不重複扣款。

6. 角色 / UI

  • 主檔維護(品項 / 庫存上限 / 價目)、租借單列表 / 開單 / 歸還驗收 → 裝備管理頁(apps/wireframes/app/admin/equipment 為 UI 真相)。
  • per-departure 需求調查 + 跨梯次彙總警示 → 出團前準備「裝備」軸(線控 /admin/control/[depId]/departure-prep)。
  • 權限走 requirePermission(resource.action);audit 由 action 層寫(比照 group-codes-repo / departure-costs)。

7. 連結

Changelog

版本日期變更Commit
12026-05-30初版 stub:登錄缺口 + 資料模型草圖(財務確認 GS 已有替代,佔位待排程)
22026-06-23從 stub 扶正到現行 schema:equipment_items(race-safe 庫存三量 + GS 7 類 seed + 庫存上限/每日價目)、per-order 租借(equipment_rentals,接 equipment_rental/equipment_deposit charge line)、per-departure 需求調查(departure_equipment_needs)+ 跨梯次彙總 summarizeEquipmentDemand(surplus/shortage + 剩餘≤2 lowStock 低水位)。現場付現逐日計價標 follow-up。
32026-06-24現場付現估算落地(§5):listEquipmentNeedsrentPriceTwd、「裝備」tab 每列 + 合計顯示 需求量 × 行程日數 × 每日租金(行程日數 = 梯次日期含頭尾),UI 派生不落 DB;與 per-order 租金 charge line 並存。
42026-06-24對齊原始 GS 價目表:計價以「晚數」計 max(1, 含頭尾天數 − 1)(1~2 天同價、3 天×2、4 天×3),修正現場付現估算原本誤用 × 含頭尾天數(1 夜團多收一倍)。createRental per-order 租金未乘計價日數的不一致記為 §5「待修齊」(動真實應收,待決策)。
52026-07-01§5 決策拍板:per-order 租借 createRental 按天數計費(同估算側 rent_price × max(1,含頭尾天數−1) × qty),解除「待決策」;現行 code 仍 flat,列為 code follow-up(三方對齊稽核 D3)。
62026-07-02§5 code 落地:createRental 改按天數計費(rent_price × max(1,含頭尾天數−1) × qty,含頭尾天數 = 租期 start→end),rent_price_twd_snapshot 存單位總價、押金不乘天數;rentTotalTwd reducer 與測試同步。解除 D3 code follow-up。