線稿索引所有 spec最後更新 2026-06-05
specs/orders.md

訂單

一句話:orders 是與產品種類無關的營運預訂外殼;「買了什麼」在 order_lines,產品專屬明細在 tour_booking_lines / lodging_booking_lines,金額與收退款真相在 訂單金流模型。本檔是訂單實體的單一入口:模型、狀態、不變式、存取契約、權限,以及「哪些功能 spec 擴充訂單」的索引。

精確欄位以 code 為準(packages/core/src/schema/business.tspackages/core/src/orders/*);狀態見 roadmap;畫面以 wireframe 為準(§7)。

1. 實體模型

訂單分四層,避免把產品欄位或金額欄位塞回外殼:

  • orders(營運外殼,kind-agnostic)order_numberuser_id(原生 FK 同庫 user)、kindtour / lodging / future package)、三軸狀態快取(booking_state / payment_state / refund_state)、notes、timestamps,以及派發欄位(created_by_user_id / assigned_to_user_id / assignment_status / assigned_at / assigned_by_user_id)。
  • order_lines(買了什麼,product-neutral):一張訂單可有多個 line,line_kind 可為 tour / lodging / addon / feesnapshot 只做顯示與稽核,不是第二份金額真相。
  • tour_booking_lines(行程產品明細):一個 tour line 對應一筆,存 departure_idparty_sizeunit_price_twd_snapshotprice_channel_snapshot。席次預留看這裡。
  • lodging_booking_lines(訂房產品明細):一個 lodging line 對應一筆,存旅宿/房型、入住退房區間、間數、入住人數;逐晚價格快照在 lodging 相關表。

orders 不擁有財務真相:訂單外殼不保存總額、單欄付款狀態、訂金/尾款 due 欄或付款方式。訂單應收、已收、餘款、退款狀態都由 訂單金流模型 的分類帳與 helper 派生。

2. 狀態機

orders 使用三軸狀態,而不是單一狀態欄:

欄位語意
履約booking_statepending_payment / confirmed / cancelled / completed
收款payment_stateunpaid / partially_paid / deposit_paid / paid / overpaid,由金流 helper 重算並快取
退款refund_statenone / partially_refunded / refunded,只反映面向客戶的退款

UI 顯示狀態一律走 deriveOrderDisplayStatus(booking_state, payment_state, refund_state),不要單獨把 payment_state 當 badge。退款、取消、完成等狀態有明確 precedence,見 訂單金流模型 §5。

派發子狀態與上述三軸正交:assignment_status = pending_review | assigned | reassigned,見 訂單派發

3. 不變式(不要違反)

  • 外殼不放產品欄位departure_idparty_size、房型/房晚、入住日等產品欄位都在 product line;不要加回 orders
  • 外殼不放金額真相:訂單應收 = SUM(order_charge_lines.amount_twd);現金 = payment_transactions;收款計畫 = payment_schedules。新功能不得把總額、付款狀態或收款計畫塞回訂單外殼。
  • tour-centric 讀取要明確 join:團控、分房、入山名冊等只處理行程單的功能,要經 order_lines + tour_booking_linesdeparture_id / party_size,並刻意排除 lodging。
  • customer 通用讀取要 kind-aware:會員中心與公開訂單頁可看到 tour/lodging,不可用 tour-only INNER JOIN 靜默漏掉訂房單。
  • race-safe 席次:tour 建單用 conditional UPDATE departures ... WHERE booked_count + N <= capacity + CHECK constraint,不靠讀後寫。
  • order_numberW{YYMMDD}{NNNNN},每 DB 一個 Postgres sequence + next_order_number() SQL function(setup-business-extras.ts)。
  • append-only 稽核:訂單相關動作寫 audit_log;財務真相的 append-only 規則見 訂單金流模型
  • owner-id 可見範圍:「只看自己的訂單」「sales 只看派給自己的」維持在 query 層(非 AC)。

4. 存取層契約

repo first arg 一律 db: ScopedDb;錯誤走 result-object。完整型別/簽名以 code 為準。

Customer(packages/core/src/orders/repo.ts — kind-aware:

  • 建立 tour 訂單:席次預留 → ordersorder_lines(tour)tour_booking_linesorder_charge_lines / payment_schedules / payment_intents
  • listOrdersForUser / getOrderById / getOrderByIdForUser 回 kind-aware DTO,依 orders.kind 分流呈現 tour / lodging。
  • getDepartureForCheckout 只負責 checkout 所需的梯次資料;價格與收款切分由定價與金流 helper 決定。

Admin(packages/core/src/orders/admin-repo.ts — order detail / list / status mutation:

  • 訂單列表與詳情需帶三軸狀態、金流摘要、旅客、audit、退款/請款相關列。
  • tour-only 聚合必須從 tour_booking_lines 取行程明細;通用金流資訊從 money helper / ledger 聚合取。
  • 取消 tour 訂單要釋回席次,但不自動退款;退款走 refunds 工作流。

其他訂單模組assignment.tsassignment-repo.tscontrol-stats.tsdashboard-stats.tsmanifest.tsmoney.tsmoney-state.tsvalidation.ts

5. 權限(order resource)

statement catalog 在 packages/core/src/permissions-ac.ts,閘走 auth.api.userHasPermission(點格式 order.action):

action用途spec
order.read看訂單(含詳情頁進入閘)本檔 / system-design
order.create建單本檔
order.update改單 / 取消 / 履約狀態本檔
order.review審核 pending_revieworder-assignment
order.assign / order.reassign指派 / 改派業務(受派池 ASSIGNABLE_ROLES=['sales','admin']order-assignment

customer 薄權限片(角色 customer);「只看自己」靠 query 層 owner-id 過濾,不靠 AC。

6. 擴充訂單的功能 spec(索引)

訂單是中央實體,下列 spec 各擴充它的一面——改訂單相關功能前先看這裡

Spec擴充什麼
order-money-model金流 SSOT:charge lines / schedules / intents / attempts / transactions / refunds / payables、三軸狀態與公式
order-assignment派發子狀態 / 指派改派 / sales 歸屬 / review
trip-pricing直客/同業價、訂金規則、尾款 schedule、包團定價
control-finance掛帳折讓(order_charge_lines(type='manual_adjustment'))、成本、payables
discount-code結帳折扣碼 + KOL 分潤快照;折扣以負 charge line 表達,order_discounts 保留兌換快照
lodging-direct-bookinglodging line / kind-aware 讀取 / 每晚庫存扣減
room-assignment旅客分房、房型升級補款 charge line / adjustment schedule
climbing-screening下單時資格審核 hook、order_screenings
traveler-validation旅客資料即時驗證、完整度、訂單金額一致檢查
member-ownership客戶/同業分類、owner 解析 → 指派輸入
dashboard / control-finance角色化統計 / 團控聚合(tour-centric 讀取)

7. 畫面(UI = wireframe)

訂單相關畫面的視覺真相在 wireframe;spec 只描述行為、連結至此(不另寫散文版面):

  • 後台列表 ../apps/wireframes/public/legacy/admin/orders.html
  • 後台詳情 ../apps/wireframes/public/legacy/admin/order-detail.html
  • 後台代訂/新建 ../apps/wireframes/public/legacy/admin/orders-new.html
  • 後台審核 ../apps/wireframes/public/legacy/admin/orders-review.html
  • 前台完成頁 ../apps/wireframes/public/legacy/site/order-complete.html

Changelog

版本日期變更Commit
22026-06-05改為現在式 SSOT:訂單外殼 + order_lines + product lines + 金流分類帳;移除舊訂單總額/單欄狀態/一單一筆產品表模型描述
12026-06-01初版:訂單實體核心 spec(模型/狀態機/不變式/契約/權限/擴充索引/wireframe)— 實體核心 spec 格式 pilot