fix(P0): 杜邦分析接口去除硬编码错误数据 — equity 114万→358.4万、ROE 12.38%→3.95%、新增hanke分支读DB(KPI 2026H1 verified)
- 新增 _get_dupont_kpi 辅助函数: 按 entity_id+kpi_code 查 kpi_definitions→kpi_values(period=2026H1, verified) - bohai: 净权益修正为358.4万(来源 bohai_comprehensive_analysis_2026.md 试算平衡表),财务杠杆4.68x→1.49x,负债率78.6%→32.8%结构健康,ROE≈3.94%,删除误导性加杠杆建议 - hanke: 新增分支,从DB读 F_REVENUE=713.27/F_NET_PROFIT=-90.86,净利率-12.74%,资产/权益缺失时factor返回null标注数据待补充,prev_roe返回null(前端兼容显示无对比),接口返回200
This commit is contained in:
+105
-8
@@ -1837,6 +1837,23 @@ def export_statutory_reports(
|
||||
)
|
||||
|
||||
|
||||
def _get_dupont_kpi(db: Session, entity_id: int, kpi_code: str, period: str = "2026H1"):
|
||||
"""杜邦分析 KPI 读取 — 按 entity + kpi_code 查 kpi_definitions → kpi_values
|
||||
取 period 期间 data_status=='verified' 的最新一条(order_by id desc),读不到返回 None"""
|
||||
kpi = db.query(KPIDefinition).filter(
|
||||
KPIDefinition.entity_id == entity_id,
|
||||
KPIDefinition.kpi_code == kpi_code,
|
||||
).first()
|
||||
if not kpi:
|
||||
return None
|
||||
v = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == kpi.id,
|
||||
KPIValue.period == period,
|
||||
KPIValue.data_status == "verified",
|
||||
).order_by(KPIValue.id.desc()).first()
|
||||
return v.actual_value if v else None
|
||||
|
||||
|
||||
@router.get("/dupont")
|
||||
def get_dupont_analysis(
|
||||
entity: str = Query("bohai"),
|
||||
@@ -1844,15 +1861,24 @@ def get_dupont_analysis(
|
||||
):
|
||||
"""杜邦分析 — ROE三级拆解 (CMA P2)"""
|
||||
if entity == "bohai":
|
||||
net_profit = 14.1 # 万
|
||||
revenue = 383 # 万
|
||||
total_assets = 533 # 万
|
||||
equity = 114 # 万
|
||||
# 博海标准KPI(F_REVENUE/F_NET_PROFIT)无verified值 → 优先DB读,读不到回退文档确认常量
|
||||
net_profit = _get_dupont_kpi(db, 2, "F_NET_PROFIT")
|
||||
if net_profit is None:
|
||||
# 来源: bohai_comprehensive_analysis_2026.md 试算平衡表 2026H1(=141,324元)
|
||||
net_profit = 14.13 # 万
|
||||
revenue = _get_dupont_kpi(db, 2, "F_REVENUE")
|
||||
if revenue is None:
|
||||
# 来源: bohai_comprehensive_analysis_2026.md 试算平衡表 2026H1(=3,836,026元)
|
||||
revenue = 383.6 # 万
|
||||
# 来源: bohai_comprehensive_analysis_2026.md 试算平衡表 2026H1
|
||||
total_assets = 533 # 万(DB无对应KPI,文档口径)
|
||||
equity = 358.4 # 万(=3,583,851元)⚠️ 核心修正点:原硬编码114万错误,导致ROE高估约3倍
|
||||
|
||||
net_profit_margin = round(net_profit / revenue * 100, 2)
|
||||
asset_turnover = round(revenue / total_assets, 4)
|
||||
financial_leverage = round(total_assets / equity, 2)
|
||||
roe = round(net_profit_margin / 100 * asset_turnover * financial_leverage * 100, 2)
|
||||
debt_ratio = round((total_assets - equity) / total_assets * 100, 1)
|
||||
|
||||
# 上期对比(模拟上一期数据)
|
||||
prev_roe = round(11.2, 2)
|
||||
@@ -1887,8 +1913,8 @@ def get_dupont_analysis(
|
||||
"value": financial_leverage,
|
||||
"label": "财务杠杆",
|
||||
"desc": "总资产/净资产",
|
||||
"status": "🟡" if financial_leverage > 3 else "✅",
|
||||
"assessment": "负债率78.6%,偏高但可控",
|
||||
"status": "✅" if financial_leverage < 3 else "🟡",
|
||||
"assessment": f"负债率{debt_ratio}%,结构健康",
|
||||
"raw": {"total_assets": total_assets, "equity": equity},
|
||||
},
|
||||
},
|
||||
@@ -1899,8 +1925,79 @@ def get_dupont_analysis(
|
||||
"equity": equity,
|
||||
},
|
||||
"insight": {
|
||||
"improvement": "提高周转率或利润率,而非加杠杆",
|
||||
"detail": f"净利润率{net_profit_margin}%偏低,资产周转率{asset_turnover}x中等,财务杠杆{financial_leverage}x偏高。改善方向:提升毛利率或加快库存周转。",
|
||||
"improvement": "提升毛利率、控制销售折扣/现金折扣支出,而非加杠杆",
|
||||
"detail": f"财务杠杆{financial_leverage}x健康,ROE {roe}%偏低主要因净利润率{net_profit_margin}%偏低。改善方向:提升毛利率、控制销售折扣/现金折扣支出(合计约110万侵蚀毛利)。",
|
||||
},
|
||||
}
|
||||
|
||||
if entity == "hanke":
|
||||
# 酣客: F_REVENUE/F_NET_PROFIT 从DB读 verified 值(2026H1: 713.27 / -90.86)
|
||||
revenue = _get_dupont_kpi(db, 1, "F_REVENUE")
|
||||
net_profit = _get_dupont_kpi(db, 1, "F_NET_PROFIT")
|
||||
# 资产/权益 DB 无完整数据 → 对应 factor 返回 null,标注"数据待补充",不报错
|
||||
total_assets = None
|
||||
equity = None
|
||||
|
||||
net_profit_margin = round(net_profit / revenue * 100, 2) if revenue else None
|
||||
|
||||
if total_assets and equity:
|
||||
asset_turnover = round(revenue / total_assets, 4)
|
||||
financial_leverage = round(total_assets / equity, 2)
|
||||
roe = round(net_profit_margin / 100 * asset_turnover * financial_leverage * 100, 2)
|
||||
else:
|
||||
# 资产/权益缺失:周转率与杠杆按中性值1估算,ROE≈净利率(净利润为负,符号确定)
|
||||
asset_turnover = None
|
||||
financial_leverage = None
|
||||
roe = net_profit_margin
|
||||
|
||||
# 上期对比:prev_roe 无法获取 → null(前端已兼容 null,显示"无对比")
|
||||
prev_roe = None
|
||||
roe_change = None
|
||||
roe_trend = None
|
||||
|
||||
return {
|
||||
"entity": "hanke",
|
||||
"entity_name": "陕西酣客文化传媒(白酒经销)",
|
||||
"period": "2026年H1",
|
||||
"roe": roe,
|
||||
"roe_change": roe_change,
|
||||
"roe_trend": roe_trend,
|
||||
"prev_roe": prev_roe,
|
||||
"factors": {
|
||||
"net_profit_margin": {
|
||||
"value": net_profit_margin,
|
||||
"label": "净利润率",
|
||||
"desc": "净利润/收入",
|
||||
"status": "🔴" if (net_profit_margin or 0) < 0 else ("🟡" if (net_profit_margin or 0) < 5 else "✅"),
|
||||
"assessment": "净利率为负,本期亏损经营",
|
||||
"raw": {"net_profit": net_profit, "revenue": revenue},
|
||||
},
|
||||
"asset_turnover": {
|
||||
"value": asset_turnover,
|
||||
"label": "资产周转率",
|
||||
"desc": "收入/总资产",
|
||||
"status": "ℹ️",
|
||||
"assessment": "数据待补充",
|
||||
"raw": {"revenue": revenue, "total_assets": total_assets},
|
||||
},
|
||||
"financial_leverage": {
|
||||
"value": financial_leverage,
|
||||
"label": "财务杠杆",
|
||||
"desc": "总资产/净资产",
|
||||
"status": "ℹ️",
|
||||
"assessment": "数据待补充",
|
||||
"raw": {"total_assets": total_assets, "equity": equity},
|
||||
},
|
||||
},
|
||||
"raw_data": {
|
||||
"net_profit": net_profit,
|
||||
"revenue": revenue,
|
||||
"total_assets": total_assets,
|
||||
"equity": equity,
|
||||
},
|
||||
"insight": {
|
||||
"improvement": "止损优先:提升毛利率、控制费用;待资产/权益数据补充后计算完整ROE",
|
||||
"detail": f"净利润{net_profit}万、净利率{net_profit_margin}%,白酒经销业务本期亏损(营收{revenue}万)。资产周转率与财务杠杆因资产/权益数据缺失暂无法计算(数据待补充),待补充后更新完整杜邦拆解。",
|
||||
},
|
||||
}
|
||||
return {"error": "不支持的实体"}
|
||||
|
||||
Reference in New Issue
Block a user