feat: 项目流水拆分为确收与回款+费用明细两Tab + 数据迁移

后端:
- 新增 expense_data TEXT 列(columns.py迁移)
- 新增 data_split 迁移:旧 budget_data cost/paid → expense_data(幂等)
- sum_budget cost/paid → sum_expense cost/paid(routes.py/helpers.py)
- TABLES 配置加入 expense_data

前端:
- 项目详情弹窗Tab:基本信息→确收与回款→费用明细→执行信息→任务管理→活动
- 确收与回款Tab:月份/确收/毛利/回款/备注
- 费用明细Tab:月份/费用类型/应付/已付/备注
- 项目表格三视图(月/季/总)cost/paid来源切换为expense_data
- 保存逻辑分两路: budget_data(rev/gross/payment)+expense_data(cost/paid)
This commit is contained in:
mac
2026-07-06 21:34:48 +08:00
parent dbc98e4693
commit f44f7eab9e
6 changed files with 275 additions and 89 deletions

View File

@@ -0,0 +1,56 @@
"""migrations/data_split.py — 预算数据拆分迁移budget_data → expense_data
旧结构budget_data = [{month, rev, gross, payment, cost, paid}]
新结构expense_data = [{month, expense_type, cost, paid, exp_note}]
部署时自动:检测未拆分的旧数据,将 cost/paid 从 budget_data 迁移到 expense_data。
幂等:只处理 expense_data 为空的记录。
"""
def migrate_data_split():
"""一次性数据拆分迁移"""
from db import db, _exec
import json
conn = db()
try:
# 查询未迁移的记录
cur = conn.cursor(dictionary=True)
cur.execute(
"SELECT id, budget_data, expense_data FROM project_finances "
"WHERE (expense_data IS NULL OR expense_data = '' OR expense_data = '[]') "
"AND budget_data IS NOT NULL AND budget_data != '' AND budget_data != '[]'"
)
rows = cur.fetchall()
cur.close()
migrated = 0
for row in rows:
try:
budget = json.loads(row.get("budget_data") or "[]")
except (json.JSONDecodeError, TypeError):
continue
expense_rows = []
for b in budget:
expense_rows.append({
"month": b.get("month", ""),
"expense_type": "",
"cost": float(b.get("cost") or 0),
"paid": float(b.get("paid") or 0),
"exp_note": "",
})
if expense_rows:
_exec(
conn,
"UPDATE project_finances SET expense_data = ? WHERE id = ?",
(json.dumps(expense_rows, ensure_ascii=False), row["id"]),
)
migrated += 1
conn.commit()
print(f"[migrate] 数据拆分迁移完成:{migrated} 条记录已从 budget_data 提取 cost/paid 到 expense_data")
finally:
conn.close()