后端: - 新增 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)
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""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()
|