feat(order): 订单支出台账(快递费/吊卡费等杂费)

订单上原本只有「卖给客户多少钱」,而一张单真实花出去的还有寄样品的快递费、
印吊卡的费用、商检报关拖车。不记下来就算不出这单赚没赚 —— 小单子几百块
杂费能把毛利吃掉一截。

· 新表 order_expenses,一张订单挂多笔:类型/金额/币种/日期/收款方/单号/
  已付未付/备注。单独成表而不是往 orders 加列,因为杂费是多笔不定项的,
  寄三次样品就是三条快递费,各有单号;塞进 orders 只能存一个合计,事后
  对不出「这 800 块是哪来的」。

· 跨币种强制填汇率并存快照。杂费基本是人民币、订单常是美元;不存快照的话
  汇率一变历史订单毛利跟着变,账永远对不上。同币种强制按 1,防止前端传个
  7.2 把美元支出又乘一遍。

· 新权限点 order.expense,不复用 order.edit。掏钱寄样品的是销售业务员,
  而销售业务组只有 order.view —— 挂在 order.edit 上要么他记不了账,要么
  为了让他记账得把「改订单金额」一起给他。启动时 syncNewPermissions()
  会自动把这个新点补给四个默认组。

· 订单列表加「杂费」列(合计 + 未付)。SUM 外层再 ROUND 一次:不加会漏出
  348.15999999999997 这种浮点尾巴直接显示在页面上。

· 金额逐行取两位再相加,保证明细几行加起来等于合计。

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Drsakura
2026-10-22 15:22:56 +08:00
co-authored by Claude Opus 5
parent 3e9fd2dc97
commit f32a2daaef
6 changed files with 643 additions and 5 deletions
+26
View File
@@ -161,6 +161,32 @@ function ensureColumns() {
sha256 TEXT, bytes BLOB NOT NULL, stored_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_cart_items_sort ON cart_items(sort);
/* 订单支出(业务员记的杂费:快递费、吊卡费、商检、报关、拖车、打样…)。
单独一张表而不是往 orders 加几列 —— 一笔订单的杂费是多笔不定项的,
寄三次样品就是三条快递费,各自有单号和日期;塞进 orders 只能存一个合计,
事后对不出「这 800 块是哪来的」。
rate_to_order 是「记账当时」的汇率快照:快递费是人民币、订单可能是美元,
不存快照的话汇率一变,历史订单的毛利会跟着变,账永远对不上。 */
CREATE TABLE IF NOT EXISTS order_expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL REFERENCES orders(id),
category TEXT NOT NULL,
amount REAL NOT NULL,
currency TEXT NOT NULL DEFAULT 'CNY',
rate_to_order REAL NOT NULL DEFAULT 1,
spent_at TEXT,
payee TEXT,
supplier_id INTEGER REFERENCES suppliers(id),
voucher_no TEXT,
is_paid INTEGER NOT NULL DEFAULT 0,
note TEXT,
created_by INTEGER REFERENCES users(id),
created_at TEXT NOT NULL,
updated_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_order_expenses_order ON order_expenses(order_id);
CREATE INDEX IF NOT EXISTS idx_order_expenses_date ON order_expenses(spent_at);
`);
const columnMigrations = {
+33
View File
@@ -630,3 +630,36 @@ CREATE INDEX IF NOT EXISTS idx_follow_up_inquiry ON follow_up_records(inquiry_id
CREATE INDEX IF NOT EXISTS idx_product_packaging_sku ON product_packaging(sku);
CREATE INDEX IF NOT EXISTS idx_contract_import_jobs_status ON contract_import_jobs(status, created_at);
CREATE INDEX IF NOT EXISTS idx_contract_import_items_job ON contract_import_items(job_id, status);
-- ---------------------------------------------------------------------------
-- 订单支出(业务员自己记的杂费:快递费、吊卡费、商检、报关、拖车、打样…)
--
-- 为什么单独一张表而不是往 orders 加几列:一笔订单的杂费是**多笔、不定项**的,
-- 寄三次样品就是三条快递费,各自有单号和日期。塞进 orders 只能存一个合计,
-- 事后对不出「这 800 块是哪来的」。
--
-- 币种是关键:快递费、吊卡费都是人民币,订单却可能是美元。所以每笔支出带自己的
-- currency,外加一个 rate_to_order —— **下单当时的汇率快照**。不存快照的话,
-- 三个月后汇率变了,历史订单的毛利会跟着变,账就永远对不上。
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS order_expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL REFERENCES orders(id),
category TEXT NOT NULL, -- 快递费 / 吊卡费 / 商检费 / 报关费 / 拖车费 / 打样费 / 唛头印刷 / 其他
amount REAL NOT NULL,
currency TEXT NOT NULL DEFAULT 'CNY',
-- 支出币种 → 订单币种的汇率快照;同币种为 1
rate_to_order REAL NOT NULL DEFAULT 1,
spent_at TEXT, -- 发生日期 YYYY-MM-DD
payee TEXT, -- 收款方:顺丰 / 某印刷厂
supplier_id INTEGER REFERENCES suppliers(id),
voucher_no TEXT, -- 快递单号 / 发票号,事后核对用
is_paid INTEGER NOT NULL DEFAULT 0,
note TEXT,
created_by INTEGER REFERENCES users(id),
created_at TEXT NOT NULL,
updated_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_order_expenses_order ON order_expenses(order_id);
CREATE INDEX IF NOT EXISTS idx_order_expenses_date ON order_expenses(spent_at);