diff --git a/backend/app/api/reports.py b/backend/app/api/reports.py index 4c52745b..357e0040 100644 --- a/backend/app/api/reports.py +++ b/backend/app/api/reports.py @@ -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": "不支持的实体"}