diff --git a/backend/flask_app.py b/backend/flask_app.py index 9b8d96f..e39ccee 100644 --- a/backend/flask_app.py +++ b/backend/flask_app.py @@ -352,8 +352,19 @@ def bootstrap(): cost_q2 = sum_finance(months_q2, "cost_expense") revenue_month = sum_finance([current_month], "revenue") cost_month = sum_finance([current_month], "cost_expense") - # Contract aggregates + # Contract aggregates — time-based signed_amount = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_status"] == "已签约") + from datetime import date + today = date.today() + def contract_in_period(op, start, end): + if op["project_status"] != "已签约": return False + try: + d = date.fromisoformat(op["created_at"][:10]) + return start <= d <= end + except: return False + signed_annual = sum(x["expected_contract_amount"] or 0 for x in operations if contract_in_period(x, date(2026,1,1), date(2026,12,31))) + signed_q2 = sum(x["expected_contract_amount"] or 0 for x in operations if contract_in_period(x, date(2026,4,1), date(2026,6,30))) + signed_month = sum(x["expected_contract_amount"] or 0 for x in operations if contract_in_period(x, date(2026,6,1), date(2026,6,30))) pipeline_amount = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_status"] not in ["已签约","已丢单","已归档","已完成"]) signed_not_executed = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_type"] == "execution" and x["execution_progress"] < 100) summary = { @@ -368,6 +379,9 @@ def bootstrap(): "upcoming_products": len([x for x in products if x["status"] in ["规划中", "设计中", "开发中", "测试中"]]), # Extended finance metrics "signed_amount": signed_amount, + "signed_annual": signed_annual, + "signed_q2": signed_q2, + "signed_month": signed_month, "pipeline_amount": pipeline_amount, "revenue_annual": revenue_annual, "revenue_q2": revenue_q2, diff --git a/static/app.js b/static/app.js index 0a7bacf..d7b94ac 100644 --- a/static/app.js +++ b/static/app.js @@ -93,7 +93,25 @@ function render() { function renderHome() { const { summary, financeMonthly } = state.data; - const m = summary.metrics; + const rows1 = [ + ["年度累计签约", money(m.signed_annual || m.signed_amount)], + ["Q2 累计签约", money(m.signed_q2 || 0)], + ["本月新增签约", money(m.signed_month || 0)], + ["合同流程中", money(m.pipeline_amount)], + ]; + const rows2 = [ + ["年度累计确收", money(m.revenue_annual)], + ["Q2 累计确收", money(m.revenue_q2)], + ["本月新增确收", money(m.monthly_revenue)], + ["已签约未执行", money(m.signed_not_executed)], + ]; + const rows3 = [ + ["年度累计毛利", money(m.gross_annual)], + ["Q2 累计毛利", money(m.gross_q2)], + ["本月新增毛利", money(m.monthly_net_profit)], + ["合同毛利率", m.revenue_annual ? Math.round(m.gross_annual / m.revenue_annual * 100) + "%" : "—"], + ]; + const tblCard = (title, rows) => card(`
| ${label} | ${value} |
${r.title}
${r.content}