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:
@@ -23,7 +23,7 @@ TABLES = {
|
||||
"products": ("product_versions", ["product_name", "version", "version_goal", "priority", "start_date", "plan_date", "dev_done_date", "test_date", "launch_date", "status", "notes", "tenant"]),
|
||||
"finance": ("finance_records", ["month", "project_name", "record_type", "category", "amount", "occurred_date", "notes", "tenant"]),
|
||||
"tasks": ("project_tasks", ["project_id", "phase", "milestone", "task", "owner", "due_date", "blockers", "notes", "status", "sort_order", "priority", "tenant"]),
|
||||
"projectFinances": ("project_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "status", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]),
|
||||
"projectFinances": ("project_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "status", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]),
|
||||
"expense": ("expense_records", ["expense_type", "expense_month", "amount", "incurred_amount", "notes", "tenant"]),
|
||||
}
|
||||
|
||||
@@ -266,6 +266,26 @@ def bootstrap():
|
||||
if b:
|
||||
total += float(b.get(field) or 0)
|
||||
return total
|
||||
|
||||
def t_parse_expense(pf):
|
||||
try:
|
||||
ed = json.loads(pf.get("expense_data") or "[]")
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
ed = []
|
||||
return {(e.get("month") or "").replace("-", "_"): e for e in ed}
|
||||
|
||||
t_em = {pf["id"]: t_parse_expense(pf) for pf in t_pfs}
|
||||
|
||||
def t_sum_expense(field, months_range):
|
||||
total = 0
|
||||
for pf in t_pfs:
|
||||
em = t_em.get(pf["id"], {})
|
||||
for m in months_range:
|
||||
e = em.get(f"2026_{m:02d}")
|
||||
if e:
|
||||
total += float(e.get(field) or 0)
|
||||
return total
|
||||
|
||||
_now_month = date.today().month
|
||||
_q_start = ((_now_month - 1) // 3) * 3 + 1
|
||||
_q_range = range(_q_start, _q_start + 3)
|
||||
@@ -300,11 +320,11 @@ def bootstrap():
|
||||
"payment_month": t_sum_budget("payment", [_now_month]),
|
||||
"payment_prev_q": t_sum_budget("payment", _prev_q_range),
|
||||
"payment_prev_month": t_sum_budget("payment", [_prev_month]) if _prev_month else 0,
|
||||
"cost_annual": t_sum_budget("cost", range(1, 13)),
|
||||
"cost_q2": t_sum_budget("cost", _q_range),
|
||||
"cost_month": t_sum_budget("cost", [_now_month]),
|
||||
"cost_prev_q": t_sum_budget("cost", _prev_q_range),
|
||||
"cost_prev_month": t_sum_budget("cost", [_prev_month]) if _prev_month else 0,
|
||||
"cost_annual": t_sum_expense("cost", range(1, 13)),
|
||||
"cost_q2": t_sum_expense("cost", _q_range),
|
||||
"cost_month": t_sum_expense("cost", [_now_month]),
|
||||
"cost_prev_q": t_sum_expense("cost", _prev_q_range),
|
||||
"cost_prev_month": t_sum_expense("cost", [_prev_month]) if _prev_month else 0,
|
||||
})
|
||||
# 费用汇总(从 expense_records 表计算)
|
||||
def t_expense_sum(m_range):
|
||||
@@ -322,11 +342,11 @@ def bootstrap():
|
||||
all_metrics[-1]["expense_month"] = t_expense_sum([_now_month])
|
||||
all_metrics[-1]["expense_prev_q"] = t_expense_sum(_prev_q_range)
|
||||
all_metrics[-1]["expense_prev_month"] = t_expense_sum([_prev_month]) if _prev_month else 0
|
||||
all_metrics[-1]["paid_annual"] = t_sum_budget("paid", range(1, 13))
|
||||
all_metrics[-1]["paid_q2"] = t_sum_budget("paid", _q_range)
|
||||
all_metrics[-1]["paid_month"] = t_sum_budget("paid", [_now_month])
|
||||
all_metrics[-1]["paid_prev_q"] = t_sum_budget("paid", _prev_q_range)
|
||||
all_metrics[-1]["paid_prev_month"] = t_sum_budget("paid", [_prev_month]) if _prev_month else 0
|
||||
all_metrics[-1]["paid_annual"] = t_sum_expense("paid", range(1, 13))
|
||||
all_metrics[-1]["paid_q2"] = t_sum_expense("paid", _q_range)
|
||||
all_metrics[-1]["paid_month"] = t_sum_expense("paid", [_now_month])
|
||||
all_metrics[-1]["paid_prev_q"] = t_sum_expense("paid", _prev_q_range)
|
||||
all_metrics[-1]["paid_prev_month"] = t_sum_expense("paid", [_prev_month]) if _prev_month else 0
|
||||
all_metrics[-1]["proj_expense_annual"] = t_sum_budget("expense", range(1, 13))
|
||||
all_metrics[-1]["proj_expense_q2"] = t_sum_budget("expense", _q_range)
|
||||
all_metrics[-1]["proj_expense_month"] = t_sum_budget("expense", [_now_month])
|
||||
@@ -382,6 +402,15 @@ def bootstrap():
|
||||
|
||||
budget_maps = [(pf, parse_budget(pf)) for pf in signed_pfs]
|
||||
|
||||
def parse_expense(pf):
|
||||
try:
|
||||
ed = json.loads(pf.get("expense_data") or "[]")
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
ed = []
|
||||
return {(e.get("month") or "").replace("-", "_"): e for e in ed}
|
||||
|
||||
expense_maps = [(pf, parse_expense(pf)) for pf in signed_pfs]
|
||||
|
||||
def sum_budget(field, months_range):
|
||||
total = 0
|
||||
for pf, bm in budget_maps:
|
||||
@@ -391,6 +420,15 @@ def bootstrap():
|
||||
total += float(b.get(field) or 0)
|
||||
return total
|
||||
|
||||
def sum_expense(field, months_range):
|
||||
total = 0
|
||||
for pf, em in expense_maps:
|
||||
for m in months_range:
|
||||
e = em.get(f"2026_{m:02d}")
|
||||
if e:
|
||||
total += float(e.get(field) or 0)
|
||||
return total
|
||||
|
||||
_now_month = date.today().month
|
||||
_q_start = ((_now_month - 1) // 3) * 3 + 1
|
||||
_q_range = range(_q_start, _q_start + 3)
|
||||
@@ -408,15 +446,15 @@ def bootstrap():
|
||||
rev_prev_month = sum_budget("rev", [_prev_month]) if _prev_month else 0
|
||||
gross_prev_month = sum_budget("gross", [_prev_month]) if _prev_month else 0
|
||||
payment_annual = sum_budget("payment", range(1, 13))
|
||||
cost_annual = sum_budget("cost", range(1, 13))
|
||||
cost_annual = sum_expense("cost", range(1, 13))
|
||||
payment_q2 = sum_budget("payment", _q_range)
|
||||
cost_q2 = sum_budget("cost", _q_range)
|
||||
cost_q2 = sum_expense("cost", _q_range)
|
||||
payment_month = sum_budget("payment", [_now_month])
|
||||
cost_month = sum_budget("cost", [_now_month])
|
||||
cost_month = sum_expense("cost", [_now_month])
|
||||
payment_prev_q = sum_budget("payment", _prev_q_range)
|
||||
cost_prev_q = sum_budget("cost", _prev_q_range)
|
||||
cost_prev_q = sum_expense("cost", _prev_q_range)
|
||||
payment_prev_month = sum_budget("payment", [_prev_month]) if _prev_month else 0
|
||||
cost_prev_month = sum_budget("cost", [_prev_month]) if _prev_month else 0
|
||||
cost_prev_month = sum_expense("cost", [_prev_month]) if _prev_month else 0
|
||||
def expense_sum(month_range):
|
||||
total = 0
|
||||
for r in expense:
|
||||
@@ -432,11 +470,11 @@ def bootstrap():
|
||||
expense_month_val = expense_sum([_now_month])
|
||||
expense_prev_q = expense_sum(_prev_q_range)
|
||||
expense_prev_month = expense_sum([_prev_month]) if _prev_month else 0
|
||||
paid_annual = sum_budget("paid", range(1, 13))
|
||||
paid_q2 = sum_budget("paid", _q_range)
|
||||
paid_month = sum_budget("paid", [_now_month])
|
||||
paid_prev_q = sum_budget("paid", _prev_q_range)
|
||||
paid_prev_month = sum_budget("paid", [_prev_month]) if _prev_month else 0
|
||||
paid_annual = sum_expense("paid", range(1, 13))
|
||||
paid_q2 = sum_expense("paid", _q_range)
|
||||
paid_month = sum_expense("paid", [_now_month])
|
||||
paid_prev_q = sum_expense("paid", _prev_q_range)
|
||||
paid_prev_month = sum_expense("paid", [_prev_month]) if _prev_month else 0
|
||||
proj_expense_annual = sum_budget("expense", range(1, 13))
|
||||
proj_expense_q2 = sum_budget("expense", _q_range)
|
||||
proj_expense_month = sum_budget("expense", [_now_month])
|
||||
|
||||
Reference in New Issue
Block a user