模型關係圖(ER Diagram)
涵蓋兩個模組的所有核心模型。藍色 為 dobtor_trade_import 核心模型;綠色 為 dobtor_food_import_inspection 擴充模型。
核心模組:dobtor_trade_import
erDiagram
TRADE-IMPORT-BRAND {
char code PK "品牌碼(唯一),驅動序號"
char name "品牌名稱(中)"
char name_en "品牌名稱(英)"
sel food_category "🆕 seasoning/spread/snack/frozen/beverage"
m2o sequence_id "ir.sequence"
m2o partner_id "供應商"
m2o default_forwarder_id "預設貨代"
m2o default_customs_broker_id "預設報關行"
m2o default_incoterm_id "預設貿易條件"
sel default_payment_basis "付款基準"
int default_payment_days "付款天數"
}
TRADE-IMPORT-CASE {
char name PK "品牌序號 AY-010"
char title "案件名稱"
m2o brand_id FK
sel source "boss / marketing"
m2o buyer_id "採購負責人"
m2o supplier_id "供應商"
date order_date
date payment_deadline "compute"
sel payment_basis
bool can_release "compute,放行閘門"
sel state "主狀態機 11 態"
m2o company_id
}
TRADE-IMPORT-SHIPMENT {
char name PK "SHP/YYYY/00001"
m2o case_id FK
sel transport_mode "sea / air"
char master_bl_no
date bl_date "提單日"
date etd
date eta
date atd
date ata
sel release_type "original/telex/seaway"
sel state "booked/in_transit/arrived"
}
TRADE-IMPORT-CONTAINER {
char name PK "貨櫃號"
m2o shipment_id FK
sel container_type "20gp/40gp/40hq/rf/lcl"
int free_days
date demurrage_date "押櫃起算"
int overdue_days "compute"
monetary demurrage_fee_est "compute"
}
TRADE-IMPORT-CONTAINER-LINE {
m2o container_id FK
m2o product_id
float qty
}
TRADE-IMPORT-DECLARATION {
char name PK "DCL/YYYY/00001"
sel declaration_type "g1/d2/b6..."
m2o case_id FK
date declaration_date
float exchange_rate
sel clearance_channel "c1/c2/c3"
monetary cif_value
monetary duty_total "compute"
monetary vat_amount "compute (CIF+關稅+貨物稅)×5%"
monetary trade_fee "compute 0.04%,未逾100免徵"
sel state "draft/declared/inspecting/released"
}
TRADE-IMPORT-DECLARATION-LINE {
m2o declaration_id FK
m2o product_id
m2o hs_code_id FK
char import_permit_no "食品報驗連動鍵"
monetary customs_value "完稅價格(關稅基底)"
float duty_rate
monetary duty_amount
}
TRADE-IMPORT-HS-CODE {
char name PK "0901.21.00.00-5"
float duty_rate
sel tax_basis "ad_valorem / specific"
bool requires_food_inspection
bool requires_quarantine
}
TRADE-IMPORT-PORT {
char name
char code "LOCODE TWKHH"
sel port_type "sea / air"
}
TRADE-IMPORT-PAYMENT {
m2o case_id FK
date pay_date
monetary amount
bool is_prepaid
}
TRADE-IMPORT-RECYCLE-DECLARATION {
char period "2026-1"
m2o case_id FK
monetary amount_total "compute"
sel state "draft/declared/paid"
}
TRADE-STOCK-RESERVATION {
char name PK "🆕 RSV/YYYY/00001"
m2o product_id FK "商品"
m2o lot_id FK "批號(選填)"
m2o location_id FK "倉位"
float reserved_qty "保留數量"
float available_qty "compute 即時庫存"
char channel "通路名稱"
char reason "保留原因"
m2o applicant_id "申請人"
date apply_date "申請日"
date expected_ship_date "預計出貨日"
date end_date "保留結束日"
sel state "active/released/cancelled"
}
TRADE-IMPORT-BRAND ||--o{ TRADE-IMPORT-CASE : "brand_id"
TRADE-IMPORT-CASE ||--o{ TRADE-IMPORT-SHIPMENT : "shipment_ids"
TRADE-IMPORT-CASE ||--o{ TRADE-IMPORT-DECLARATION : "declaration_ids"
TRADE-IMPORT-CASE ||--o{ TRADE-IMPORT-PAYMENT : "payment_ids"
TRADE-IMPORT-CASE ||--o{ TRADE-IMPORT-RECYCLE-DECLARATION : "recycle_declaration_ids"
TRADE-IMPORT-SHIPMENT ||--o{ TRADE-IMPORT-CONTAINER : "container_ids"
TRADE-IMPORT-CONTAINER ||--o{ TRADE-IMPORT-CONTAINER-LINE : "line_ids"
TRADE-IMPORT-DECLARATION ||--o{ TRADE-IMPORT-DECLARATION-LINE : "line_ids"
TRADE-IMPORT-DECLARATION-LINE }o--|| TRADE-IMPORT-HS-CODE : "hs_code_id"
TRADE-IMPORT-SHIPMENT }o--|| TRADE-IMPORT-PORT : "pol_id / pod_id"
食品擴充模組:dobtor_food_import_inspection
erDiagram
FOOD-REGULATION-PROFILE {
char name
sel food_category
char tfda_category
sel inspection_method "paper/batch/enhanced/each/verify/monitor"
sel risk_level "low/medium/high"
int shelf_life_days
bool requires_quarantine
}
PRODUCT-TEMPLATE-EXT {
bool is_food
char name_en
char factory_code
m2o regulation_profile_id FK
m2o default_hs_code_id FK
sel product_state "new/normal/barcode_changed/renamed/discontinued/stop_import"
sel label_print_mark "print / label"
int pack_qty
}
FOOD-LABEL {
m2o product_id FK
int version
sel label_method "oem_preprint/oem_send/we_print/we_send/sanxin_label/none"
sel state "draft/review/approved/applied"
char product_name_zh
text ingredients_zh
text label_preview "compute,中文標全文"
}
FOOD-ALLERGEN {
char name
}
FOOD-NUTRITION-LINE {
m2o product_id FK
char nutrient
float amount
char unit
}
TRADE-IMPORT-INSPECTION {
char name PK "報驗序號"
sel inspection_type "border / self"
m2o case_id FK
m2o declaration_id FK
m2o inspection_agent_id "三信"
sel inspection_method
date apply_deadline "到港前15日內"
date amend_deadline "補正通知+30日"
bool requires_quarantine
sel quarantine_state "na/pending/passed/rejected"
sel state "pending/sampling/testing/released/rejected"
}
TRADE-IMPORT-INSPECTION-SAMPLE {
m2o inspection_id FK
m2o product_id FK
m2o purchase_order_id FK
date sample_date
float sample_qty
sel result
}
TRADE-IMPORT-DOCUMENT {
m2o case_id FK
m2o product_id FK
sel doc_type "spec/coa/tw_test/image/zh_label/health_cert/official_cert/import_permit"
date issue_date
date expiry_date
sel state "draft/valid/expired"
}
STOCK-LOT-EXT {
date manufacture_date
char supplier_lot "原廠批號"
m2o trade_import_case_id FK
int remaining_days "compute"
float life_ratio "compute"
sel expiry_alert "fresh/half/two_third/expired"
}
FOOD-RECALL-DEFERRED {
char name PK "📌延後 Phase4 RCL/YYYY/00001"
text reason "lot回連即可手動追溯"
sel state "draft/notified/recovering/closed"
}
FOOD-TRACEBOOK-REPORT {
char period "月份"
}
FOOD-REGULATION-PROFILE ||--o{ PRODUCT-TEMPLATE-EXT : "regulation_profile_id"
PRODUCT-TEMPLATE-EXT ||--o{ FOOD-LABEL : "label_ids"
PRODUCT-TEMPLATE-EXT }o--o{ FOOD-ALLERGEN : "allergen_ids"
PRODUCT-TEMPLATE-EXT ||--o{ FOOD-NUTRITION-LINE : "nutrition_ids"
TRADE-IMPORT-INSPECTION ||--o{ TRADE-IMPORT-INSPECTION-SAMPLE : "sample_ids"
FOOD-RECALL-DEFERRED }o--o{ STOCK-LOT-EXT : "lot_ids"
主狀態機與子狀態
trade.import.case 為主狀態機,declaration 與 inspection 各有獨立子狀態機,三者以 can_release 閘門匯流。⚠️ 本次範圍:draft → released + done(到岸成本);delivering/stored 屬倉後作業,暫不開發
trade.import.case — 主狀態機
stateDiagram-v2
direction TB
[*] --> draft
draft --> confirmed : action_confirm
confirmed --> production : 備貨中
production --> ready : 文件齊備
ready --> in_transit : ATD 開船
in_transit --> arrived : ATA 到港
arrived --> customs : 送關
customs --> released : action_release
note right of customs
放行閘門 can_release
報單全數 released
查驗全數通過(食品模組)
end note
released --> done : action_done
draft --> cancel
confirmed --> cancel
production --> cancel
ready --> cancel
in_transit --> cancel
arrived --> cancel
customs --> cancel
cancel --> [*]
state "倉後 delivering/stored 後期擴充" as oos
released --> oos
子狀態機:報單 + 查驗
stateDiagram-v2
direction TB
state "報單 trade.import.declaration" as D {
[*] --> d_draft
d_draft --> d_declared : 送關
d_declared --> d_released : C1 直接放行
d_declared --> d_inspecting : C2/C3 查驗
d_inspecting --> d_released : 海關放行
d_released --> [*]
}
state "查驗 trade.import.inspection" as I {
[*] --> i_pending
i_pending --> i_sampling : 食藥署抽中
i_sampling --> i_testing : 抽驗中
i_testing --> i_released : 查驗合格
i_testing --> i_rejected : 不合格
i_released --> [*]
i_rejected --> [*]
}
state "防檢 quarantine_state" as Q {
[*] --> q_na
q_na --> q_pending : requires_quarantine
q_pending --> q_passed : 防檢放行
q_pending --> q_rejected : 防檢退駁
q_passed --> [*]
q_rejected --> [*]
}
can_release 閘門計算邏輯(雙模組繼承覆寫)
flowchart LR
A([開始評估 can_release]) --> B{declaration_ids\n全部 released?}
B -- 否 --> Z1([can_release = False])
B -- 是 --> C{安裝了\n食品模組?}
C -- 否 --> Z2([can_release = True ✅\n基礎模組])
C -- 是 --> D{有 border\ntype 查驗記錄?}
D -- 否 --> Z3([can_release = False])
D -- 是 --> E{所有 border\n查驗 released?}
E -- 否 --> Z4([can_release = False])
E -- 是 --> F{有 requires_quarantine\n的查驗?}
F -- 否 --> Z5([can_release = True ✅\n食品模組])
F -- 是 --> G{quarantine_state\n全部 passed?}
G -- 否 --> Z6([can_release = False])
G -- 是 --> Z7([can_release = True ✅\n食品+防檢雙軌])
業務流程泳道圖
依角色(採購、原廠/貨代、報關行/三信、財務)劃分責任,對應 trade.import.case 主狀態。✅ 範圍:建單 → 海關放行 → 到岸成本結算
進口作業流程(含食品查驗)— 範圍至出關+費用結算
flowchart TD
subgraph sg1["👔 採購/業務"]
A1([老闆下單/行銷建單\nstate=draft]) --> A2[建立 trade.import.case\n選品牌帶出預設]
A2 --> A3[建立採購單 PO\n設定 Incoterm/幣別]
A3 --> A4[action_confirm\nstate=confirmed]
A4 --> A5[追蹤原廠備貨\nstate=production]
A5 --> A6{規格書比對\nspec_confirmed?}
A6 -- OK --> A7[確認中文標方式\nfood.label approved]
A7 --> A8[文件齊備\nstate=ready]
end
subgraph sg2["🚢 貨代/船公司"]
B1[建立 shipment\n填訂艙號/船名/航次] --> B2[建立貨櫃\n設定 container_type]
B2 --> B3[更新 ETD→ATD\nstate=in_transit]
B3 --> B4[更新 ETA→ATA\nstate=arrived]
end
subgraph sg3["📑 報關行"]
C1[建立 declaration\n填海關別/通關方式] --> C2[回填報單號\n提交 declaration_date]
C2 --> C3{clearance_channel}
C3 -- 類型C1 --> C4[海關放行\ndeclaration.state=released]
C3 -- 類型C2C3 --> C5[文審/查驗中\nstate=inspecting]
C5 --> C4
end
subgraph sg4["🔬 三信/食藥署"]
D1[建立 inspection\ntype=border] --> D2[預報驗\napply_deadline 前]
D2 --> D3{食藥署抽中?}
D3 -- 抽中 --> D4[建立 sample\nstate=sampling]
D4 --> D5[送驗\nstate=testing]
D5 --> D6{檢驗結果}
D6 -- 合格 --> D7[inspection.state=released]
D6 -- 不合格 --> D8[state=rejected\n退運/銷毀]
D3 -- 免驗 --> D7
end
subgraph sg5["💰 財務(範圍終點)"]
E1[閘門通過\ncan_release=True] --> E2[action_release\nstate=released]
E2 --> E3[登錄各項費用\n關稅/運費/保險/倉租/報關費]
E3 --> E4[到岸成本結算\nstock.landed.cost]
E4 --> E5([state=done 本次範圍終點])
end
subgraph sg6["🚫 後期擴充(倉後)"]
F1[車輛調度\nstate=delivering]
F2[action_store 收貨/批號登錄]
F3[效期管制/FEFO]
end
A8 --> B1
A8 --> C1
A8 --> D1
B4 --> C1
C4 --> E1
D7 --> E1
E5 -.-> F1
📌 保稅倉精靈(Phase 3 條件式)
延後至 Phase 3,僅保稅倉客戶才需要。Phase 2 只建 l10n_tw_is_bonded 欄位。
flowchart LR
A([保稅入倉\nl10n_tw_is_bonded=True]) --> B[出保稅完稅精靈\n需要時才做]
B --> C[依批量計稅\n保稅→已稅調撥]
C --> D[產生 D2 報單]
D --> E([完稅完成])
📌 部分扣倉精靈(Phase 3 條件式)
延後至 Phase 3 尾端,C3 比例低先用手動 picking 處理。
flowchart TD
A([C3 查驗\n部分不合格]) --> B{line_can_release}
B -- 合格品 --> C[放行至一般倉]
B -- 待驗品 --> D[is_hold 庫位\n等待結果]
D --> E{最終}
E -- released --> C
E -- rejected --> F[退運/銷毀]
放行閘門邏輯詳解
action_release 執行前必須通過 can_release 閘門,食品模組覆寫後為雙軌(報關+食藥署/防檢署)。
基礎模組閘門(dobtor_trade_import)
flowchart TD
A[action_release 呼叫] --> B{declaration_ids\n是否存在?}
B -- 無報單 --> E1([❌ 擋住\ncan_release=False])
B -- 有 --> C{所有 declaration\nstate == released?}
C -- 任一未放行 --> E1
C -- 全部放行 --> OK1([✅ 放行通過])
食品模組閘門(覆寫,僅認 border 類型)
flowchart TD
A[super._compute_can_release] --> B{base can_release\n已通過?}
B -- False --> Skip([跳過,維持 False])
B -- True --> C[篩選 border type\ninspection_ids]
C --> D{border 記錄\n存在?}
D -- 無 --> E1([❌ can_release=False])
D -- 有 --> E{所有 border\nstate == released?}
E -- 否 --> E1
E -- 是 --> F{任一 border\nrequires_quarantine?}
F -- 否 --> OK([✅ can_release=True])
F -- 是 --> G{所有 quarantine\nstate == passed?}
G -- 否 --> E1
G -- 是 --> OK
style OK fill:#48bb78,color:white
style E1 fill:#fc8181,color:white
海關狀態彙整邏輯(customs_state compute)
flowchart LR
A[declaration_ids] --> B{全部 released?}
B -- 是 --> S1([customs_state = released])
B -- 否 --> C{任一 inspecting?}
C -- 是 --> S2([customs_state = inspecting])
C -- 否 --> D{有 declared\n其餘未送?}
D -- 是 --> S3([customs_state = declaring])
D -- 否 --> E{皆 draft?}
E -- 是 --> S4([customs_state = draft])
E -- 否 --> F{無報單?}
F -- 是 --> S5([customs_state = none])
稅費計算流程
action_compute_taxes 強制順序計算,完稅價格(customs_value)≠ CIF,禁止直接代入。
稅費計算四步驟(declaration.action_compute_taxes)
flowchart TD
INPUT([報單明細\ndeclaration.line_ids]) --> S1
subgraph S1["Step 1 — 關稅"]
1A[Σ 各明細 customs_value × duty_rate] --> 1B[duty_total]
1C["⚠️ 基底 = customs_value(完稅價格)\n≠ cif_value(起岸價格)"]
end
subgraph S2["Step 2 — 貨物稅"]
2A[適用品項依貨物稅率計] --> 2B[commodity_tax]
end
subgraph S3["Step 3 — 營業稅(可扣抵進項)"]
3A["(完稅價格總額 + 關稅 + 貨物稅 + 菸酒稅/菸品健康捐)× 5%"] --> 3B[vat_amount]
3C["📌 非成本,為可扣抵進項稅額"]
end
subgraph S4["Step 4 — 推廣貿易服務費"]
4A[完稅價格 × 0.04%] --> 4B{金額 > NT$100?}
4B -- 是 --> 4C[trade_fee = 計算值]
4B -- 否 --> 4D[trade_fee = 0\n免徵]
end
S1 --> S2 --> S3 --> S4
S4 --> OUT([稅費彙總\n供到岸成本分攤])
付款期限 compute(依 payment_basis 選擇基準日)
flowchart LR
PB{payment_basis} --> TT[tt_prepaid\n基準 = order_date]
PB --> IV[iv_days\n基準 = invoice_date]
PB --> BL[bl_days\n基準 = 最早 bl_date]
PB --> DA[da_days\n基準 = acceptance_date]
PB --> SP[split\n多筆 payment 各自計]
PB --> CC[credit_card\n不計期限]
TT --> CALC[基準日 + payment_days\n= payment_deadline]
IV --> CALC
BL --> CALC
DA --> CALC
CALC --> CRON[Cron: 到期前\n建 mail.activity 通知財務]
🚫 本次範圍外 — 倉後流程,暫不開發
效期管制(stock.lot expiry tracking)、FEFO 揀貨策略、效期預警 Cron 均屬收貨入倉後的倉庫管理功能。
本次模組範圍以海關放行(released)+ 到岸成本結算(done)為終點,不含物理入倉與批號效期操作。
此頁面為規格參考,列為後期擴充項目。
本次模組範圍以海關放行(released)+ 到岸成本結算(done)為終點,不含物理入倉與批號效期操作。
此頁面為規格參考,列為後期擴充項目。
效期管制流程(食品模組)— 後期參考
沿用原生 stock.lot expiration_date + product_expiry 模組,擴充效期預警 compute 與 FEFO 庫存策略。
效期 compute 計算鏈
flowchart LR
subgraph Input["入庫登錄"]
I1[manufacture_date\n製造日]
I2[expiration_date\n到期日(原生)]
I3[shelf_life_days\n=regulation_profile.shelf_life_days]
end
subgraph Compute["每日 compute(UTC+8)"]
C1["remaining_days\n= expiration_date − today\n(需 UTC→UTC+8 偏移)"]
C2["life_ratio\n= remaining_days / shelf_life_days"]
C3["half_life_days\n= remaining_days − shelf_life_days/2"]
C4["third_life_days\n= remaining_days − shelf_life_days/3"]
end
subgraph Alert["expiry_alert Selection"]
A1["🟢 fresh\nlife_ratio > 0.5"]
A2["🟡 half\n0.33 < life_ratio ≤ 0.5\n過半效期"]
A3["🟠 two_third\n0 < life_ratio ≤ 0.33\n過2/3效期"]
A4["🔴 expired\nremaining_days ≤ 0"]
end
I1 --> C1
I2 --> C1
I3 --> C2
C1 --> C2
C1 --> C3
C1 --> C4
C2 --> A1
C2 --> A2
C2 --> A3
C2 --> A4
效期預警觸發流程(每日 Cron)
flowchart TD
CRON([每日 Cron 執行]) --> Q1[查詢所有\nexpiry_alert IN half, two_third\n且仍有庫存的 stock.lot]
Q1 --> L1{逐批號檢查}
L1 --> A1[建立 mail.activity\n通知促銷/下架負責人]
A1 --> A2[lot list 著色顯示\nhalf=黃 / two_third=橘 / expired=紅]
A2 --> R1[效期預警報表\n「二分之一庫存表」]
R1 --> R2{維度切換}
R2 --> V1[依庫別]
R2 --> V2[依品牌]
R2 --> V3[依警示級]
Q2([FEFO 策略]) --> F1[removal_date\n= expiration_date\n由原生 product_expiry 提供]
F1 --> F2[出庫自動選最早到期批號\nstock.removal_strategy = FEFO]
延後/縮減開發決策(v3.1)
依據客戶業務文件(2026-06-04)評估後,以下項目正式調整優先順序。未出現在業務文件中的功能一律延後。
📌 開發優先順序調整流程圖
flowchart TD
subgraph P1["Phase 1 MVP(維持)"]
P1A[trade.import.brand\n🆕 加 food_category]
P1B[trade.import.case 主狀態機]
P1C[shipment + container + 多視圖]
P1D["📌 前移:product_state 警語\n(Alice痛點:誤訂停產品)"]
P1E["📌 前移:factory_quote_price PO line\n(Alice痛點:報價分散)"]
end
subgraph P2["Phase 2 貿易核心(調整)"]
P2A[報單/稅費/HS Code]
P2B[付款管理 + cron 到期提醒]
P2C[到岸成本 / 回收費申報]
P2E["📌 縮減:保稅倉欄位架構預留\n精靈移到Phase3(條件式)"]
end
subgraph P3["Phase 3 食品合規(調整)"]
P3A[法規主檔/中文標/查驗/效期]
P3B[食品業者登錄/非追不可申報]
P3C[放行閘門覆寫]
P3D["📌 移至此:保稅出倉精靈D2\n(有保稅倉需求才做)"]
P3E["📌 移至此:部分扣倉精靈\n(C3比例低,有需求才做)"]
P3F["📌 縮減:food.recall 只做lot回連\n完整流程移Phase4"]
end
subgraph P4["Phase 4 產品化(縮減)"]
P4A[CorPaaS 多租戶]
P4B[Kanban 儀表板強化]
P4C["📌 food.recall 完整流程\n(有需求才做)"]
P4D["❌ 移除:OCA spreadsheet\n無需求依據,原生pivot+Excel夠用"]
end
subgraph OOS["🚫 不在本模組範圍(本次決策)"]
O1["先付款後分批出貨\n→ 銷售端SO功能,非進口模組"]
O2["EDI電子報關直連\n→ 只保留edi_ref欄位即可"]
O3["倉後流程(出關後)\n→ 批號效期/FEFO/保留單\n→ 後期另立模組/Phase擴充"]
end
P1 --> P2 --> P3 --> P4
🆕 新增:庫存保留單狀態機
stateDiagram-v2
direction LR
[*] --> active : 申請確認
active --> released : 出貨解除/手動結清
active --> cancelled : 取消保留
released --> [*]
cancelled --> [*]
note right of active
Cron: end_date < today
→ mail.activity 提醒申請人
end note
觸發 activity 警示的條件:
· 保留結束日到期未解除
· 保留量 > 實際庫存(超額保留)
倉庫揀貨整合:
· stock.quant 顯示「已被保留數量」
· 揀貨時可見 smart button 保留單計數
· 保留結束日到期未解除
· 保留量 > 實際庫存(超額保留)
倉庫揀貨整合:
· stock.quant 顯示「已被保留數量」
· 揀貨時可見 smart button 保留單計數
確認不納入 / 正式移除
❌ 移除出開發範圍
OCA spreadsheet 互動報表
理由:無任何客戶需求依據。Odoo 原生 pivot + Excel export 已足夠,不需額外依賴 OCA 模組。
OCA spreadsheet 互動報表
理由:無任何客戶需求依據。Odoo 原生 pivot + Excel export 已足夠,不需額外依賴 OCA 模組。
🚫 不在本模組範圍
先付款後分批出貨
理由:銷售端邏輯(SO backorder)。進口模組管採購端,銷售端另行處理,兩者不應混用。
先付款後分批出貨
理由:銷售端邏輯(SO backorder)。進口模組管採購端,銷售端另行處理,兩者不應混用。
⏸ 只保留欄位,不實作介接
EDI 電子報關直連(Trade-Van)
理由:政府介接成本高,客戶未要求。
EDI 電子報關直連(Trade-Van)
理由:政府介接成本高,客戶未要求。
edi_ref 欄位保留供日後擴充。
🚫 出關後倉後流程 — 本次全部不開發
批號登錄 / 效期管制(stock.lot expiry)/ FEFO 揀貨
庫存保留單(trade.stock.reservation)
收貨入倉(action_store)/ 追溯回收(food.recall)
理由:本次模組範圍以海關放行(released)+ 到岸成本結算(done)為終點,物理入倉與倉庫管理後期另立需求再做。
批號登錄 / 效期管制(stock.lot expiry)/ FEFO 揀貨
庫存保留單(trade.stock.reservation)
收貨入倉(action_store)/ 追溯回收(food.recall)
理由:本次模組範圍以海關放行(released)+ 到岸成本結算(done)為終點,物理入倉與倉庫管理後期另立需求再做。
✅ 已驗證必要功能(本次範圍內)
付款期限 cron 提醒、product_state 警語、factory_quote_price、非追不可申報
→ 均在 Alice 文件中有明確痛點對應,且均屬出關前流程
付款期限 cron 提醒、product_state 警語、factory_quote_price、非追不可申報
→ 均在 Alice 文件中有明確痛點對應,且均屬出關前流程
付款期限計算與款項追蹤
trade.import.case 依 payment_basis 決定付款基準日,加 payment_days 算出 payment_deadline;Cron 到期前建 activity 通知財務。
payment_deadline compute 邏輯
flowchart TD
A([action_confirm 確認作業單]) --> B{payment_basis?}
B -- tt_prepaid 電匯預付 --> C[base = order_date 訂貨日]
B -- iv_days 發票天數 --> D[base = invoice_date 發票日]
B -- bl_days 提單天數 --> E["base = min(shipment.bl_date)\n取最早船務提單日"]
B -- da_days 承兌天數 --> F[base = acceptance_date 承兌日]
B -- split 分批匯款 --> G[手動新增 payment_ids\npayment_deadline = N/A]
B -- credit_card 信用卡 --> H[信用卡帳單\npayment_deadline = N/A]
C & D & E & F --> I["payment_deadline =\nbase + relativedelta(days=payment_days)"]
I --> J[store=True 存資料庫\n支援日曆/filter]
I --> K{今日 vs payment_deadline}
K -- 距到期 N 天 --> L[Cron 建 mail.activity\n通知財務人員]
K -- 已逾期 --> M[逾期旗標\nlist 著紅色]
款項追蹤 payment_ids
flowchart TD
subgraph CASE["trade.import.case"]
PD[payment_deadline]
PT[paid_total compute]
BAL[balance compute]
end
subgraph PMT["trade.import.payment (One2many)"]
P1["匯款 1\npay_date / amount / currency_id\nexchange_rate / is_prepaid"]
P2["匯款 2 (換匯扣抵)"]
P3["..."]
end
PMT -- Σ amount --> PT
PT & PD --> BAL
BAL --> W{balance > 0?}
W -- 是 --> X[仍有未付款項]
W -- 否 --> Y[✅ 已結清]
subgraph RATE["匯率快照"]
R1[exchange_rate = 報關日匯率]
R2[不隨市場浮動]
end
PMT -.-> RATE
payment_basis 各選項對照
flowchart LR
subgraph BASIS["payment_basis 六種"]
A1["tt_prepaid\n電匯預付\n基準:訂貨日"]
A2["iv_days\n發票天數\n基準:發票日"]
A3["bl_days\n提單天數\n基準:B/L 日"]
A4["da_days\n承兌天數\n基準:承兌日"]
A5["split\n分批匯款\n手動輸入"]
A6["credit_card\n信用卡"]
end
A1 & A2 & A3 & A4 --> DL["payment_deadline\n= base + payment_days"]
A5 --> ML["多筆 payment_ids\n逐批追蹤"]
A6 --> CC["帳單日另計\n不走 deadline 欄位"]
DL --> CAL["calendar 視圖\n付款期限維度"]
DL --> ACT["mail.activity\n到期提醒財務"]
食品查驗雙軌詳細流程
trade.import.inspection 以 inspection_type 區分邊境報驗(進放行閘門)與自主送驗(不進閘門)。border type 再分防檢署雙軌。
邊境報驗(border)完整流程
flowchart TD
A([case → arrived 到港]) --> B[向食藥署申請邊境報驗\ntrade.import.inspection\ntype=border]
B --> DL[apply_deadline\n到港前15日內/到港後法定期限]
B --> C{海關通關方式\nclearance_channel}
C -- C1 免審免驗 --> D[state = released\n直接進放行閘門]
C -- C2 應審免驗 --> E[文件審查\nstate = inspecting]
C -- C3 應審應驗 --> F[抽驗\nstate = sampling]
E --> G{審查結果}
F --> F2[建 inspection.sample\n記錄抽驗批號/機構/項目]
F2 --> H[state = testing\n檢驗中]
H --> G
G -- 合格 --> I[state = released]
G -- 不合格 --> J[state = rejected\n補正通知日+30=amend_deadline\n可展延一次30日]
J --> K{補正結果}
K -- 補正合格 --> I
K -- 不合格/逾期 --> L[退運/銷毀]
I --> M{requires_quarantine?}
M -- False --> N([✅ 進放行閘門\nborder released])
M -- True 防疫動植物 --> O[quarantine_state = pending\n防檢署平行審查]
O --> P{防檢署結果}
P -- passed --> N
P -- rejected --> Q([❌ 防檢署退駁])
自主送驗(self)流程
flowchart TD
A([上市前自主品管]) --> B[建 trade.import.inspection\ntype=self\nchannel=請購選號]
B --> C[送驗至 lab_id 檢驗機構]
C --> D[state = testing]
D --> E{lab 出具報告}
E -- 合格 --> F[state = released\nreport_email_date 記錄]
E -- 不合格 --> G[state = rejected\n通知品管]
F --> H[❌ 不進放行閘門\n僅供品管留痕]
H --> I[product_id 累積\ninspection_sample_ids 歷史]
抽驗歷史回連 product
flowchart TD
A[trade.import.inspection.sample] --> B[product_id\ncase_id\npurchase_order_id]
B --> C{product.template\nhas_open_doc_gap\n或最近 rejected?}
C -- 是 --> D[下次建 PO line\nonchange 跳警語]
D --> E[列出缺漏文件\npending inspection]
C -- 否 --> F[正常建單]
A --> G[product smart button\n查驗歷史 N 筆]
中文標版本控管(food.label)
food.label 一產品多版本;approved 後鎖定不可改,新需求另開版本。採購單可覆寫 label_method 但不回寫產品主檔。
food.label 狀態機
stateDiagram-v2
direction TB
[*] --> draft : 新建中文標
draft --> review : 送審
review --> approved : 主管核准
review --> draft : 退回修改
approved --> applied : 已用於進口作業
approved --> [*] : 建新版本 V+1
note right of approved
approved 後任何欄位均 readonly
如需修改請另開新版本
end note
label_method 決策與 override
flowchart TD
A[product.template\ndefault_label_method] --> B{採購單 PO line\n有 label_method_override?}
B -- 無 override --> C[沿用產品預設\nlabel_method]
B -- 有 override --> D[以 override 為準\n不回寫產品主檔]
C & D --> E{label_method 值}
E -- oem_preprint --> F[原廠已印於外觀\n不貼標]
E -- oem_send --> G[寄標至原廠\n原廠自貼]
E -- we_print --> H[進口商自印貼]
E -- we_send --> I[自印後寄原廠]
E -- sanxin_label --> J[三信代貼\n配合報驗時序]
E -- none --> K[免標示商品]
F & G & H & I & J & K --> L[出貨/報驗以此為準]
L --> M[food.label.label_preview\n彙整中文標全文供平台複貼]
中文標 × 報驗 × 作業單 三者關係
flowchart LR
subgraph BRAND["品牌主檔"]
BR[trade.import.brand\ndefault_label_method\nregulation_profile_id]
end
subgraph PRODUCT["產品主檔"]
PT[product.template\nlabel_method\nis_food\nregulation_profile_id]
FL["food.label\nV1 approved ✅\nV2 draft 🔧"]
end
subgraph CASE["作業單"]
IC[trade.import.case\nlabel_method]
PO["purchase.order.line\nlabel_method_override\n(不回寫)"]
INS[trade.import.inspection\nborder / self]
end
BR -- onchange 帶入 --> IC
IC -- 建立 --> PO
PT -- 預設 --> PO
PO -- override 優先 --> INS
FL -- V最新approved --> PT
PT -- 關聯 --> INS
進口來源追溯鏈
本次範圍:從作業單向上追溯至品牌/供應商/報單/船務(進口來源追溯)。批號效期管制、向下客戶追蹤屬倉後流程,後期再做。
進口來源追溯鏈(本次範圍)
flowchart TD
subgraph CORE["本次範圍:作業單 → 供應來源"]
CASE["trade.import.case\nname: AY-010\nbrand_id / supplier_id\nstate: released"]
CASE --> BRAND["trade.import.brand\n品牌/供應商/貨代\nfood_category"]
CASE --> PO["purchase.order\n採購單號 / 品項 / 價格\n原廠 PI/Invoice"]
CASE --> SHIP["trade.import.shipment\n船名 / BL / ETD / ATA\nmaster_bl_no"]
CASE --> DECL["trade.import.declaration\n報單號 / clearance_channel\n關稅 / 完稅價格"]
CASE --> INSP["trade.import.inspection\nborder: 報驗號 / 放行日\nquarantine_state"]
CASE --> DOC["trade.import.document\nspec / coa / health_cert\n到期日管控"]
SHIP --> PORT["trade.import.port\n起運港 POL / 目的港 POD"]
DECL --> HS["trade.import.hs.code\n稅則 / duty_rate\ninspection_no"]
end
subgraph LATER["🚫 後期擴充(倉後)"]
LOT["stock.lot\ntrade_import_case_id 回連\n批號 / 效期"]
LOT --> DOWN["向下追溯\n出貨 → 客戶"]
LOT --> RECALL["food.recall\n條件式 Phase 4"]
end
CASE -.-> LOT
報單 ← HS Code ← 報驗 三角連動(本次範圍)
flowchart LR
HS["trade.import.hs.code\nname: 0901.21.00.00-5\nduty_rate / inspection_no\ntax_basis"]
DLINE["declaration.line\nhs_code_id / customs_value\nduty_rate / import_permit_no"]
INSP["trade.import.inspection\nborder type\nimport_permit_no 連動鍵"]
HS -- onchange 帶入 --> DLINE
HS -- inspection_no 反推 --> INSP
DLINE -- import_permit_no --> INSP
INSP -- 食藥署查驗合格 --> GATE([can_release 閘門])
DLINE -- customs_value × duty_rate --> TAX([關稅 + 營業稅])
夥伴角色旗標(res.partner 擴充)
flowchart TD
P["res.partner\n(單一模型,多角色)"]
P --> F[is_forwarder 貨代]
P --> CB[is_customs_broker 報關行]
P --> SL[is_shipping_line 船公司]
P --> TR[is_trucking 拖車]
P --> IA[is_inspection_agent 報驗行三信]
P --> BW[is_bonded_warehouse 保稅倉]
F -- domain --> CASE_F["case.forwarder_id"]
CB -- domain --> CASE_CB["case.customs_broker_id"]
SL -- domain --> SHIP_SL["shipment.shipping_line_id"]
IA -- domain --> INSP_IA["inspection.inspection_agent_id"]
subgraph FOOD["食品模組擴充"]
RC[food_business_no\n食品業者登錄字號]
INS[insurance_no\n保險字號]
end
P --> FOOD
結構化文件管控(trade.import.document)
取代純 attachment 池;對文件到期日主動提醒;連動報驗前查核與證照效期(工廠執照、健康證明、進口許可)。
文件狀態機
stateDiagram-v2
direction LR
[*] --> draft : 上傳文件
draft --> valid : 填入 expiry_date
valid --> expired : expiry_date < today
expired --> valid : 補件/換新
draft --> valid : 無到期限制文件
文件到期 Cron 流程
flowchart TD
A([Cron 每日執行]) --> B["掃描 trade.import.document\nexpiry_date < today\nstate != expired"]
B --> C[更新 state → expired]
C --> D[建 mail.activity\n通知 case 負責人]
D --> E{doc_type 分類}
E -- health_cert 健康證明 --> F[通知採購補健康證]
E -- official_cert 官方證明 --> G[通知報關行]
E -- import_permit 輸入許可 --> H[通知食藥署報驗負責人]
E -- spec 規格書 --> I[通知品管更新規格]
E -- tw_test 台灣檢驗 --> J[通知品管重新送驗]
文件類型 × 使用時機對照
flowchart LR
subgraph TYPES["doc_type 八種"]
T1[spec 規格書]
T2[coa 聲明書/合格證]
T3[tw_test 台灣檢驗報告]
T4[image 產品圖檔]
T5[zh_label 中文標]
T6[health_cert 健康證明正本]
T7[official_cert 官方證明]
T8[import_permit 輸入許可]
end
subgraph WHEN["使用時機"]
W1[報驗前查核\n確認 spec/coa/health_cert 齊備]
W2[報驗申請時\n上傳 import_permit/official_cert]
W3[中文標審核\nzh_label → food.label 對應]
W4[通關時\ntw_test 合格才放行 C2]
end
T1 & T2 & T6 --> W1
T7 & T8 --> W2
T5 --> W3
T3 --> W4
subgraph GAP["缺件警示"]
G1["product.template\nhas_open_doc_gap compute\nPO line onchange 跳警語"]
end
T1 & T2 & T3 -.-> G1