fix(bep-caliber): rebate rate uses YTD actual voucher cumulative (571103 debit / 5001 credit), book+cash card read derived, fail-closed
ci/woodpecker/push/main Pipeline was canceled
ci/woodpecker/push/main Pipeline was canceled
This commit is contained in:
@@ -29,16 +29,25 @@ router = APIRouter(prefix="/api/cma/dashboard", tags=["驾驶舱"],
|
||||
# 月销 = voucher_details subject 5001(主营业务收入) 贷方 YTD 月均(万元)
|
||||
# 月管理费 = 5602% 借−贷 净额 YTD 月均(carry_forward=0 行域;账面口径含折旧)
|
||||
# 折旧 = 560213 借−贷 净额 YTD 月均(同上;口径4 见 _decisions/cma-expense-profit-net-caliber-20261002.md)
|
||||
# 渠补率 = 厂家政策 84.61%(财务Bot权威口径)
|
||||
# 渠补率 = **实测累计**(Σ571103借方 ÷ Σ5001贷方,YTD)——取自派生落库值 F_BEP_REBATE(单一事实源)
|
||||
# 保本额 = 月固定成本 ÷ (1-渠补率)
|
||||
# 2026-10-02 口径修订(任总 11:43 CST 放行「改,这样数据更加真实」):
|
||||
# ① 渠补率不再用政策常量 84.61 参与计算,改用派生落库的实测累计值;
|
||||
# 政策常量仅作对照返回(inputs.rebate_policy_pct),**不参与计算**;
|
||||
# ② 账面/现金两条路径**统一读派生落库值**——此前 book_bep 实时复算 voucher、
|
||||
# cash_bep 读派生值,9 月数据入库而派生器未跑时同卡两口径不同步;
|
||||
# ③ fail-closed:六条派生值(含渠补率)**同期齐全**才出卡;缺数 ⇒ available=False,
|
||||
# 不出保本值、不回落政策常量(不跨期回落,避免混期口径)。
|
||||
# 账面口径(含折旧) / 现金口径(剔除折旧) 双口径只读返回,无写操作。
|
||||
# 现金口径数值优先读派生KPI(F_BEP_REVENUE_CASH/F_BEP_COVER_CASH)最新落库值,无派生值时回退现算。
|
||||
BEP_CODES = ("F_BEP_REVENUE", "F_BEP_COVER", "F_BEP_GAP",
|
||||
"F_BEP_REVENUE_CASH", "F_BEP_COVER_CASH")
|
||||
"F_BEP_REVENUE_CASH", "F_BEP_COVER_CASH", "F_BEP_REBATE")
|
||||
BEP_BOOK_CODES = ("F_BEP_REVENUE", "F_BEP_COVER", "F_BEP_GAP") # 账面3条必须齐全才 available
|
||||
BEP_CASH_CODES = ("F_BEP_REVENUE_CASH", "F_BEP_COVER_CASH")
|
||||
BEP_REBATE_CODE = "F_BEP_REBATE" # 实测渠补率派生KPI编码(卡面单一事实源)
|
||||
BEP_SALES_SUBJECT = "5001"
|
||||
BEP_ADMIN_PREFIX = "5602"
|
||||
BEP_DEP_SUBJECT = "560213"
|
||||
# 政策常量:仅作对照展示(2026-10-02 起不参与任何计算)
|
||||
BEP_REBATE_POLICY_PCT = 84.61
|
||||
|
||||
|
||||
@@ -98,7 +107,13 @@ def get_bep_card(
|
||||
db: Session = Depends(get_db),
|
||||
entity_id: int = Depends(get_entity_id),
|
||||
):
|
||||
"""本量利·保本点 卡片数据(只读,A区)——账面/现金双口径"""
|
||||
"""本量利·保本点 卡片数据(只读,A区)——账面/现金双口径
|
||||
|
||||
2026-10-02 口径修订(任总 11:43 CST 放行):
|
||||
· 渠补率 = 派生落库的**实测累计值**(F_BEP_REBATE,单一事实源);政策常量仅对照返回;
|
||||
· 账面 / 现金两条路径**统一读派生落库值**(不再实时复算 voucher);
|
||||
· fail-closed:六条派生值同期不齐(尤其缺渠补率)⇒ available=False,不出保本值、不回落常量。
|
||||
"""
|
||||
kpis = {k.kpi_code: k for k in db.query(KPIDefinition).filter(
|
||||
KPIDefinition.entity_id == entity_id,
|
||||
KPIDefinition.status == "active",
|
||||
@@ -109,60 +124,70 @@ def get_bep_card(
|
||||
return {"available": False, "message": "BEP账面口径派生KPI未完整注册",
|
||||
"found": list(kpis.keys()), "missing": missing_book}
|
||||
|
||||
# 最新派生期间与值(同派生器:source_type='derived');现金2条缺失不阻塞整卡
|
||||
latest = {}
|
||||
for code in BEP_CODES:
|
||||
if code not in kpis:
|
||||
latest[code] = None
|
||||
continue
|
||||
v = db.query(KPIValue).filter(
|
||||
KPIValue.kpi_id == kpis[code].id,
|
||||
KPIValue.source_type == "derived",
|
||||
KPIValue.actual_value.isnot(None),
|
||||
month_period_clause(KPIValue.period),
|
||||
).order_by(KPIValue.period.desc(), KPIValue.id.desc()).first()
|
||||
latest[code] = {"period": v.period, "value": v.actual_value} if v else None
|
||||
# 派生落库值(同派生器:source_type='derived')——按 code × period 建表,period desc 排序
|
||||
# ⚠️ 不过滤 actual_value IS NULL:fail-closed 落库的 pending 期(值 NULL)必须参与「最新期」判定,
|
||||
# 否则卡面会悄悄跨期回落到上一期,掩盖「本期不出值」的事实。
|
||||
rows = db.query(KPIValue, KPIDefinition.kpi_code).join(
|
||||
KPIDefinition, KPIDefinition.id == KPIValue.kpi_id,
|
||||
).filter(
|
||||
KPIDefinition.entity_id == entity_id,
|
||||
KPIDefinition.kpi_code.in_(BEP_CODES),
|
||||
KPIValue.source_type == "derived",
|
||||
month_period_clause(KPIValue.period),
|
||||
).order_by(KPIValue.period.desc(), KPIValue.id.desc()).all()
|
||||
|
||||
by_code = {} # {code: {period: value|None}}
|
||||
for v, code in rows:
|
||||
by_code.setdefault(code, {}).setdefault(v.period, v.actual_value)
|
||||
|
||||
# 卡面期 = 账面三条派生行的**最新期间**(与派生器目标期一致)
|
||||
book_periods = sorted({p for c in BEP_BOOK_CODES for p in by_code.get(c, {})}, reverse=True)
|
||||
if not book_periods:
|
||||
return {"available": False, "entity_id": entity_id,
|
||||
"message": "尚无派生值,请先运行 kpi_bep_deriver.py",
|
||||
"rebate_policy_pct": BEP_REBATE_POLICY_PCT, "readonly": True}
|
||||
period = book_periods[0]
|
||||
|
||||
# fail-closed:六条派生值(含实测渠补率)本期必须齐全,缺一条即不出保本值
|
||||
missing_at_period = [c for c in BEP_CODES if by_code.get(c, {}).get(period) is None]
|
||||
if missing_at_period:
|
||||
if BEP_REBATE_CODE in missing_at_period:
|
||||
msg = ("渠补率(实测)不可得:本期未落 F_BEP_REBATE 派生值 ⇒ 不出保本值"
|
||||
"(fail-closed,不回落政策常量、不跨期回落)")
|
||||
else:
|
||||
msg = ("本期派生值不齐 %s ⇒ 不出保本值(fail-closed,不跨期回落)"
|
||||
% ",".join(missing_at_period))
|
||||
return {"available": False, "entity_id": entity_id, "period": period,
|
||||
"message": msg, "missing_codes": missing_at_period,
|
||||
"rebate_policy_pct": BEP_REBATE_POLICY_PCT,
|
||||
"derived_periods": book_periods[:6], "readonly": True}
|
||||
|
||||
periods = [latest[c]["period"] for c in BEP_BOOK_CODES if latest[c]]
|
||||
if not periods:
|
||||
return {"available": False, "message": "尚无派生值,请先运行 kpi_bep_deriver.py"}
|
||||
period = max(periods)
|
||||
year = period[:4]
|
||||
months = int(period[5:7])
|
||||
period_from = f"{year}-01"
|
||||
period_to = period
|
||||
|
||||
# 输入口径(万元 / %)——与派生器一致
|
||||
sales_avg = _bep_sum_field(db, entity_id, period_from, period_to,
|
||||
# 六条派生落库值(同期)—— 账面 / 现金两条路径同一来源,卡面不做实时复算
|
||||
d = {c: by_code[c][period] for c in BEP_CODES}
|
||||
book_bep = float(d["F_BEP_REVENUE"])
|
||||
book_cover = float(d["F_BEP_COVER"])
|
||||
book_gap = float(d["F_BEP_GAP"])
|
||||
cash_bep = float(d["F_BEP_REVENUE_CASH"])
|
||||
cash_cover = float(d["F_BEP_COVER_CASH"])
|
||||
cash_gap = round(100.0 - cash_cover, 1) # 缺口 = 100 − 覆盖率(与派生器同口径)
|
||||
rebate_pct = float(d[BEP_REBATE_CODE]) # 实测累计渠补率(单一事实源)
|
||||
cm_pct = round(100.0 - rebate_pct, 2)
|
||||
|
||||
# 输入口径(万元,展示用;数值口径一律以派生落库值为准)
|
||||
sales_avg = _bep_sum_field(db, entity_id, period_from, period,
|
||||
"credit_amount", ("eq", BEP_SALES_SUBJECT)) / 10000.0 / months
|
||||
admin_avg = _bep_sum_field(db, entity_id, period_from, period_to,
|
||||
admin_avg = _bep_sum_field(db, entity_id, period_from, period,
|
||||
"net", ("like", BEP_ADMIN_PREFIX)) / 10000.0 / months
|
||||
dep_avg = _bep_sum_field(db, entity_id, period_from, period_to,
|
||||
dep_avg = _bep_sum_field(db, entity_id, period_from, period,
|
||||
"net", ("eq", BEP_DEP_SUBJECT)) / 10000.0 / months
|
||||
rebate_pct = BEP_REBATE_POLICY_PCT
|
||||
cm_pct = 100.0 - rebate_pct
|
||||
|
||||
# 账面口径:管理费含折旧(与派生落库值一致)
|
||||
book_bep = round(admin_avg / (cm_pct / 100.0), 1)
|
||||
book_cover = round(sales_avg / book_bep * 100.0, 1) if book_bep else 0.0
|
||||
book_gap = round((book_bep - sales_avg) / book_bep * 100.0, 1) if book_bep else 0.0
|
||||
# 现金口径:管理费-折旧;优先读派生KPI最新落库值(单一事实源),缺失时回退现算
|
||||
cash_admin = admin_avg - dep_avg
|
||||
cash_derived_bep = latest["F_BEP_REVENUE_CASH"]["value"] if latest.get("F_BEP_REVENUE_CASH") else None
|
||||
cash_derived_cover = latest["F_BEP_COVER_CASH"]["value"] if latest.get("F_BEP_COVER_CASH") else None
|
||||
if cash_derived_bep is not None and cash_derived_cover is not None:
|
||||
cash_bep = cash_derived_bep
|
||||
cash_cover = cash_derived_cover
|
||||
else:
|
||||
cash_bep = round(cash_admin / (cm_pct / 100.0), 1) if cash_admin > 0 else 0.0
|
||||
cash_cover = round(sales_avg / cash_bep * 100.0, 1) if cash_bep else 0.0
|
||||
cash_gap = round((cash_bep - sales_avg) / cash_bep * 100.0, 1) if cash_bep else 0.0
|
||||
|
||||
# 回显最新落库派生值(账面3条+现金2条,存在时输出)
|
||||
derived = {}
|
||||
for code in BEP_CODES:
|
||||
lv = latest[code]
|
||||
derived[code] = {"period": lv["period"], "value": lv["value"]} if lv else None
|
||||
# 回显六条派生落库值(同期)
|
||||
derived = {c: {"period": period, "value": d[c]} for c in BEP_CODES}
|
||||
|
||||
cover_kpi = kpis["F_BEP_COVER"]
|
||||
book_level = _bep_level(book_cover, cover_kpi)
|
||||
@@ -182,7 +207,9 @@ def get_bep_card(
|
||||
"admin_avg": round(admin_avg, 2),
|
||||
"dep_avg": round(dep_avg, 2),
|
||||
"rebate_pct": rebate_pct,
|
||||
"cm_pct": round(cm_pct, 2),
|
||||
"rebate_policy_pct": BEP_REBATE_POLICY_PCT, # 政策常量:仅作对照,不参与计算
|
||||
"rebate_source": "derived:F_BEP_REBATE(Σ571103借方÷Σ5001贷方, YTD累计)",
|
||||
"cm_pct": cm_pct,
|
||||
},
|
||||
"book": {
|
||||
"bep_revenue": book_bep,
|
||||
@@ -200,7 +227,8 @@ def get_bep_card(
|
||||
"gap_level": cash_gap_level,
|
||||
},
|
||||
"derived": derived,
|
||||
"formula": "保本销售额 = 月固定成本 ÷ (1 − 渠补率84.61%)",
|
||||
"data_source": "derived", # 账面/现金同源:均读派生落库值
|
||||
"formula": f"保本销售额 = 月固定成本 ÷ (1 − 实测渠补率{rebate_pct:.2f}%)",
|
||||
"readonly": True,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user