diff --git a/backend/flask_app.py b/backend/flask_app.py
index 69ba75b..2bf15c9 100644
--- a/backend/flask_app.py
+++ b/backend/flask_app.py
@@ -685,7 +685,7 @@ def attach_common(conn, resource, items):
def monthly_finance(conn, tenant="科普·无界"):
months = [f"2026-{m:02d}" for m in range(1, 13)]
pfs = rows(conn,
- "SELECT sign_amount, sign_month, status, budget_data FROM project_finances WHERE tenant=?",
+ "SELECT sign_amount, sign_month, status, budget_data FROM project_finances WHERE tenant=? AND status='已签约'",
[tenant])
# 预解析 budget_data:{pf_index: {month_key: {rev, gross, payment, cost}}}
@@ -722,7 +722,7 @@ def monthly_finance(conn, tenant="科普·无界"):
data.append({
"month": month, "revenue": revenue,
"labor": 0, "expense": 0, "purchase": 0,
- "net_profit": gross,
+ "gross": gross,
"sign": sign, "payment": payment, "cost": cost,
})
return data
@@ -777,22 +777,33 @@ def bootstrap():
total += float(b.get(field) or 0)
return total
+ # 本季度月份范围(Q1=1-3, Q2=4-6, Q3=7-9, Q4=10-12),基于当前月
+ _now_month = date.today().month
+ _q_start = ((_now_month - 1) // 3) * 3 + 1
+ _q_range = range(_q_start, _q_start + 3)
rev_annual = sum_budget("rev", range(1, 13))
gross_annual = sum_budget("gross", range(1, 13))
- rev_q2 = sum_budget("rev", range(4, 7))
- gross_q2 = sum_budget("gross", range(4, 7))
- rev_month = sum_budget("rev", [6])
- gross_month = sum_budget("gross", [6])
+ rev_q2 = sum_budget("rev", _q_range)
+ gross_q2 = sum_budget("gross", _q_range)
+ rev_month = sum_budget("rev", [_now_month])
+ gross_month = sum_budget("gross", [_now_month])
+ payment_annual = sum_budget("payment", range(1, 13))
+ cost_annual = sum_budget("cost", range(1, 13))
+ payment_q2 = sum_budget("payment", _q_range)
+ cost_q2 = sum_budget("cost", _q_range)
+ payment_month = sum_budget("payment", [_now_month])
+ cost_month = sum_budget("cost", [_now_month])
# Contract aggregates — from project_finances (经营管理项目)
def pf_status_sum(status):
return sum(x["sign_amount"] or 0 for x in pfs if x["status"] == status)
signed_amount = pf_status_sum("已签约")
# 年度签约 = 所有已签约项目 2026 年的签约金额
signed_annual = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约")
- # Q2 签约 = 签约月份在 2026-04~2026-06 的已签约项目
- signed_q2 = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in ["2026-04","2026-05","2026-06"])
- # 本月签约 = 签约月份为 2026-06 的已签约项目
- signed_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "") == "2026-06")
+ # 本季度签约 = 签约月份在当前季度的已签约项目
+ _q_months = [f"2026-{m:02d}" for m in _q_range]
+ signed_q2 = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _q_months)
+ # 本月签约 = 签约月份为当月的已签约项目
+ signed_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"2026-{_now_month:02d}")
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 = {
@@ -819,6 +830,12 @@ def bootstrap():
"revenue_q2": rev_q2,
"gross_annual": gross_annual,
"gross_q2": gross_q2,
+ "payment_annual": payment_annual,
+ "payment_q2": payment_q2,
+ "payment_month": payment_month,
+ "cost_annual": cost_annual,
+ "cost_q2": cost_q2,
+ "cost_month": cost_month,
"signed_not_executed": signed_not_executed,
},
"recent": q("SELECT * FROM follow_up_records WHERE tenant=? ORDER BY id DESC LIMIT 8", tenant),
diff --git a/static/modules/home.js b/static/modules/home.js
index 9932ddb..f809dea 100644
--- a/static/modules/home.js
+++ b/static/modules/home.js
@@ -3,22 +3,33 @@
function renderHome() {
const { summary, financeMonthly } = state.data;
const m = summary.metrics;
+ const moneyInt = (v) => `${Math.round(Number(v || 0)).toLocaleString("zh-CN")} 元`;
const rows1 = [
- ["年度累计签约", money(m.signed_annual || m.signed_amount)],
- ["Q2 累计签约", money(m.signed_q2 || 0)],
- ["本月新增签约", money(m.signed_month || 0)],
+ ["年度累计", moneyInt(m.signed_annual || m.signed_amount)],
+ ["季度累计", moneyInt(m.signed_q2 || 0)],
+ ["本月新增", moneyInt(m.signed_month || 0)],
];
const rows2 = [
- ["年度累计确收", money(m.revenue_annual)],
- ["Q2 累计确收", money(m.revenue_q2)],
- ["本月新增确收", money(m.monthly_revenue)],
+ ["年度累计", moneyInt(m.revenue_annual)],
+ ["季度累计", moneyInt(m.revenue_q2)],
+ ["本月新增", moneyInt(m.monthly_revenue)],
];
const rows3 = [
- ["年度累计毛利", money(m.gross_annual)],
- ["Q2 累计毛利", money(m.gross_q2)],
- ["本月新增毛利", money(m.monthly_net_profit)],
+ ["年度累计", moneyInt(m.gross_annual)],
+ ["季度累计", moneyInt(m.gross_q2)],
+ ["本月新增", moneyInt(m.monthly_net_profit)],
];
- const tblCard = (title, rows) => card(`
${title}
${rows.map(([label, value]) => `| ${label} | ${value} |
`).join("")}
`, "p-4");
+ const rows4 = [
+ ["年度累计", moneyInt(m.payment_annual || 0)],
+ ["季度累计", moneyInt(m.payment_q2 || 0)],
+ ["本月新增", moneyInt(m.payment_month || 0)],
+ ];
+ const rows5 = [
+ ["年度累计", moneyInt(m.cost_annual || 0)],
+ ["季度累计", moneyInt(m.cost_q2 || 0)],
+ ["本月新增", moneyInt(m.cost_month || 0)],
+ ];
+const tblCard = (title, rows) => card(`${title}
${rows.map(([label, value]) => `| ${label} | ${value} |
`).join("")}
`, "p-4");
document.querySelector("#home").innerHTML = `
@@ -29,13 +40,13 @@ function renderHome() {
["产品迭代", m.upcoming_products, "products"],
].map(([label, value, tab]) => ``).join("")}
-
${tblCard("合同金额", rows1)}${tblCard("确收金额", rows2)}${tblCard("确收毛利", rows3)}
+
${tblCard("合同金额", rows1)}${tblCard("确收金额", rows2)}${tblCard("确收毛利", rows3)}${tblCard("回款金额", rows4)}${tblCard("费用金额", rows5)}
${card(`
月度签约趋势
2026`, "p-4")}
${card(`
月度确收与毛利
2026`, "p-4")}
${card(`
月度回款与费用
2026`, "p-4")}
- ${card(`
近期动态
${summary.recent.map((r) => `
${r.content}${r.followed_at}
`).join("")}
`, "p-5")}
+ ${card(`
近期动态
${summary.recent.map((r) => `
${r.content}${r.followed_at}
`).join("")}
`, "p-5")}
`;
renderCharts(financeMonthly);
@@ -81,7 +92,7 @@ function renderCharts(data) {
type: "line",
data: { labels, datasets: [
{ label: "确收", data: data.map((x) => x.revenue || 0), borderColor: "#2563eb", backgroundColor: "rgba(37,99,235,0.06)", fill: true, tension: 0.3 },
- { label: "毛利", data: data.map((x) => x.net_profit || 0), borderColor: "#059669", backgroundColor: "rgba(5,150,105,0.06)", fill: true, tension: 0.3 },
+ { label: "毛利", data: data.map((x) => x.gross || 0), borderColor: "#059669", backgroundColor: "rgba(5,150,105,0.06)", fill: true, tension: 0.3 },
]},
options: baseOpts,
});