feat: 总览工作台新增损益/现金流/项目经营详细报表
- 预算总览和发生总览各新增3个tab: 损益详细报表、现金流详细报表、项目经营详细报表 - 12个月卡片布局, 按部门(学术/科普/科研/医患/MCN)展示矩阵 - 后端新增6个聚合API: dept-matrix/cf-matrix/project-matrix x 2 - tab改为finance-tab风格, 与经营月报一致 - 修复项目报表函数未注册导致的加载失败 - 版本号 v1.2.0.26
This commit is contained in:
@@ -724,6 +724,359 @@ def delete_file(file_id):
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
# ---------- 总览部门损益矩阵 API ----------
|
||||
|
||||
@bp.route("/api/overview/dept-matrix")
|
||||
@login_required
|
||||
def overview_dept_matrix():
|
||||
"""返回各工作台的损益数据矩阵(按年/季/月筛选)"""
|
||||
conn = db()
|
||||
_year = int(request.args.get("year", date.today().year))
|
||||
view = request.args.get("view", "monthly")
|
||||
|
||||
# 12个月数组
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
|
||||
tenants = ["学术·无界", "科普·无界", "科研·无界", "医患·无界", "MCN·无界"]
|
||||
result = {"tenants": tenants, "months": months, "data": {}, "total": {"project_gross": [0]*12, "dept_expense": [0]*12, "profit": [0]*12}}
|
||||
|
||||
for tenant in tenants:
|
||||
# 项目毛利(从 budget_data JSON)
|
||||
pfs = rows(conn, "SELECT budget_data FROM plan_finances WHERE tenant=?", [tenant])
|
||||
gross = [0]*12
|
||||
for pf in pfs:
|
||||
try:
|
||||
bd = json.loads(pf.get("budget_data") or "[]")
|
||||
except:
|
||||
bd = []
|
||||
for b in bd:
|
||||
m = (b.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
gross[i] += float(b.get("gross") or 0)
|
||||
|
||||
# 部门费用(从 plan_expense_records)
|
||||
expense = [0]*12
|
||||
exps = rows(conn, "SELECT expense_month, amount FROM plan_expense_records WHERE tenant=? AND expense_month LIKE %s", [tenant, f"{_year}-%"])
|
||||
for e in exps:
|
||||
m = (e.get("expense_month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
expense[i] += float(e.get("amount") or 0)
|
||||
|
||||
profit = [gross[i] - expense[i] for i in range(12)]
|
||||
|
||||
result["data"][tenant] = {
|
||||
"project_gross": gross,
|
||||
"dept_expense": expense,
|
||||
"profit": profit
|
||||
}
|
||||
|
||||
for i in range(12):
|
||||
result["total"]["project_gross"][i] += gross[i]
|
||||
result["total"]["dept_expense"][i] += expense[i]
|
||||
result["total"]["profit"][i] += profit[i]
|
||||
|
||||
# 按视图聚合
|
||||
if view == "quarterly":
|
||||
def to_quarterly(arr12):
|
||||
qr = [(0,1,2),(3,4,5),(6,7,8),(9,10,11)]
|
||||
return [arr12[q[0]]+arr12[q[1]]+arr12[q[2]] for q in qr]
|
||||
result["months"] = ["Q1","Q2","Q3","Q4"]
|
||||
for t in tenants:
|
||||
for key in ["project_gross","dept_expense","profit"]:
|
||||
result["data"][t][key] = to_quarterly(result["data"][t][key])
|
||||
for key in ["project_gross","dept_expense","profit"]:
|
||||
result["total"][key] = to_quarterly(result["total"][key])
|
||||
elif view == "monthly":
|
||||
sel_month = request.args.get("month", "")
|
||||
if sel_month and sel_month in months:
|
||||
mi = months.index(sel_month)
|
||||
result["months"] = [sel_month]
|
||||
for t in tenants:
|
||||
for key in ["project_gross","dept_expense","profit"]:
|
||||
result["data"][t][key] = [result["data"][t][key][mi]]
|
||||
for key in ["project_gross","dept_expense","profit"]:
|
||||
result["total"][key] = [result["total"][key][mi]]
|
||||
# else: yearly (default) — keep all 12 months
|
||||
|
||||
# Compute annual totals
|
||||
result["annual"] = {}
|
||||
for t in tenants:
|
||||
d = result["data"][t]
|
||||
result["annual"][t] = {
|
||||
"project_gross": sum(d["project_gross"]),
|
||||
"dept_expense": sum(d["dept_expense"]),
|
||||
"profit": sum(d["profit"])
|
||||
}
|
||||
result["annual"]["total"] = {
|
||||
"project_gross": sum(result["total"]["project_gross"]),
|
||||
"dept_expense": sum(result["total"]["dept_expense"]),
|
||||
"profit": sum(result["total"]["profit"])
|
||||
}
|
||||
|
||||
conn.close()
|
||||
return jsonify(result)
|
||||
|
||||
@bp.route("/api/overview/finance-dept-matrix")
|
||||
@login_required
|
||||
def overview_finance_dept_matrix():
|
||||
"""返回各工作台已发生损益数据矩阵"""
|
||||
conn = db()
|
||||
_year = date.today().year
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
tenants = ["学术·无界", "科普·无界", "科研·无界", "医患·无界", "MCN·无界"]
|
||||
result = {"tenants": tenants, "months": months, "data": {}, "total": {"project_gross": [0]*12, "dept_expense": [0]*12, "profit": [0]*12}}
|
||||
|
||||
for tenant in tenants:
|
||||
pfs = rows(conn, "SELECT budget_data FROM project_finances WHERE tenant=?", [tenant])
|
||||
gross = [0]*12
|
||||
for pf in pfs:
|
||||
try:
|
||||
bd = json.loads(pf.get("budget_data") or "[]")
|
||||
except:
|
||||
bd = []
|
||||
for b in bd:
|
||||
m = (b.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
gross[i] += float(b.get("gross") or 0)
|
||||
|
||||
expense = [0]*12
|
||||
exps = rows(conn, "SELECT expense_month, amount FROM expense_records WHERE tenant=? AND expense_month LIKE %s", [tenant, f"{_year}-%"])
|
||||
for e in exps:
|
||||
m = (e.get("expense_month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
expense[i] += float(e.get("amount") or 0)
|
||||
|
||||
profit = [gross[i] - expense[i] for i in range(12)]
|
||||
result["data"][tenant] = {"project_gross": gross, "dept_expense": expense, "profit": profit}
|
||||
for i in range(12):
|
||||
result["total"]["project_gross"][i] += gross[i]
|
||||
result["total"]["dept_expense"][i] += expense[i]
|
||||
result["total"]["profit"][i] += profit[i]
|
||||
|
||||
result["annual"] = {}
|
||||
for t in tenants:
|
||||
d = result["data"][t]
|
||||
result["annual"][t] = {"project_gross": sum(d["project_gross"]), "dept_expense": sum(d["dept_expense"]), "profit": sum(d["profit"])}
|
||||
result["annual"]["total"] = {"project_gross": sum(result["total"]["project_gross"]), "dept_expense": sum(result["total"]["dept_expense"]), "profit": sum(result["total"]["profit"])}
|
||||
|
||||
conn.close()
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@bp.route("/api/overview/dept-cf-matrix")
|
||||
@login_required
|
||||
def overview_dept_cf_matrix():
|
||||
"""返回各工作台预算现金流数据矩阵"""
|
||||
conn = db()
|
||||
_year = date.today().year
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
tenants = ["学术·无界", "科普·无界", "科研·无界", "医患·无界", "MCN·无界"]
|
||||
result = {"tenants": tenants, "months": months, "data": {}, "total": {"project_cf": [0]*12, "dept_cf": [0]*12, "bl_cf": [0]*12}}
|
||||
|
||||
for tenant in tenants:
|
||||
pfs = rows(conn, "SELECT budget_data, expense_data FROM plan_finances WHERE tenant=?", [tenant])
|
||||
project_cf = [0]*12
|
||||
for pf in pfs:
|
||||
try: bd = json.loads(pf.get("budget_data") or "[]")
|
||||
except: bd = []
|
||||
try: ed = json.loads(pf.get("expense_data") or "[]")
|
||||
except: ed = []
|
||||
for b in bd:
|
||||
m = (b.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
project_cf[i] += float(b.get("payment") or 0)
|
||||
for e in ed:
|
||||
m = (e.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
project_cf[i] -= float(e.get("paid") or 0)
|
||||
|
||||
dept_cf = [0]*12
|
||||
exps = rows(conn, "SELECT expense_month, incurred_amount FROM plan_expense_records WHERE tenant=? AND expense_month LIKE %s", [tenant, f"{_year}-%"])
|
||||
for e in exps:
|
||||
m = (e.get("expense_month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
dept_cf[i] += float(e.get("incurred_amount") or 0)
|
||||
|
||||
bl_cf = [project_cf[i] - dept_cf[i] for i in range(12)]
|
||||
result["data"][tenant] = {"project_cf": project_cf, "dept_cf": dept_cf, "bl_cf": bl_cf}
|
||||
for i in range(12):
|
||||
result["total"]["project_cf"][i] += project_cf[i]
|
||||
result["total"]["dept_cf"][i] += dept_cf[i]
|
||||
result["total"]["bl_cf"][i] += bl_cf[i]
|
||||
|
||||
result["annual"] = {}
|
||||
for t in tenants:
|
||||
d = result["data"][t]
|
||||
result["annual"][t] = {"project_cf": sum(d["project_cf"]), "dept_cf": sum(d["dept_cf"]), "bl_cf": sum(d["bl_cf"])}
|
||||
result["annual"]["total"] = {"project_cf": sum(result["total"]["project_cf"]), "dept_cf": sum(result["total"]["dept_cf"]), "bl_cf": sum(result["total"]["bl_cf"])}
|
||||
|
||||
conn.close()
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@bp.route("/api/overview/finance-dept-cf-matrix")
|
||||
@login_required
|
||||
def overview_finance_dept_cf_matrix():
|
||||
"""返回各工作台已发生现金流数据矩阵"""
|
||||
conn = db()
|
||||
_year = date.today().year
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
tenants = ["学术·无界", "科普·无界", "科研·无界", "医患·无界", "MCN·无界"]
|
||||
result = {"tenants": tenants, "months": months, "data": {}, "total": {"project_cf": [0]*12, "dept_cf": [0]*12, "bl_cf": [0]*12}}
|
||||
|
||||
for tenant in tenants:
|
||||
pfs = rows(conn, "SELECT budget_data, expense_data FROM project_finances WHERE tenant=?", [tenant])
|
||||
project_cf = [0]*12
|
||||
for pf in pfs:
|
||||
try: bd = json.loads(pf.get("budget_data") or "[]")
|
||||
except: bd = []
|
||||
try: ed = json.loads(pf.get("expense_data") or "[]")
|
||||
except: ed = []
|
||||
for b in bd:
|
||||
m = (b.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
project_cf[i] += float(b.get("payment") or 0)
|
||||
for e in ed:
|
||||
m = (e.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
project_cf[i] -= float(e.get("paid") or 0)
|
||||
|
||||
dept_cf = [0]*12
|
||||
exps = rows(conn, "SELECT expense_month, incurred_amount FROM expense_records WHERE tenant=? AND expense_month LIKE %s", [tenant, f"{_year}-%"])
|
||||
for e in exps:
|
||||
m = (e.get("expense_month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
dept_cf[i] += float(e.get("incurred_amount") or 0)
|
||||
|
||||
bl_cf = [project_cf[i] - dept_cf[i] for i in range(12)]
|
||||
result["data"][tenant] = {"project_cf": project_cf, "dept_cf": dept_cf, "bl_cf": bl_cf}
|
||||
for i in range(12):
|
||||
result["total"]["project_cf"][i] += project_cf[i]
|
||||
result["total"]["dept_cf"][i] += dept_cf[i]
|
||||
result["total"]["bl_cf"][i] += bl_cf[i]
|
||||
|
||||
result["annual"] = {}
|
||||
for t in tenants:
|
||||
d = result["data"][t]
|
||||
result["annual"][t] = {"project_cf": sum(d["project_cf"]), "dept_cf": sum(d["dept_cf"]), "bl_cf": sum(d["bl_cf"])}
|
||||
result["annual"]["total"] = {"project_cf": sum(result["total"]["project_cf"]), "dept_cf": sum(result["total"]["dept_cf"]), "bl_cf": sum(result["total"]["bl_cf"])}
|
||||
|
||||
conn.close()
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@bp.route("/api/overview/dept-project-matrix")
|
||||
@login_required
|
||||
def overview_dept_project_matrix():
|
||||
"""返回各工作台预算项目经营详情矩阵"""
|
||||
conn = db()
|
||||
_year = date.today().year
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
tenants = ["学术·无界", "科普·无界", "科研·无界", "医患·无界", "MCN·无界"]
|
||||
keys = ["sign","revenue","gross","cost","paid","payment","cashflow"]
|
||||
result = {"tenants": tenants, "months": months, "data": {}, "total": {k: [0]*12 for k in keys}}
|
||||
|
||||
for tenant in tenants:
|
||||
tdata = {k: [0]*12 for k in keys}
|
||||
pfs = rows(conn, "SELECT sign_amount, sign_month, budget_data, expense_data FROM plan_finances WHERE tenant=?", [tenant])
|
||||
for pf in pfs:
|
||||
sm = (pf.get("sign_month") or "")[:7]
|
||||
if sm in months:
|
||||
i = months.index(sm)
|
||||
tdata["sign"][i] += float(pf.get("sign_amount") or 0)
|
||||
try: bd = json.loads(pf.get("budget_data") or "[]")
|
||||
except: bd = []
|
||||
try: ed = json.loads(pf.get("expense_data") or "[]")
|
||||
except: ed = []
|
||||
for b in bd:
|
||||
m = (b.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
tdata["revenue"][i] += float(b.get("rev") or 0)
|
||||
tdata["gross"][i] += float(b.get("gross") or 0)
|
||||
tdata["payment"][i] += float(b.get("payment") or 0)
|
||||
for e in ed:
|
||||
m = (e.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
tdata["cost"][i] += float(e.get("cost") or 0)
|
||||
tdata["paid"][i] += float(e.get("paid") or 0)
|
||||
for i in range(12):
|
||||
tdata["cashflow"][i] = tdata["payment"][i] - tdata["paid"][i]
|
||||
result["data"][tenant] = tdata
|
||||
for k in keys:
|
||||
for i in range(12):
|
||||
result["total"][k][i] += tdata[k][i]
|
||||
|
||||
result["annual"] = {}
|
||||
for t in tenants:
|
||||
result["annual"][t] = {k: sum(result["data"][t][k]) for k in keys}
|
||||
result["annual"]["total"] = {k: sum(result["total"][k]) for k in keys}
|
||||
conn.close()
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@bp.route("/api/overview/finance-dept-project-matrix")
|
||||
@login_required
|
||||
def overview_finance_dept_project_matrix():
|
||||
"""返回各工作台已发生项目经营详情矩阵"""
|
||||
conn = db()
|
||||
_year = date.today().year
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
tenants = ["学术·无界", "科普·无界", "科研·无界", "医患·无界", "MCN·无界"]
|
||||
keys = ["sign","revenue","gross","cost","paid","payment","cashflow"]
|
||||
result = {"tenants": tenants, "months": months, "data": {}, "total": {k: [0]*12 for k in keys}}
|
||||
|
||||
for tenant in tenants:
|
||||
tdata = {k: [0]*12 for k in keys}
|
||||
pfs = rows(conn, "SELECT sign_amount, sign_month, budget_data, expense_data FROM project_finances WHERE tenant=?", [tenant])
|
||||
for pf in pfs:
|
||||
sm = (pf.get("sign_month") or "")[:7]
|
||||
if sm in months:
|
||||
i = months.index(sm)
|
||||
tdata["sign"][i] += float(pf.get("sign_amount") or 0)
|
||||
try: bd = json.loads(pf.get("budget_data") or "[]")
|
||||
except: bd = []
|
||||
try: ed = json.loads(pf.get("expense_data") or "[]")
|
||||
except: ed = []
|
||||
for b in bd:
|
||||
m = (b.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
tdata["revenue"][i] += float(b.get("rev") or 0)
|
||||
tdata["gross"][i] += float(b.get("gross") or 0)
|
||||
tdata["payment"][i] += float(b.get("payment") or 0)
|
||||
for e in ed:
|
||||
m = (e.get("month") or "")[:7]
|
||||
if m in months:
|
||||
i = months.index(m)
|
||||
tdata["cost"][i] += float(e.get("cost") or 0)
|
||||
tdata["paid"][i] += float(e.get("paid") or 0)
|
||||
for i in range(12):
|
||||
tdata["cashflow"][i] = tdata["payment"][i] - tdata["paid"][i]
|
||||
result["data"][tenant] = tdata
|
||||
for k in keys:
|
||||
for i in range(12):
|
||||
result["total"][k][i] += tdata[k][i]
|
||||
|
||||
result["annual"] = {}
|
||||
for t in tenants:
|
||||
result["annual"][t] = {k: sum(result["data"][t][k]) for k in keys}
|
||||
result["annual"]["total"] = {k: sum(result["total"][k]) for k in keys}
|
||||
conn.close()
|
||||
return jsonify(result)
|
||||
|
||||
# ---------- health ----------
|
||||
|
||||
@bp.route("/api/health")
|
||||
|
||||
Reference in New Issue
Block a user