跳至主要内容

US-201: 創建訂單

User Story

作為 店員 我想要 能夠為客戶創建訂單 以便 記錄客戶的購買需求並啟動訂單處理流程


驗收標準 (Acceptance Criteria)

Scenario 1: 為個人客戶創建標準訂單

  • Given 我是已登入的店員
  • And 系統中有現有客戶「李大華」
  • And 有可用商品「玫瑰花束(12 朵)」價格 NT$1,200
  • When 我選擇客戶「李大華」
  • And 我添加商品「玫瑰花束(12 朵)」數量 2
  • And 我填寫配送地址「台北市信義區信義路五段 7 號」
  • And 我選擇配送日期「2025-11-01」與時段「14:00-18:00」
  • And 我填寫卡片訊息「生日快樂!」
  • And 我點擊「創建訂單」按鈕
  • Then 系統應創建訂單並自動分配訂單編號(格式:{租戶代碼}-{YYYYMMDD}-{流水號}
  • And 訂單狀態應為「待確認」
  • And 訂單總金額應為 NT$2,400(含稅)
  • And 我應看到訂單創建成功的通知
  • And 系統應發送訂單確認通知給客戶(Email/SMS)

Scenario 2: 為企業客戶創建訂單

  • Given 我是已登入的店員
  • And 系統中有企業客戶「XX 科技公司」
  • When 我選擇企業客戶「XX 科技公司」
  • And 我選擇子帳戶「人資部」
  • And 我添加商品「祝賀花籃」
  • And 我填寫收件人資訊(與訂購客戶不同)
  • And 我選擇支付方式為「月結」
  • And 我點擊「創建訂單」按鈕
  • Then 系統應創建訂單並關聯到企業主帳戶
  • And 訂單應標記為「月結付款」
  • And 系統應記錄子帳戶資訊

Scenario 3: 創建客製化專案訂單(無預設商品)

  • Given 我是已登入的店員
  • And 客戶要求「會場佈置」服務
  • When 我選擇訂單類型為「客製化專案」
  • And 我輸入需求描述「婚禮會場佈置,粉色系,100 坪空間」
  • And 我手動輸入協商價格 NT$50,000
  • And 我填寫調整原因「客製化專案無標準價格」
  • And 我點擊「創建訂單」按鈕
  • Then 系統應創建訂單且不包含商品項目
  • And 訂單應標記為「客製化專案」
  • And 審計日誌應記錄價格調整原因

Scenario 4: 創建訂單時缺少必填欄位(錯誤場景)

  • Given 我是已登入的店員
  • When 我開始創建訂單但未選擇客戶
  • And 我點擊「創建訂單」按鈕
  • Then 系統應顯示錯誤訊息「請選擇客戶」
  • And 訂單不應被創建
  • And 表單應保留我已填寫的資料

Scenario 5: 配送日期早於當天(錯誤場景)

  • Given 我是已登入的店員
  • And 今天是 2025-10-31
  • When 我選擇配送日期為 2025-10-20(早於今天)
  • And 我點擊「創建訂單」按鈕
  • Then 系統應顯示錯誤訊息「配送日期不能早於今天」
  • And 訂單不應被創建

業務規則 (Business Rules)

  1. 訂單編號規則

    • 格式: {租戶代碼}-{YYYYMMDD}-{流水號}
    • 範例: ABC-20251031-0001
    • 流水號每日從 0001 開始重新計數
  2. 必填欄位

    • 客戶資訊(必須選擇現有客戶或創建新客戶)
    • 至少一個商品項目(客製化專案除外)
    • 配送地址與聯絡電話
    • 期望配送日期與時段
  3. 價格計算規則

    • 小計 = Σ(商品單價 × 數量)
    • 稅額 = 小計 × 稅率(從租戶設定獲取,預設 5%)
    • 總計 = 小計 + 稅額
  4. 價格調整

    • 店員可手動調整最終價格
    • 調整時必須填寫調整原因(記錄至審計日誌)
    • 調整後的價格不能為負數
  5. 配送日期限制

    • 不能早於當天
    • 不能選擇店休日(從租戶設定獲取)
  6. 初始狀態

    • 新創建的訂單狀態固定為「待確認」
  7. 多租戶隔離

    • 訂單自動關聯到當前用戶的 tenantId
    • 客戶選擇列表僅顯示當前租戶的客戶
    • 商品選擇列表僅顯示當前租戶的商品

UI/UX 需求 (UI/UX Requirements)

頁面結構

實作位置: src/applets/order-applet/order-editor.tsx

┌────────────────────────────────────────────────────────────────┐
│ Header Bar (sticky) │
│ [📦 創建訂單] [創建訂單] [✕] │
├─────────────────────────────────────┬──────────────────────────┤
│ │ │
│ 訂單表單 (OrderForm) │ 說明側邊欄 │
│ - 客戶選擇 │ - 建立訂單說明 │
│ - 商品列表 │ - 注意事項 │
│ - 配送資訊 │ - 相關連結 │
│ - 付款方式 │ - 需要協助 │
│ │ │
│ grid-cols-1 lg:grid-cols-3 │ card bg-base-200 │
│ lg:col-span-2 │ lg:col-span-1 │
└─────────────────────────────────────┴──────────────────────────┘

頂部工具列

  • 固定位置: sticky top-0 z-40 bg-base-100 border-b border-base-300
  • 左側: 頁面標題(ShoppingBag 圖示 + 「創建訂單」)
  • 右側:
    • 「創建訂單」按鈕(variant="soft" color="primary"
    • 取消按鈕(X 圖示)

草稿自動儲存

  • 表單變更時自動儲存至 Redux Store(draft-orders-slice
  • 草稿 ID 從 URL 參數取得或自動生成(draft-{timestamp}
  • 訂單創建成功後自動刪除草稿

互動行為

  • 客戶選擇: 支援自動完成(輸入姓名或電話搜尋)
  • 商品選擇: 顯示商品名稱、價格、庫存狀態
  • 配送日期: 排除店休日(日曆組件中標灰)
  • 價格即時計算: 數量/單價變更時自動更新

錯誤處理

  • 欄位驗證: 紅色邊框 + 欄位下方顯示錯誤訊息
  • API 錯誤: 頁面頂部顯示 alert alert-error
  • 成功通知: prompt.success() Toast 通知

響應式設計

  • 桌面 (lg:): 三欄 Grid(表單 2 欄 + 側邊欄 1 欄)
  • 平板/手機: 單欄佈局(垂直排列)

技術規格 (Technical Specifications)

API 端點

  • 端點: POST /api/v1/orders
  • 權限要求: ROLE_STAFF 或更高
  • 多租戶隔離: 自動附加 tenantId(從 JWT Token 提取)

請求 Payload

interface CreateOrderRequest {
customerId: string; // 客戶 ID(必填)
items: OrderItem[]; // 商品列表(至少 1 個,客製化專案除外)
deliveryAddress: string; // 配送地址(必填)
deliveryPhone: string; // 聯絡電話(必填)
deliveryDate: string; // 配送日期 ISO 8601(必填)
deliveryTimeSlot: string; // 時段(如 "14:00-18:00")
cardMessage?: string; // 卡片訊息(可選)
specialRequests?: string; // 特殊要求(可選)
recipientName?: string; // 收件人姓名(可選)
recipientPhone?: string; // 收件人電話(可選)
internalNotes?: string; // 內部備註(可選)
isCustomProject?: boolean; // 是否為客製化專案(可選)
customProjectDescription?: string; // 客製化專案描述(isCustomProject=true 時必填)
manualPriceAdjustment?: number; // 手動價格調整(可選)
priceAdjustmentReason?: string; // 價格調整原因(manualPriceAdjustment 存在時必填)
}

interface OrderItem {
productId: string; // 商品 ID
quantity: number; // 數量(必須 > 0)
price: number; // 單價(從商品獲取,可調整)
notes?: string; // 項目備註(如「不要百合」)
}

響應範例

{
"orderId": "ABC-20251031-0001",
"orderNumber": "ABC-20251031-0001",
"status": "pending_confirmation",
"customerId": "cust-123",
"items": [...],
"deliveryInfo": {...},
"pricing": {
"subtotal": 2400,
"tax": 120,
"total": 2520
},
"createdAt": "2025-10-31T10:30:00Z",
"createdBy": "user-001"
}

數據模型

  • 主要 Entity: Order
  • 關聯 Entity: OrderItem, Customer, Product
  • 詳細定義: 參考訂單數據模型(data-models/order.md 待建立)

前端組件

組件架構:

src/applets/order-applet/
├── order-editor.tsx # 訂單編輯/創建頁面
├── components/
│ └── order-form.tsx # 訂單表單組件
├── types.ts # OrderFormData 型別定義
└── (order-finder.tsx) # 訂單列表(US-203)

src/services/sales/
└── order-service.ts # 訂單 Service(CRUD 操作)

src/features/sales/
└── draft-orders-slice.ts # 草稿狀態管理

使用框架組件:

  • @appfuse/appfuse-web/componentsInput, Select, Button
  • @appfuse/appfuse-webprompt(Toast 通知)
  • @appfuse/appfuse-web/utilsi18n, logger, ErrorResponse
  • lucide-reactShoppingBag, X

狀態管理:

  • Redux Toolkit:draft-orders-slice 管理草稿
  • React Hook Form:表單驗證與狀態
  • orderService:API 調用(透過 ajax 自動處理 Token 與錯誤)

SBE 場景 (Specification by Example)

詳細的測試場景與範例數據:


估算 (Estimation)

  • Story Points: 5 點
  • 預估工時: 2-3 天
  • 複雜度: 中等

工作拆分

  1. 後端開發 (1 天)

    • 實作 POST /api/v1/orders API
    • 訂單編號生成邏輯
    • 價格計算與驗證
    • 多租戶隔離
  2. 前端開發 (1-1.5 天)

    • 創建 OrderForm 組件
    • 客戶選擇與商品選擇邏輯
    • 價格即時計算
    • 表單驗證與錯誤處理
  3. 整合與測試 (0.5 天)

    • 整合測試(API + UI)
    • E2E 測試(關鍵流程)
    • Mock API 更新

依賴 (Dependencies)

前置條件

  • US-101: 創建產品 - 需要商品資料
  • US-301: 創建客戶 - 需要客戶資料
  • Epic 0: 認證系統 - 需要 JWT Token 與角色驗證

並行開發

  • 可與 US-202(確認訂單)並行開發

外部依賴

  • 租戶設定 API(獲取稅率、店休日)
  • 通知系統 API(發送訂單確認通知)

測試策略 (Testing Strategy)

單元測試

  • 測試價格計算邏輯(小計、稅額、總計)
  • 測試訂單編號生成邏輯
  • 測試表單驗證規則

整合測試

  • 測試 POST /api/v1/orders API 端點
  • 測試數據庫訂單創建與關聯
  • 測試多租戶隔離(確保不同租戶的訂單編號獨立)

E2E 測試

  • 測試完整的訂單創建流程(選擇客戶 → 添加商品 → 提交)
  • 測試錯誤處理(缺少必填欄位、無效日期)
  • 測試成功反饋(Toast 通知、導航)

手動測試

  • 測試響應式佈局(桌面、平板、手機)
  • 測試無障礙性(鍵盤導航、螢幕閱讀器)

完成定義 (Definition of Done)

  • 後端 API 實作完成並通過單元測試
  • 前端 UI 實作完成並符合設計規範
  • 整合測試通過(API + UI)
  • E2E 測試通過(至少涵蓋 Scenario 1 與 Scenario 4)
  • 單元測試覆蓋率 > 80%
  • Code Review 通過
  • API 文檔已更新(訂單 API 規格
  • Mock API 已更新(支援創建訂單)
  • 多租戶隔離驗證通過
  • 無障礙性測試通過(WCAG 2.1 AA)
  • 產品經理驗收通過

備註 (Notes)

設計決策

  • 為何價格可手動調整? 因為花店常有特殊需求(如加花材、VIP 客戶折扣),需要彈性調整價格。
  • 為何需要調整原因? 為了審計追蹤與財務透明,所有價格調整都需記錄原因。

未來優化

  • 支援商品組合包(Bundle)的快速選擇
  • 支援歷史訂單範本(快速創建重複訂單)
  • 支援批量導入訂單(Excel/CSV)

最後更新: 2025-12-03