- 派生器/口径:F_BEP_VC_RATE 新增;卡面/公式行文案改「月固定成本 ÷ 边际贡献率」 - 保本额 = 月固定成本 ÷ (1 − 变动成本率);实测渠补率(F_BEP_REBATE)降为 e1 专用 chip - 报告卡口径说明文字同步(固定成本=固定费用合计) 任务: finance-20261003-cvp-fix-and-bohai-coverage-fullstack ⑤
This commit is contained in:
committed by
Hermes CI Fix
parent
4fe956fcf9
commit
587d5b3983
@@ -35,8 +35,10 @@ router = APIRouter(prefix="/api/cma/dashboard", tags=["驾驶舱"],
|
|||||||
# 月销 = voucher_details subject 5001(主营业务收入) 贷方 YTD 月均(万元)
|
# 月销 = voucher_details subject 5001(主营业务收入) 贷方 YTD 月均(万元)
|
||||||
# 月管理费 = 5602% 借−贷 净额 YTD 月均(carry_forward=0 行域;账面口径含折旧)
|
# 月管理费 = 5602% 借−贷 净额 YTD 月均(carry_forward=0 行域;账面口径含折旧)
|
||||||
# 折旧 = 560213 借−贷 净额 YTD 月均(同上;口径4 见 _decisions/cma-expense-profit-net-caliber-20261002.md)
|
# 折旧 = 560213 借−贷 净额 YTD 月均(同上;口径4 见 _decisions/cma-expense-profit-net-caliber-20261002.md)
|
||||||
# 渠补率 = **实测累计**(Σ571103借方 ÷ Σ5001贷方,YTD)——取自派生落库值 F_BEP_REBATE(单一事实源)
|
# 变动成本率 = **实测累计**(⑤ 口径统一:酣客=Σ(5401+5403+571103−530103)÷Σ5001贷;
|
||||||
# 保本额 = 月固定成本 ÷ (1-渠补率)
|
# 博海=Σ6401借÷Σ6001贷)——取自派生落库值 F_BEP_VC_RATE(单一事实源)
|
||||||
|
# 实测渠补率 = Σ571103借 ÷ Σ5001贷(酣客专用,F_BEP_REBATE,仅作 chip 展示,**不再充当变动成本率**)
|
||||||
|
# 保本额 = 月固定成本 ÷ 边际贡献率(边际贡献率 = 1 − 变动成本率)
|
||||||
# 2026-10-02 口径修订(任总 11:43 CST 放行「改,这样数据更加真实」):
|
# 2026-10-02 口径修订(任总 11:43 CST 放行「改,这样数据更加真实」):
|
||||||
# ① 渠补率不再用政策常量 84.61 参与计算,改用派生落库的实测累计值;
|
# ① 渠补率不再用政策常量 84.61 参与计算,改用派生落库的实测累计值;
|
||||||
# 政策常量仅作对照返回(inputs.rebate_policy_pct),**不参与计算**;
|
# 政策常量仅作对照返回(inputs.rebate_policy_pct),**不参与计算**;
|
||||||
@@ -46,10 +48,16 @@ router = APIRouter(prefix="/api/cma/dashboard", tags=["驾驶舱"],
|
|||||||
# 不出保本值、不回落政策常量(不跨期回落,避免混期口径)。
|
# 不出保本值、不回落政策常量(不跨期回落,避免混期口径)。
|
||||||
# 账面口径(含折旧) / 现金口径(剔除折旧) 双口径只读返回,无写操作。
|
# 账面口径(含折旧) / 现金口径(剔除折旧) 双口径只读返回,无写操作。
|
||||||
BEP_CODES = ("F_BEP_REVENUE", "F_BEP_COVER", "F_BEP_GAP",
|
BEP_CODES = ("F_BEP_REVENUE", "F_BEP_COVER", "F_BEP_GAP",
|
||||||
"F_BEP_REVENUE_CASH", "F_BEP_COVER_CASH", "F_BEP_REBATE")
|
"F_BEP_REVENUE_CASH", "F_BEP_COVER_CASH",
|
||||||
|
"F_BEP_VC_RATE", "F_BEP_REBATE")
|
||||||
BEP_BOOK_CODES = ("F_BEP_REVENUE", "F_BEP_COVER", "F_BEP_GAP") # 账面3条必须齐全才 available
|
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_CASH_CODES = ("F_BEP_REVENUE_CASH", "F_BEP_COVER_CASH")
|
||||||
BEP_REBATE_CODE = "F_BEP_REBATE" # 变动/渠补率派生KPI编码(卡面单一事实源)
|
# 保本公式所用「变动成本率」编码(⑤ 口径统一 2026-10-03:边际贡献率 = 1 − 变动成本率)
|
||||||
|
BEP_VC_CODE = "F_BEP_VC_RATE"
|
||||||
|
# 实测渠补率编码(酣客 e1 专用、**可选**;⑤ 后**不再充当变动成本率**,仅作卡面 chip)
|
||||||
|
BEP_REBATE_CODE = "F_BEP_REBATE"
|
||||||
|
# fail-closed 必备六条:VC_RATE 必填;REBATE 因博海(e2)无渠补科目而不作必备
|
||||||
|
BEP_REQUIRED_CODES = tuple(c for c in BEP_CODES if c != BEP_REBATE_CODE)
|
||||||
# 展示用输入口径常量(**仅酣客(entity1)**;多实体一律走 app/utils/bep_caliber.py 的 ENTITY_CALIBERS,
|
# 展示用输入口径常量(**仅酣客(entity1)**;多实体一律走 app/utils/bep_caliber.py 的 ENTITY_CALIBERS,
|
||||||
# 2026-10-03 扩面:博海 entity2 为 6001/6401/6601+6602+6603/1602)
|
# 2026-10-03 扩面:博海 entity2 为 6001/6401/6601+6602+6603/1602)
|
||||||
BEP_SALES_SUBJECT = "5001"
|
BEP_SALES_SUBJECT = "5001"
|
||||||
@@ -153,6 +161,10 @@ def get_bep_card(
|
|||||||
· 渠补率 = 派生落库的**实测累计值**(F_BEP_REBATE,单一事实源);政策常量仅对照返回;
|
· 渠补率 = 派生落库的**实测累计值**(F_BEP_REBATE,单一事实源);政策常量仅对照返回;
|
||||||
· 账面 / 现金两条路径**统一读派生落库值**(不再实时复算 voucher);
|
· 账面 / 现金两条路径**统一读派生落库值**(不再实时复算 voucher);
|
||||||
· fail-closed:六条派生值同期不齐(尤其缺渠补率)⇒ available=False,不出保本值、不回落常量。
|
· fail-closed:六条派生值同期不齐(尤其缺渠补率)⇒ available=False,不出保本值、不回落常量。
|
||||||
|
|
||||||
|
2026-10-03 口径统一(⑤,任总 18:00 CST):**边际贡献率 = 1 − 变动成本率**(F_BEP_VC_RATE,
|
||||||
|
完整口径:销货成本+税金及附加+渠补−厂补 / 博海 6401÷6001),不再用渠补率充当变动成本率;
|
||||||
|
实测渠补率 F_BEP_REBATE 降为 e1 专用展示 chip(e2 无渠补科目 ⇒ None,不再纳入必备集)。
|
||||||
"""
|
"""
|
||||||
cal = caliber_or_none(entity_id)
|
cal = caliber_or_none(entity_id)
|
||||||
if cal is None:
|
if cal is None:
|
||||||
@@ -199,20 +211,21 @@ def get_bep_card(
|
|||||||
months = int(period[5:7])
|
months = int(period[5:7])
|
||||||
period_from = f"{year}-01"
|
period_from = f"{year}-01"
|
||||||
|
|
||||||
# fail-closed:六条派生值(含变动成本率)本期必须齐全,缺一条即不出保本值
|
# fail-closed:必备派生值(含变动成本率)本期必须齐全,缺一条即不出保本值
|
||||||
missing_at_period = [c for c in BEP_CODES if by_code.get(c, {}).get(period) is None]
|
missing_at_period = [c for c in BEP_REQUIRED_CODES if by_code.get(c, {}).get(period) is None]
|
||||||
if missing_at_period:
|
if missing_at_period:
|
||||||
# ① 不适用(派生器 fail-closed 落库:remark 已注明「保本点不适用」)
|
# ① 不适用(派生器 fail-closed 落库:remark 已注明「保本点不适用」)
|
||||||
# —— 与「缺数」区分:卡面显示「不适用」而非「数据不齐」,且不得显示 0。
|
# —— 与「缺数」区分:卡面显示「不适用」而非「数据不齐」,且不得显示 0。
|
||||||
na_remarks = [remark_map.get((c, period), "") for c in missing_at_period]
|
na_remarks = [remark_map.get((c, period), "") for c in missing_at_period]
|
||||||
na_hit = [r for r in na_remarks if "不适用" in r]
|
na_hit = [r for r in na_remarks if "不适用" in r]
|
||||||
if na_hit and BEP_REBATE_CODE not in missing_at_period:
|
if na_hit and BEP_VC_CODE not in missing_at_period:
|
||||||
reason = na_hit[0]
|
reason = na_hit[0]
|
||||||
label = ("边际贡献为负 · 保本点不适用" if "边际贡献非正" in reason
|
label = ("边际贡献为负 · 保本点不适用" if "边际贡献非正" in reason
|
||||||
else "固定成本退化 · 保本点不适用")
|
else "固定成本退化 · 保本点不适用")
|
||||||
return {"available": False, "not_applicable": True, "entity_id": entity_id,
|
return {"available": False, "not_applicable": True, "entity_id": entity_id,
|
||||||
"period": period, "months": months,
|
"period": period, "months": months,
|
||||||
"label": label, "reason": reason,
|
"label": label, "reason": reason,
|
||||||
|
"vc_pct": by_code.get(BEP_VC_CODE, {}).get(period),
|
||||||
"rebate_pct": by_code.get(BEP_REBATE_CODE, {}).get(period),
|
"rebate_pct": by_code.get(BEP_REBATE_CODE, {}).get(period),
|
||||||
"rebate_policy_pct": (cal.get("policy_pct")
|
"rebate_policy_pct": (cal.get("policy_pct")
|
||||||
if cal.get("policy_pct") is not None else BEP_REBATE_POLICY_PCT),
|
if cal.get("policy_pct") is not None else BEP_REBATE_POLICY_PCT),
|
||||||
@@ -221,8 +234,8 @@ def get_bep_card(
|
|||||||
"derived_periods": book_periods[:6],
|
"derived_periods": book_periods[:6],
|
||||||
"data_source": "derived", "readonly": True}
|
"data_source": "derived", "readonly": True}
|
||||||
# ② 缺数(真不齐)
|
# ② 缺数(真不齐)
|
||||||
if BEP_REBATE_CODE in missing_at_period:
|
if BEP_VC_CODE in missing_at_period:
|
||||||
msg = (f"{cal['margin_label']}不可得:本期未落 F_BEP_REBATE 派生值 ⇒ 不出保本值"
|
msg = (f"{cal['margin_label']}不可得:本期未落 F_BEP_VC_RATE 派生值 ⇒ 不出保本值"
|
||||||
"(fail-closed,不回落政策常量、不跨期回落)")
|
"(fail-closed,不回落政策常量、不跨期回落)")
|
||||||
else:
|
else:
|
||||||
msg = ("本期派生值不齐 %s ⇒ 不出保本值(fail-closed,不跨期回落)"
|
msg = ("本期派生值不齐 %s ⇒ 不出保本值(fail-closed,不跨期回落)"
|
||||||
@@ -233,16 +246,19 @@ def get_bep_card(
|
|||||||
else BEP_REBATE_POLICY_PCT),
|
else BEP_REBATE_POLICY_PCT),
|
||||||
"derived_periods": book_periods[:6], "readonly": True}
|
"derived_periods": book_periods[:6], "readonly": True}
|
||||||
|
|
||||||
# 六条派生落库值(同期)—— 账面 / 现金两条路径同一来源,卡面不做实时复算
|
# 派生落库值(同期)—— 账面 / 现金两条路径同一来源,卡面不做实时复算
|
||||||
d = {c: by_code[c][period] for c in BEP_CODES}
|
d = {c: by_code.get(c, {}).get(period) for c in BEP_CODES}
|
||||||
book_bep = float(d["F_BEP_REVENUE"])
|
book_bep = float(d["F_BEP_REVENUE"])
|
||||||
book_cover = float(d["F_BEP_COVER"])
|
book_cover = float(d["F_BEP_COVER"])
|
||||||
book_gap = float(d["F_BEP_GAP"])
|
book_gap = float(d["F_BEP_GAP"])
|
||||||
cash_bep = float(d["F_BEP_REVENUE_CASH"])
|
cash_bep = float(d["F_BEP_REVENUE_CASH"])
|
||||||
cash_cover = float(d["F_BEP_COVER_CASH"])
|
cash_cover = float(d["F_BEP_COVER_CASH"])
|
||||||
cash_gap = round(100.0 - cash_cover, 1) # 缺口 = 100 − 覆盖率(与派生器同口径)
|
cash_gap = round(100.0 - cash_cover, 1) # 缺口 = 100 − 覆盖率(与派生器同口径)
|
||||||
rebate_pct = float(d[BEP_REBATE_CODE]) # 实测累计渠补率(单一事实源)
|
# ⑤ 口径统一(任总 2026-10-03 18:00「统一」):保本公式用**变动成本率**(F_BEP_VC_RATE),
|
||||||
cm_pct = round(100.0 - rebate_pct, 2)
|
# 边际贡献率 = 1 − 变动成本率;F_BEP_REBATE(实测渠补率)仅酣客(e1)有,**不再充当变动成本率**。
|
||||||
|
vc_pct = float(d[BEP_VC_CODE])
|
||||||
|
cm_pct = round(100.0 - vc_pct, 2)
|
||||||
|
rebate_pct = (float(d[BEP_REBATE_CODE]) if d.get(BEP_REBATE_CODE) is not None else None)
|
||||||
|
|
||||||
# 输入口径(万元,展示用;数值口径一律以派生落库值为准)——按实体口径现算
|
# 输入口径(万元,展示用;数值口径一律以派生落库值为准)——按实体口径现算
|
||||||
inp = _bep_inputs(db, entity_id, cal, period_from, period, months)
|
inp = _bep_inputs(db, entity_id, cal, period_from, period, months)
|
||||||
@@ -272,9 +288,12 @@ def get_bep_card(
|
|||||||
"sales_avg": round(sales_avg, 2),
|
"sales_avg": round(sales_avg, 2),
|
||||||
"admin_avg": round(admin_avg, 2),
|
"admin_avg": round(admin_avg, 2),
|
||||||
"dep_avg": round(dep_avg, 2),
|
"dep_avg": round(dep_avg, 2),
|
||||||
|
"vc_pct": vc_pct,
|
||||||
"rebate_pct": rebate_pct,
|
"rebate_pct": rebate_pct,
|
||||||
"rebate_policy_pct": BEP_REBATE_POLICY_PCT, # 政策常量:仅作对照,不参与计算
|
"rebate_policy_pct": BEP_REBATE_POLICY_PCT, # 政策常量:仅作对照,不参与计算
|
||||||
"rebate_source": "derived:F_BEP_REBATE(Σ571103借方÷Σ5001贷方, YTD累计)",
|
"rebate_source": ("derived:F_BEP_VC_RATE(⑤完整口径:销货成本+税金及附加+渠补−厂补)"
|
||||||
|
if entity_id == 1
|
||||||
|
else "derived:F_BEP_VC_RATE(Σ6401借÷Σ6001贷)"),
|
||||||
"cm_pct": cm_pct,
|
"cm_pct": cm_pct,
|
||||||
},
|
},
|
||||||
"book": {
|
"book": {
|
||||||
@@ -294,7 +313,8 @@ def get_bep_card(
|
|||||||
},
|
},
|
||||||
"derived": derived,
|
"derived": derived,
|
||||||
"data_source": "derived", # 账面/现金同源:均读派生落库值
|
"data_source": "derived", # 账面/现金同源:均读派生落库值
|
||||||
"formula": f"保本销售额 = 月固定成本 ÷ (1 − 实测渠补率{rebate_pct:.2f}%)",
|
"formula": (f"保本销售额 = 月固定成本 ÷ 边际贡献率{cm_pct:.2f}%"
|
||||||
|
f"(边际贡献率 = 1 − 变动成本率{vc_pct:.2f}%)"),
|
||||||
"readonly": True,
|
"readonly": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,18 +5,24 @@
|
|||||||
· 派生落库写入面:`backend/scripts/kpi_bep_deriver.py`
|
· 派生落库写入面:`backend/scripts/kpi_bep_deriver.py`
|
||||||
· 卡面读取面 :`backend/app/api/dashboard.py::/bep-card`
|
· 卡面读取面 :`backend/app/api/dashboard.py::/bep-card`
|
||||||
|
|
||||||
口径(2026-10-03 财务裁定,任务 finance-20261003-cvp-fix-and-bohai-coverage-fullstack):
|
口径(2026-10-03 财务裁定 + 任总 18:00「统一」,任务 finance-20261003-cvp-fix-and-bohai-coverage-fullstack):
|
||||||
|
|
||||||
| 项 | entity 1 陕西酣客 | entity 2 博海 |
|
| 项 | entity 1 陕西酣客 | entity 2 博海 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 变动成本(分子) | Σ571103 借方(渠补/捐赠支出) | Σ6401 借方(主营业务成本) |
|
| 变动成本(分子) | **(Σ5401借 + Σ5403借 + Σ571103借 − Σ530103贷)**〔⑤口径统一〕 | Σ6401 借方(主营业务成本) |
|
||||||
| 收入基数(分母) | Σ5001 贷方(主营业务收入) | Σ6001 贷方(主营业务收入) |
|
| 收入基数(分母) | Σ5001 贷方(主营业务收入) | Σ6001 贷方(主营业务收入) |
|
||||||
| 变动成本率 | Σ571103借 ÷ Σ5001贷(实测渠补率) | Σ6401借 ÷ Σ6001贷(主营业务成本率) |
|
| 变动成本率 | 上式 ÷ Σ5001贷(**完整口径**,非渠补率) | Σ6401借 ÷ Σ6001贷(主营业务成本率) |
|
||||||
| 固定成本 | `5602%`(管理费用,含折旧)借−贷净额 | `6601+6602+6603`(销售/管理/财务费用)借−贷净额 |
|
| 实测渠补率(F_BEP_REBATE) | Σ571103借 ÷ Σ5001贷(**保留**为渠补单一事实源,**不再**充当变动成本率) | 无(None) |
|
||||||
|
| 固定成本 | `5601%` + `5602%` + `5603%` 借−贷净额〔⑤扩容〕 | `6601+6602+6603`(销售/管理/财务费用)借−贷净额 |
|
||||||
| 折旧剔除(现金口径) | `560213`(管理费用-折旧叶子) | `1602` 累计折旧**当期贷方发生额**(见下) |
|
| 折旧剔除(现金口径) | `560213`(管理费用-折旧叶子) | `1602` 累计折旧**当期贷方发生额**(见下) |
|
||||||
| 本期利润结转科目 | `3103` 本年利润 | `4103` 本年利润 |
|
| 本期利润结转科目 | `3103` 本年利润 | `4103` 本年利润 |
|
||||||
| 结账信号次选(落盘) | `/root/WB_WS/_data/zxt-hanke/{YYYY.MM}/check.md` | 无(只用 DB 信号) |
|
| 结账信号次选(落盘) | `/root/WB_WS/_data/zxt-hanke/{YYYY.MM}/check.md` | 无(只用 DB 信号) |
|
||||||
|
|
||||||
|
⑤ 口径统一依据(任总 2026-10-03 18:00 DM「统一」):完整口径唯一可勾稽至营业利润
|
||||||
|
`808.80 − 672.65 − 224.16 = −88.01 ≈ −88.00` 既定营业利润。
|
||||||
|
2026-08 锚点(YTD 月均):变动成本率 **83.17%**、边际贡献率 **16.83%**、固定月均 **28.02 万**、
|
||||||
|
保本 **166.45 万/月**、覆盖率 **60.74%**、缺口 **39.26%**、现金保本 **≈121.8 万/月**。
|
||||||
|
|
||||||
博海折旧口径说明:博海科目表只到 4 位一级科目(无费用类折旧叶子,**不存在** 酣客式 `560213`),
|
博海折旧口径说明:博海科目表只到 4 位一级科目(无费用类折旧叶子,**不存在** 酣客式 `560213`),
|
||||||
唯一含「折旧」的是资产类 `1602 累计折旧`;其**当期贷方发生额** = 当期计提折旧额(对方科目
|
唯一含「折旧」的是资产类 `1602 累计折旧`;其**当期贷方发生额** = 当期计提折旧额(对方科目
|
||||||
计入 `6602 管理费用`),故取它作现金口径剔除项。实测 2026-01..07 每月 1,468.83 元,
|
计入 `6602 管理费用`),故取它作现金口径剔除项。实测 2026-01..07 每月 1,468.83 元,
|
||||||
@@ -33,15 +39,22 @@ CLOSING_SUMMARY_LIKE = "%结转%"
|
|||||||
# 酣客结账信号次选来源(智享通落盘 check.md;与 app/api/data.py 的 ZXT_LANDING_DIR 同源)
|
# 酣客结账信号次选来源(智享通落盘 check.md;与 app/api/data.py 的 ZXT_LANDING_DIR 同源)
|
||||||
ZXT_LANDING_DIR_HANKE = "/root/WB_WS/_data/zxt-hanke"
|
ZXT_LANDING_DIR_HANKE = "/root/WB_WS/_data/zxt-hanke"
|
||||||
|
|
||||||
|
# 变动成本「带号科目项」记法: terms = [(sign, subject_code, field), ...]
|
||||||
|
# sign='+' ⇒ 取该科目 field 的发生额相加;sign='-' ⇒ 取该科目 field 的发生额相减
|
||||||
|
# field ∈ {'debit' → debit_amount, 'credit' → credit_amount, 'net' → 借−贷}
|
||||||
ENTITY_CALIBERS = {
|
ENTITY_CALIBERS = {
|
||||||
1: dict(
|
1: dict(
|
||||||
name="陕西酣客",
|
name="陕西酣客",
|
||||||
margin_label="实测渠补率",
|
margin_label="变动成本率",
|
||||||
margin_desc="Σ571103借方 ÷ Σ5001贷方",
|
margin_desc="(Σ5401借+Σ5403借+Σ571103借−Σ530103贷) ÷ Σ5001贷",
|
||||||
|
# ⑤ 口径统一(2026-10-03 任总「统一」):变动成本 = 销货成本 + 税金及附加 + 渠补 − 厂补
|
||||||
|
varcost_terms=[("+", "5401", "debit"), ("+", "5403", "debit"),
|
||||||
|
("+", "571103", "debit"), ("-", "530103", "credit")],
|
||||||
|
# F_BEP_REBATE 实测渠补率专用(单一事实源保留;不再充当变动成本率)
|
||||||
|
rebate_terms=[("+", "571103", "debit")],
|
||||||
revenue_field="credit_amount", revenue_filter=("eq", "5001"),
|
revenue_field="credit_amount", revenue_filter=("eq", "5001"),
|
||||||
varcost_field="debit_amount", varcost_filter=("eq", "571103"),
|
fixed_filters=[("like", "5601"), ("like", "5602"), ("like", "5603")],
|
||||||
fixed_filters=[("like", "5602")],
|
fixed_label="固定成本(5601+5602+5603)",
|
||||||
fixed_label="管理费",
|
|
||||||
dep_field="net", dep_filter=("eq", "560213"),
|
dep_field="net", dep_filter=("eq", "560213"),
|
||||||
closing_subject="3103",
|
closing_subject="3103",
|
||||||
landing_dir=ZXT_LANDING_DIR_HANKE,
|
landing_dir=ZXT_LANDING_DIR_HANKE,
|
||||||
@@ -52,15 +65,16 @@ ENTITY_CALIBERS = {
|
|||||||
name="博海",
|
name="博海",
|
||||||
margin_label="变动成本率(主营业务成本率)",
|
margin_label="变动成本率(主营业务成本率)",
|
||||||
margin_desc="Σ6401借方 ÷ Σ6001贷方",
|
margin_desc="Σ6401借方 ÷ Σ6001贷方",
|
||||||
|
varcost_terms=[("+", "6401", "debit")],
|
||||||
|
rebate_terms=None, # 博海无渠补科目
|
||||||
revenue_field="credit_amount", revenue_filter=("eq", "6001"),
|
revenue_field="credit_amount", revenue_filter=("eq", "6001"),
|
||||||
varcost_field="debit_amount", varcost_filter=("eq", "6401"),
|
|
||||||
fixed_filters=[("like", "6601"), ("like", "6602"), ("like", "6603")],
|
fixed_filters=[("like", "6601"), ("like", "6602"), ("like", "6603")],
|
||||||
fixed_label="固定成本(6601+6602+6603)",
|
fixed_label="固定成本(6601+6602+6603)",
|
||||||
dep_field="credit_amount", dep_filter=("eq", "1602"),
|
dep_field="credit_amount", dep_filter=("eq", "1602"),
|
||||||
closing_subject="4103",
|
closing_subject="4103",
|
||||||
landing_dir=None,
|
landing_dir=None,
|
||||||
exclude_closing=True,
|
exclude_closing=True,
|
||||||
policy_pct=None, # 博海无渠补政策常量
|
policy_pct=None,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
F_BEP_GAP 保本缺口(%) = (保本销售额-月均销售) ÷ 保本销售额 × 100 (反向: <=0绿 <=15黄 >15红)
|
F_BEP_GAP 保本缺口(%) = (保本销售额-月均销售) ÷ 保本销售额 × 100 (反向: <=0绿 <=15黄 >15红)
|
||||||
F_BEP_REVENUE_CASH 现金保本销售额(万元) = (月均固定成本-月均折旧) ÷ 边际贡献率 (现金口径,中性无阈值)
|
F_BEP_REVENUE_CASH 现金保本销售额(万元) = (月均固定成本-月均折旧) ÷ 边际贡献率 (现金口径,中性无阈值)
|
||||||
F_BEP_COVER_CASH 现金保本覆盖率(%) = 月均销售 ÷ 现金保本销售额 × 100 (正向: >=100绿 >=85黄 <85红)
|
F_BEP_COVER_CASH 现金保本覆盖率(%) = 月均销售 ÷ 现金保本销售额 × 100 (正向: >=100绿 >=85黄 <85红)
|
||||||
F_BEP_REBATE 变动/渠补率(%) = 见下方「实体口径注册表」 (YTD累计; 卡面单一事实源,无阈值)
|
F_BEP_VC_RATE 变动成本率(%) = 见下方「实体口径注册表」 (YTD累计; 保本公式分母,无阈值)
|
||||||
|
F_BEP_REBATE 实测渠补率(%) = e1 专用展示 chip(Σ571103÷Σ5001,YTD; e2 无渠补科目 ⇒ NULL)
|
||||||
|
|
||||||
实体口径注册表(2026-10-03 新增,任务 finance-20261003-cvp-fix-and-bohai-coverage-fullstack):
|
实体口径注册表(2026-10-03 新增,任务 finance-20261003-cvp-fix-and-bohai-coverage-fullstack):
|
||||||
· entity 1 陕西酣客:变动成本率 = Σ571103借方 ÷ Σ5001贷方(实测渠补率);固定成本 = 5602% 净额;
|
· entity 1 陕西酣客:变动成本率 = Σ(5401销货成本 + 5403税金及附加 + 571103渠补 − 530103厂补) ÷ Σ5001贷方
|
||||||
折旧剔除 = 560213(管理费用-折旧叶子)。
|
〔⑤口径统一 2026-10-03,任总 18:00「统一」;勾稽:808.80 − 672.65 − 224.16 = −88.01 ≈ −88.00 营业利润〕;
|
||||||
|
固定成本 = 5601% + 5602% + 5603% 净额;折旧剔除 = 560213(管理费用-折旧叶子)。
|
||||||
|
实测渠补率 F_BEP_REBATE 保留(仍是渠补单一事实源),但**不再充当变动成本率**。
|
||||||
· entity 2 博海:**无渠补科目(571103)**,变动成本 = Σ6401借(主营业务成本),
|
· entity 2 博海:**无渠补科目(571103)**,变动成本 = Σ6401借(主营业务成本),
|
||||||
变动成本率 = Σ6401借 ÷ Σ6001贷;固定成本 = (6601 销售费用 + 6602 管理费用 + 6603 财务费用) 净额;
|
变动成本率 = Σ6401借 ÷ Σ6001贷;固定成本 = (6601 销售费用 + 6602 管理费用 + 6603 财务费用) 净额;
|
||||||
折旧剔除 = 1602 累计折旧「当期贷方发生额」(博海科目表只到 4 位一级,无费用类折旧叶子;
|
折旧剔除 = 1602 累计折旧「当期贷方发生额」(博海科目表只到 4 位一级,无费用类折旧叶子;
|
||||||
@@ -21,11 +24,11 @@
|
|||||||
|
|
||||||
口径(与财务Bot报告一致):
|
口径(与财务Bot报告一致):
|
||||||
- 销售收入: entity1 = subject_code='5001'(主营业务收入) 贷方合计;entity2 = '6001' 贷方合计。当年1月至目标月 YTD 月均
|
- 销售收入: entity1 = subject_code='5001'(主营业务收入) 贷方合计;entity2 = '6001' 贷方合计。当年1月至目标月 YTD 月均
|
||||||
- 固定成本: entity1 = subject_code LIKE '5602%'(管理费用,含560213折旧) 借−贷净额合计(carry_forward=0),YTD 月均
|
- 固定成本: entity1 = subject_code LIKE '5601%'/'5602%'/'5603%'(销售/管理/财务费用,含560213折旧) 借−贷净额合计(carry_forward=0),YTD 月均〔⑤扩容〕
|
||||||
entity2 = 6601/6602/6603 借−贷净额合计(同上,但另需剔除结转腿)
|
entity2 = 6601/6602/6603 借−贷净额合计(同上,但另需剔除结转腿)
|
||||||
- 折旧: entity1 = '560213' 借−贷净额;entity2 = '1602' 贷方发生额(现金口径剔除项)
|
- 折旧: entity1 = '560213' 借−贷净额;entity2 = '1602' 贷方发生额(现金口径剔除项)
|
||||||
- 变动成本率: **实测累计口径**(2026-10-02 财务裁定,任总 11:43 CST 放行「改,这样数据更加真实」)
|
- 变动成本率: **完整实测口径**(⑤口径统一 2026-10-03,任总 18:00「统一」;勾稽件 _reports/陕西酣客_边际贡献式损益表_20261003.md)
|
||||||
酣客 渠补率(实测) = Σ(571103 借方) ÷ Σ(5001 贷方) × 100,期间 = 年初至当期(YTD),保留 2 位小数。
|
酣客 = Σ(5401销货成本 + 5403税金及附加 + 571103渠补 − 530103厂补) ÷ Σ(5001 贷方) × 100,YTD,保留 2 位小数。
|
||||||
`POLICY_REBATE_RATE_PCT = 84.61` 政策常量**仅作对照展示**(写进返回值 rebate_policy_pct / remark),
|
`POLICY_REBATE_RATE_PCT = 84.61` 政策常量**仅作对照展示**(写进返回值 rebate_policy_pct / remark),
|
||||||
**不参与任何计算**;`--rebate-rate` 显式传值时视为人工诊断覆盖(正常调用不再使用)。
|
**不参与任何计算**;`--rebate-rate` 显式传值时视为人工诊断覆盖(正常调用不再使用)。
|
||||||
- 边际贡献率 = 100 − 变动成本率
|
- 边际贡献率 = 100 − 变动成本率
|
||||||
@@ -85,13 +88,17 @@ TARGET_KPIS = [
|
|||||||
("F_BEP_GAP", "保本缺口"),
|
("F_BEP_GAP", "保本缺口"),
|
||||||
("F_BEP_REVENUE_CASH", "现金口径保本销售额"),
|
("F_BEP_REVENUE_CASH", "现金口径保本销售额"),
|
||||||
("F_BEP_COVER_CASH", "现金口径保本覆盖率"),
|
("F_BEP_COVER_CASH", "现金口径保本覆盖率"),
|
||||||
|
("F_BEP_VC_RATE", "变动成本率"),
|
||||||
("F_BEP_REBATE", "实测渠补率"),
|
("F_BEP_REBATE", "实测渠补率"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# 实测渠补率 KPI 编码(卡面单一事实源;无阈值,不参与预警级别判定)
|
# 变动成本率 KPI(⑤ 口径统一 2026-10-03:保本公式分子口径 = 销货成本+税金及附加+渠补−厂补)
|
||||||
|
VC_RATE_KPI = "F_BEP_VC_RATE"
|
||||||
|
|
||||||
|
# 实测渠补率 KPI 编码(卡面单一事实源;无阈值,不参与预警级别判定;⑤ 后**不再充当变动成本率**)
|
||||||
REBATE_KPI = "F_BEP_REBATE"
|
REBATE_KPI = "F_BEP_REBATE"
|
||||||
|
|
||||||
# 保本口径 KPI(缺 F_BEP_REBATE 定义时不阻断派生主流程,仅跳过渠补率落库并告警)
|
# 保本口径 KPI(缺 F_BEP_REBATE 定义时不阻断派生主流程,见 run_derive 告警分支)
|
||||||
CORE_KPI_CODES = [c for c, _ in TARGET_KPIS if c != REBATE_KPI]
|
CORE_KPI_CODES = [c for c, _ in TARGET_KPIS if c != REBATE_KPI]
|
||||||
|
|
||||||
# 现金口径 KPI 编码(计算路径独立于账面,remark 文案用现金口径)
|
# 现金口径 KPI 编码(计算路径独立于账面,remark 文案用现金口径)
|
||||||
@@ -222,6 +229,42 @@ def _subject_clause(kind: str, code: str):
|
|||||||
raise ValueError(f"未知科目过滤类型: {kind}")
|
raise ValueError(f"未知科目过滤类型: {kind}")
|
||||||
|
|
||||||
|
|
||||||
|
# 带号科目项的字段映射(terms 记法见 bep_caliber.ENTITY_CALIBERS)
|
||||||
|
_TERM_FIELD = {
|
||||||
|
"debit": lambda: VoucherDetail.debit_amount,
|
||||||
|
"credit": lambda: VoucherDetail.credit_amount,
|
||||||
|
"net": lambda: VoucherDetail.debit_amount - VoucherDetail.credit_amount,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _sum_terms(db, entity_id: int, period_from: str, period_to: str,
|
||||||
|
terms, *, exclude_closing: bool = False) -> float:
|
||||||
|
"""按「带号科目项」聚合金额(元)。terms = [(sign, code, field), ...]
|
||||||
|
|
||||||
|
· sign='+' ⇒ 加该科目 field 发生额;sign='-' ⇒ 减之
|
||||||
|
· field ∈ {debit, credit, net}
|
||||||
|
· code 以 '%' 结尾时按前缀(like)匹配,否则精确匹配
|
||||||
|
⑤ 口径统一(2026-10-03):酣客变动成本 = (Σ5401借 + Σ5403借 + Σ571103借 − Σ530103贷)。
|
||||||
|
"""
|
||||||
|
total = 0.0
|
||||||
|
for sign, code, field in terms:
|
||||||
|
expr = _TERM_FIELD[field]()
|
||||||
|
q = db.query(func.sum(expr)).filter(
|
||||||
|
VoucherDetail.entity_id == entity_id,
|
||||||
|
VoucherDetail.carry_forward == 0,
|
||||||
|
VoucherDetail.period >= period_from,
|
||||||
|
VoucherDetail.period <= period_to,
|
||||||
|
)
|
||||||
|
q = (q.filter(VoucherDetail.subject_code.like(code)) if code.endswith("%")
|
||||||
|
else q.filter(VoucherDetail.subject_code == code))
|
||||||
|
if exclude_closing:
|
||||||
|
q = q.filter(or_(VoucherDetail.summary.is_(None),
|
||||||
|
~VoucherDetail.summary.like(CLOSING_SUMMARY_LIKE)))
|
||||||
|
v = float(q.scalar() or 0.0)
|
||||||
|
total += v if sign == "+" else -v
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
def compute_margin_rate(db, entity_id: int, period_from: str, period_to: str,
|
def compute_margin_rate(db, entity_id: int, period_from: str, period_to: str,
|
||||||
caliber: dict = None) -> dict:
|
caliber: dict = None) -> dict:
|
||||||
"""变动成本率(YTD 累计,2 位小数)—— 按实体口径现算。
|
"""变动成本率(YTD 累计,2 位小数)—— 按实体口径现算。
|
||||||
@@ -236,9 +279,8 @@ def compute_margin_rate(db, entity_id: int, period_from: str, period_to: str,
|
|||||||
(调用方不得回落政策常量,须按「不可得」处理)。
|
(调用方不得回落政策常量,须按「不可得」处理)。
|
||||||
"""
|
"""
|
||||||
cal = caliber or ENTITY_CALIBERS[entity_id]
|
cal = caliber or ENTITY_CALIBERS[entity_id]
|
||||||
num_raw = _sum_field(db, entity_id, period_from, period_to,
|
num_raw = _sum_terms(db, entity_id, period_from, period_to,
|
||||||
cal["varcost_field"], cal["varcost_filter"],
|
cal["varcost_terms"], exclude_closing=cal["exclude_closing"])
|
||||||
exclude_closing=cal["exclude_closing"])
|
|
||||||
den_raw = _sum_field(db, entity_id, period_from, period_to,
|
den_raw = _sum_field(db, entity_id, period_from, period_to,
|
||||||
cal["revenue_field"], cal["revenue_filter"],
|
cal["revenue_field"], cal["revenue_filter"],
|
||||||
exclude_closing=cal["exclude_closing"])
|
exclude_closing=cal["exclude_closing"])
|
||||||
@@ -256,7 +298,7 @@ def compute_margin_rate(db, entity_id: int, period_from: str, period_to: str,
|
|||||||
info["reason"] = f"{cal['margin_label']}不可得:{cal['revenue_filter'][1]} 累计缺数或为 0"
|
info["reason"] = f"{cal['margin_label']}不可得:{cal['revenue_filter'][1]} 累计缺数或为 0"
|
||||||
return info
|
return info
|
||||||
if num_w <= 0:
|
if num_w <= 0:
|
||||||
info["reason"] = f"{cal['margin_label']}不可得:{cal['varcost_filter'][1]} 累计缺数或为 0"
|
info["reason"] = f"{cal['margin_label']}不可得:变动成本累计缺数或为 0"
|
||||||
return info
|
return info
|
||||||
info["available"] = True
|
info["available"] = True
|
||||||
info["rebate_pct"] = round(num_w / den_w * 100.0, 2)
|
info["rebate_pct"] = round(num_w / den_w * 100.0, 2)
|
||||||
@@ -264,17 +306,39 @@ def compute_margin_rate(db, entity_id: int, period_from: str, period_to: str,
|
|||||||
|
|
||||||
|
|
||||||
def compute_rebate_rate(db, entity_id: int, period_from: str, period_to: str) -> dict:
|
def compute_rebate_rate(db, entity_id: int, period_from: str, period_to: str) -> dict:
|
||||||
"""实测累计渠补率(2026-10-02 财务裁定口径)—— 酣客(entity1) 专用薄封装。
|
"""实测累计**渠补率**(F_BEP_REBATE,2026-10-02 口径)—— 酣客(entity1) 专用。
|
||||||
|
|
||||||
渠补率(实测) = Σ(571103 借方) ÷ Σ(5001 贷方) × 100,期间 = 年初至当期(YTD),保留 2 位小数。
|
渠补率(实测) = Σ(571103 借方) ÷ Σ(5001 贷方) × 100,期间 = 年初至当期(YTD),保留 2 位小数。
|
||||||
· 571103 = 渠补/捐赠支出(损益类费用科目)⇒ 本期发生额落**借方**;
|
· 571103 = 渠补/捐赠支出(损益类费用科目)⇒ 本期发生额落**借方**;
|
||||||
· 5001 = 主营业务收入 ⇒ 本期发生额落**贷方**;
|
· 5001 = 主营业务收入 ⇒ 本期发生额落**贷方**;
|
||||||
· 行域 = carry_forward=0(与 _sum_field 一致,排除期间损益结转腿)。
|
· 行域 = carry_forward=0(排除期间损益结转腿)。
|
||||||
|
|
||||||
保留本函数名/键名以兼容既有调用方与单测;通用实现见 compute_margin_rate。
|
⚠️ ⑤ 口径统一(2026-10-03)后:本函数只算**渠补口径**,是 F_BEP_REBATE 的单一事实源;
|
||||||
|
保本公式所用的**变动成本率**(销货成本+税金及附加+渠补−厂补)见 compute_margin_rate。
|
||||||
|
博海(rebate_terms=None)无渠补科目 ⇒ 返回 available=False(F_BEP_REBATE=NULL)。
|
||||||
"""
|
"""
|
||||||
return compute_margin_rate(db, entity_id, period_from, period_to,
|
cal = ENTITY_CALIBERS.get(entity_id)
|
||||||
ENTITY_CALIBERS.get(entity_id))
|
if not cal or not cal.get("rebate_terms"):
|
||||||
|
return {"available": False, "rebate_pct": None, "rebate_sum_w": 0.0,
|
||||||
|
"sales_sum_w": 0.0, "label": "实测渠补率",
|
||||||
|
"reason": f"{cal['name'] if cal else entity_id} 无渠补科目"}
|
||||||
|
num_raw = _sum_terms(db, entity_id, period_from, period_to,
|
||||||
|
cal["rebate_terms"], exclude_closing=cal["exclude_closing"])
|
||||||
|
den_raw = _sum_field(db, entity_id, period_from, period_to,
|
||||||
|
cal["revenue_field"], cal["revenue_filter"],
|
||||||
|
exclude_closing=cal["exclude_closing"])
|
||||||
|
num_w, den_w = num_raw / 10000.0, den_raw / 10000.0
|
||||||
|
info = {"rebate_sum_w": round(num_w, 2), "sales_sum_w": round(den_w, 2),
|
||||||
|
"rebate_pct": None, "available": False, "reason": "", "label": "实测渠补率"}
|
||||||
|
if den_w <= 0:
|
||||||
|
info["reason"] = "渠补率不可得:Σ5001贷累计缺数或为 0"
|
||||||
|
return info
|
||||||
|
if num_w <= 0:
|
||||||
|
info["reason"] = "渠补率不可得:Σ571103借累计缺数或为 0"
|
||||||
|
return info
|
||||||
|
info["available"] = True
|
||||||
|
info["rebate_pct"] = round(num_w / den_w * 100.0, 2)
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
def _month_sales(db, entity_id: int, period: str, caliber: dict = None) -> float:
|
def _month_sales(db, entity_id: int, period: str, caliber: dict = None) -> float:
|
||||||
@@ -460,9 +524,11 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
dep_avg = dep_sum_w / months
|
dep_avg = dep_sum_w / months
|
||||||
cash_admin_avg = admin_avg - dep_avg # 现金口径固定成本(剔除折旧纸面费用)
|
cash_admin_avg = admin_avg - dep_avg # 现金口径固定成本(剔除折旧纸面费用)
|
||||||
|
|
||||||
# 2. 变动成本率:默认逐期实测累计(不再取政策常量;常量仅对照,仅 entity1 有)
|
# 2. 变动成本率(⑤ 后=销货成本+税金及附加+渠补−厂补 完整口径):默认逐期实测累计
|
||||||
rebate_info = compute_margin_rate(db, entity_id, period_from, period_to, cal)
|
rebate_info = compute_margin_rate(db, entity_id, period_from, period_to, cal)
|
||||||
calc_pct = rebate_info["rebate_pct"]
|
calc_pct = rebate_info["rebate_pct"]
|
||||||
|
# ⑤:渠补率(e1 专用;e2 无 ⇒ available=False)。F_BEP_REBATE 专用,独立于上面变动成本率。
|
||||||
|
rebate_kpi_info = compute_rebate_rate(db, entity_id, period_from, period_to)
|
||||||
if rebate_rate_pct is not None:
|
if rebate_rate_pct is not None:
|
||||||
rebate_info = dict(rebate_info, available=True, rebate_pct=round(float(rebate_rate_pct), 2),
|
rebate_info = dict(rebate_info, available=True, rebate_pct=round(float(rebate_rate_pct), 2),
|
||||||
reason="人工覆盖(--rebate-rate)")
|
reason="人工覆盖(--rebate-rate)")
|
||||||
@@ -484,7 +550,8 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
period, cal["margin_label"], rebate_pct)
|
period, cal["margin_label"], rebate_pct)
|
||||||
return _fail_closed_cm_non_positive(db, entity_id, period, months, rebate_info, cal,
|
return _fail_closed_cm_non_positive(db, entity_id, period, months, rebate_info, cal,
|
||||||
kpis, write_kpi, sales_avg=sales_avg,
|
kpis, write_kpi, sales_avg=sales_avg,
|
||||||
admin_avg=admin_avg, dep_avg=dep_avg)
|
admin_avg=admin_avg, dep_avg=dep_avg,
|
||||||
|
rebate_kpi_info=rebate_kpi_info)
|
||||||
|
|
||||||
# 2c. fail-closed ③(2026-10-03,同族补充):固定成本退化 ⇒ 保本点不适用
|
# 2c. fail-closed ③(2026-10-03,同族补充):固定成本退化 ⇒ 保本点不适用
|
||||||
# 边际贡献率 > 0 但分子(固定成本)退化时,公式给出 保本额≈0 / 覆盖率趋于无穷,
|
# 边际贡献率 > 0 但分子(固定成本)退化时,公式给出 保本额≈0 / 覆盖率趋于无穷,
|
||||||
@@ -498,7 +565,8 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
return _fail_closed_cm_non_positive(db, entity_id, period, months, rebate_info, cal,
|
return _fail_closed_cm_non_positive(db, entity_id, period, months, rebate_info, cal,
|
||||||
kpis, write_kpi, sales_avg=sales_avg,
|
kpis, write_kpi, sales_avg=sales_avg,
|
||||||
admin_avg=admin_avg, dep_avg=dep_avg, guard="fixed",
|
admin_avg=admin_avg, dep_avg=dep_avg, guard="fixed",
|
||||||
cash_admin_avg=cash_admin_avg)
|
cash_admin_avg=cash_admin_avg,
|
||||||
|
rebate_kpi_info=rebate_kpi_info)
|
||||||
|
|
||||||
# 3. 派生计算(万元 / %)——账面3条路径不变;现金口径在账面基础上剔除折旧
|
# 3. 派生计算(万元 / %)——账面3条路径不变;现金口径在账面基础上剔除折旧
|
||||||
bep = admin_avg / (cm_pct / 100.0)
|
bep = admin_avg / (cm_pct / 100.0)
|
||||||
@@ -513,7 +581,9 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
"F_BEP_GAP": round(gap, 1),
|
"F_BEP_GAP": round(gap, 1),
|
||||||
"F_BEP_REVENUE_CASH": round(cash_bep, 1),
|
"F_BEP_REVENUE_CASH": round(cash_bep, 1),
|
||||||
"F_BEP_COVER_CASH": round(cash_cover, 1),
|
"F_BEP_COVER_CASH": round(cash_cover, 1),
|
||||||
"F_BEP_REBATE": round(rebate_pct, 2),
|
"F_BEP_VC_RATE": round(rebate_pct, 2), # 变动成本率(⑤ 保本分子口径)
|
||||||
|
"F_BEP_REBATE": (round(rebate_kpi_info["rebate_pct"], 2)
|
||||||
|
if rebate_kpi_info["available"] else None), # 实测渠补率(e1;e2=NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
# 4. upsert kpi_values (source_type='derived',幂等)
|
# 4. upsert kpi_values (source_type='derived',幂等)
|
||||||
@@ -526,15 +596,20 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
if kpi is None: # 定义缺失(仅可能为 F_BEP_REBATE,见上方告警)
|
if kpi is None: # 定义缺失(仅可能为 F_BEP_REBATE,见上方告警)
|
||||||
written[code] = "no-definition"
|
written[code] = "no-definition"
|
||||||
continue
|
continue
|
||||||
if code == REBATE_KPI:
|
if code == VC_RATE_KPI:
|
||||||
if entity_id == 1:
|
remark = (f"变动成本率(YTD累计): {cal['margin_desc']} = "
|
||||||
remark = (f"实测渠补率(YTD累计): Σ571103借方 {rebate_info['rebate_sum_w']:.2f}万 ÷ "
|
|
||||||
f"Σ5001贷方 {rebate_info['sales_sum_w']:.2f}万 × 100 = {rebate_pct:.2f}%"
|
|
||||||
f"(政策常量 {POLICY_REBATE_RATE_PCT:.2f}% 仅供对照,不参与计算)")
|
|
||||||
else:
|
|
||||||
remark = (f"{cal['margin_label']}(YTD累计): {cal['margin_desc']} = "
|
|
||||||
f"{rebate_info['rebate_sum_w']:.2f}万 ÷ {rebate_info['sales_sum_w']:.2f}万 "
|
f"{rebate_info['rebate_sum_w']:.2f}万 ÷ {rebate_info['sales_sum_w']:.2f}万 "
|
||||||
f"× 100 = {rebate_pct:.2f}%({cal['name']}口径,无渠补科目)")
|
f"× 100 = {rebate_pct:.2f}%(⑤口径统一:销货成本+税金及附加+渠补−厂补;"
|
||||||
|
f"保本额 = 固定成本月均 ÷ (1−变动成本率))")
|
||||||
|
elif code == REBATE_KPI:
|
||||||
|
if rebate_kpi_info["available"]:
|
||||||
|
rr = rebate_kpi_info["rebate_pct"]
|
||||||
|
remark = (f"实测渠补率(YTD累计): Σ571103借方 {rebate_kpi_info['rebate_sum_w']:.2f}万 ÷ "
|
||||||
|
f"Σ5001贷方 {rebate_kpi_info['sales_sum_w']:.2f}万 × 100 = {rr:.2f}%"
|
||||||
|
f"(政策常量 {POLICY_REBATE_RATE_PCT:.2f}% 仅供对照;⑤ 后**不再充当变动成本率**,"
|
||||||
|
f"变动成本率见 F_BEP_VC_RATE)")
|
||||||
|
else:
|
||||||
|
remark = f"{cal['name']}无渠补科目(实测渠补率不适用);⑤ 后变动成本率见 F_BEP_VC_RATE"
|
||||||
elif code in CASH_CODES:
|
elif code in CASH_CODES:
|
||||||
if entity_id == 1:
|
if entity_id == 1:
|
||||||
remark = (f"现金口径: 管理费月均{admin_avg:.2f}万-折旧月均{dep_avg:.2f}万"
|
remark = (f"现金口径: 管理费月均{admin_avg:.2f}万-折旧月均{dep_avg:.2f}万"
|
||||||
@@ -560,9 +635,11 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
is_new = existing is None
|
is_new = existing is None
|
||||||
if existing:
|
if existing:
|
||||||
old_value = float(existing.actual_value) if existing.actual_value is not None else None
|
old_value = float(existing.actual_value) if existing.actual_value is not None else None
|
||||||
new_value = float(values[code])
|
nv = values[code]
|
||||||
value_changed = old_value is None or abs(old_value - new_value) > 1e-9
|
new_value = float(nv) if nv is not None else None
|
||||||
existing.actual_value = values[code]
|
value_changed = ((old_value is None) != (new_value is None)
|
||||||
|
or (old_value is not None and abs(old_value - new_value) > 1e-9))
|
||||||
|
existing.actual_value = nv
|
||||||
existing.source_batch = batch
|
existing.source_batch = batch
|
||||||
if value_changed:
|
if value_changed:
|
||||||
existing.remark = (f"{remark}|值已更新(batch={batch}, "
|
existing.remark = (f"{remark}|值已更新(batch={batch}, "
|
||||||
@@ -651,8 +728,10 @@ def run_derive(db, entity_id: int, period: str, rebate_rate_pct: float = None,
|
|||||||
"rebate_sum_w": rebate_info["rebate_sum_w"],
|
"rebate_sum_w": rebate_info["rebate_sum_w"],
|
||||||
"rebate_sales_base_w": rebate_info["sales_sum_w"],
|
"rebate_sales_base_w": rebate_info["sales_sum_w"],
|
||||||
"rebate_actual_pct": rebate_pct,
|
"rebate_actual_pct": rebate_pct,
|
||||||
|
"vc_rate_pct": rebate_pct,
|
||||||
|
"rebate_kpi_pct": (rebate_kpi_info["rebate_pct"] if rebate_kpi_info["available"] else None),
|
||||||
"rebate_policy_pct": POLICY_REBATE_RATE_PCT,
|
"rebate_policy_pct": POLICY_REBATE_RATE_PCT,
|
||||||
"rebate_source": rebate_info.get("reason") or "YTD实测累计(Σ571103借方/Σ5001贷方)",
|
"rebate_source": rebate_info.get("reason") or "YTD实测累计(⑤完整口径)",
|
||||||
"rebate_calc_pct": round(calc_pct, 2) if calc_pct is not None else None,
|
"rebate_calc_pct": round(calc_pct, 2) if calc_pct is not None else None,
|
||||||
"cm_pct": round(cm_pct, 2),
|
"cm_pct": round(cm_pct, 2),
|
||||||
"values": values,
|
"values": values,
|
||||||
@@ -734,7 +813,8 @@ def _fail_closed_cm_non_positive(db, entity_id: int, period: str, months: int,
|
|||||||
rebate_info: dict, cal: dict, kpis: dict, write_kpi: bool,
|
rebate_info: dict, cal: dict, kpis: dict, write_kpi: bool,
|
||||||
sales_avg: float = None, admin_avg: float = None,
|
sales_avg: float = None, admin_avg: float = None,
|
||||||
dep_avg: float = None, guard: str = "cm",
|
dep_avg: float = None, guard: str = "cm",
|
||||||
cash_admin_avg: float = None) -> dict:
|
cash_admin_avg: float = None,
|
||||||
|
rebate_kpi_info: dict = None) -> dict:
|
||||||
"""fail-closed ② 落库:保本点**不适用**(2026-10-03 财务裁定)。
|
"""fail-closed ② 落库:保本点**不适用**(2026-10-03 财务裁定)。
|
||||||
|
|
||||||
五条保本派生值(REVENUE/COVER/GAP/REVENUE_CASH/COVER_CASH)写
|
五条保本派生值(REVENUE/COVER/GAP/REVENUE_CASH/COVER_CASH)写
|
||||||
@@ -758,14 +838,16 @@ def _fail_closed_cm_non_positive(db, entity_id: int, period: str, months: int,
|
|||||||
detail = (f"边际贡献非正,保本点不适用({cal['margin_label']} {rebate_pct:.2f}% ≥ 100%,"
|
detail = (f"边际贡献非正,保本点不适用({cal['margin_label']} {rebate_pct:.2f}% ≥ 100%,"
|
||||||
f"边际贡献率 {cm_pct:.2f}% ⇒ 每增一元销售不产生正贡献,保本点无经济意义;"
|
f"边际贡献率 {cm_pct:.2f}% ⇒ 每增一元销售不产生正贡献,保本点无经济意义;"
|
||||||
f"fail-closed,禁写 0.0)")
|
f"fail-closed,禁写 0.0)")
|
||||||
if entity_id == 1:
|
vc_rate_remark = (f"变动成本率(YTD累计): {cal['margin_desc']} = "
|
||||||
rebate_remark = (f"实测渠补率(YTD累计): Σ571103借方 {rebate_info['rebate_sum_w']:.2f}万 ÷ "
|
|
||||||
f"Σ5001贷方 {rebate_info['sales_sum_w']:.2f}万 × 100 = {rebate_pct:.2f}%"
|
|
||||||
f"(政策常量 {POLICY_REBATE_RATE_PCT:.2f}% 仅供对照,不参与计算)")
|
|
||||||
else:
|
|
||||||
rebate_remark = (f"{cal['margin_label']}(YTD累计): {cal['margin_desc']} = "
|
|
||||||
f"{rebate_info['rebate_sum_w']:.2f}万 ÷ {rebate_info['sales_sum_w']:.2f}万 "
|
f"{rebate_info['rebate_sum_w']:.2f}万 ÷ {rebate_info['sales_sum_w']:.2f}万 "
|
||||||
f"× 100 = {rebate_pct:.2f}%({cal['name']}口径,无渠补科目)")
|
f"× 100 = {rebate_pct:.2f}%(⑤口径统一;保本点因边际贡献非正/固定成本退化不适用)")
|
||||||
|
if rebate_kpi_info and rebate_kpi_info.get("available"):
|
||||||
|
rebate_remark = (f"实测渠补率(YTD累计): Σ571103借方 {rebate_kpi_info['rebate_sum_w']:.2f}万 ÷ "
|
||||||
|
f"Σ5001贷方 {rebate_kpi_info['sales_sum_w']:.2f}万 × 100 = "
|
||||||
|
f"{rebate_kpi_info['rebate_pct']:.2f}%(政策常量 {POLICY_REBATE_RATE_PCT:.2f}% 仅供对照;"
|
||||||
|
f"⑤ 后不再充当变动成本率)")
|
||||||
|
else:
|
||||||
|
rebate_remark = f"{cal['name']}无渠补科目(实测渠补率不适用);变动成本率见 F_BEP_VC_RATE"
|
||||||
batch = f"bep-{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
batch = f"bep-{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||||
written = {}
|
written = {}
|
||||||
changed = []
|
changed = []
|
||||||
@@ -775,9 +857,16 @@ def _fail_closed_cm_non_positive(db, entity_id: int, period: str, months: int,
|
|||||||
if kpi is None:
|
if kpi is None:
|
||||||
written[code] = "no-definition"
|
written[code] = "no-definition"
|
||||||
continue
|
continue
|
||||||
is_rebate = (code == REBATE_KPI)
|
if code == VC_RATE_KPI:
|
||||||
new_val = round(float(rebate_pct), 2) if is_rebate else None
|
new_val = round(float(rebate_pct), 2) # 变动成本率可测 ⇒ 照常落库
|
||||||
rm = rebate_remark if is_rebate else detail
|
rm = vc_rate_remark
|
||||||
|
elif code == REBATE_KPI:
|
||||||
|
new_val = (round(float(rebate_kpi_info["rebate_pct"]), 2)
|
||||||
|
if rebate_kpi_info and rebate_kpi_info.get("available") else None)
|
||||||
|
rm = rebate_remark
|
||||||
|
else:
|
||||||
|
new_val = None
|
||||||
|
rm = detail
|
||||||
existing = db.query(KPIValue).filter(
|
existing = db.query(KPIValue).filter(
|
||||||
KPIValue.kpi_id == kpi.id,
|
KPIValue.kpi_id == kpi.id,
|
||||||
KPIValue.period == period,
|
KPIValue.period == period,
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
-- CMA BEP 变动成本率派生KPI定义 — F_BEP_VC_RATE(2026-10-03 ⑤ 口径统一)
|
||||||
|
-- 依据: 任总 2026-10-03 18:00 DM「统一」+ 任务 finance-20261003-cvp-fix-and-bohai-coverage-fullstack §追加⑤
|
||||||
|
-- 口径: 变动成本率 = 变动成本 ÷ 收入基数(YTD 累计,2位小数)
|
||||||
|
-- · 酣客(e1): 变动成本 = Σ5401借(销货成本)+Σ5403借(税金及附加)+Σ571103借(渠补)−Σ530103贷(厂补)
|
||||||
|
-- · 博海(e2): 变动成本 = Σ6401借(主营业务成本)
|
||||||
|
-- 保本额 = 固定成本月均 ÷ (1 − 变动成本率)
|
||||||
|
-- 说明: F_BEP_REBATE(实测渠补率/博海主营成本率) 保留,⑤ 后**不再充当变动成本率**
|
||||||
|
-- 幂等: NOT EXISTS 守卫,可安全重跑
|
||||||
|
INSERT INTO kpi_definitions
|
||||||
|
(entity_id, kpi_code, kpi_name, dimension, category, formula, formula_desc,
|
||||||
|
data_source_type, data_source, data_owner, frequency, unit, target_value,
|
||||||
|
target_calc_type, threshold_green, threshold_yellow, threshold_red,
|
||||||
|
kpi_level, status, bot_source, epic, created_by)
|
||||||
|
SELECT 1, 'F_BEP_VC_RATE', '变动成本率', 'finance', 'profitability',
|
||||||
|
'round((sum(5401借)+sum(5403借)+sum(571103借)-sum(530103贷))/sum(5001贷)*100,2)',
|
||||||
|
'变动成本率(YTD累计,⑤完整口径)=(Σ5401借+Σ5403借+Σ571103借−Σ530103贷)÷Σ5001贷; 供保本点卡面单一事实源; 缺数期不出值(fail-closed)',
|
||||||
|
'derived', 'BEP派生器 kpi_bep_deriver.py(voucher_details 5401/5403/571103/530103/5001 累计)', '财务部', 'monthly', '%', 0.00,
|
||||||
|
'ratio', NULL, NULL, NULL,
|
||||||
|
'strategic', 'active', 'finance-bot', 'Epic2', 1
|
||||||
|
FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM kpi_definitions WHERE entity_id = 1 AND kpi_code = 'F_BEP_VC_RATE'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO kpi_definitions
|
||||||
|
(entity_id, kpi_code, kpi_name, dimension, category, formula, formula_desc,
|
||||||
|
data_source_type, data_source, data_owner, frequency, unit, target_value,
|
||||||
|
target_calc_type, threshold_green, threshold_yellow, threshold_red,
|
||||||
|
kpi_level, status, bot_source, epic, created_by)
|
||||||
|
SELECT 2, 'F_BEP_VC_RATE', '变动成本率(主营业务成本率)', 'finance', 'profitability',
|
||||||
|
'round(sum(6401借)/sum(6001贷)*100,2)',
|
||||||
|
'变动成本率(实测,YTD累计)=Σ6401借方÷Σ6001贷方(剔结转账腿); 博海无渠补科目; 供保本点卡面单一事实源, 无阈值',
|
||||||
|
'derived', 'BEP派生器 kpi_bep_deriver.py(voucher_details 6401/6001 累计)', '财务部', 'monthly', '%', 0,
|
||||||
|
'ratio', NULL, NULL, NULL,
|
||||||
|
'strategic', 'active', 'finance-bot', 'Epic2', 1
|
||||||
|
FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM kpi_definitions WHERE entity_id = 2 AND kpi_code = 'F_BEP_VC_RATE'
|
||||||
|
);
|
||||||
@@ -81,10 +81,10 @@
|
|||||||
<div class="detail-title">📐 公式</div>
|
<div class="detail-title">📐 公式</div>
|
||||||
<div class="detail-line">{{ card.formula }}</div>
|
<div class="detail-line">{{ card.formula }}</div>
|
||||||
<div v-if="mode === 'book'" class="detail-line sub">
|
<div v-if="mode === 'book'" class="detail-line sub">
|
||||||
账面:固定成本{{ fmt(inputs.admin_avg) }}万 ÷ {{ fmt(inputs.cm_pct, 2) }}% = 保本{{ fmt(current.bep_revenue) }}万(管理费含折旧)
|
账面:固定成本{{ fmt(inputs.admin_avg) }}万 ÷ {{ fmt(inputs.cm_pct, 2) }}% = 保本{{ fmt(current.bep_revenue) }}万(固定成本含折旧)
|
||||||
</div>
|
</div>
|
||||||
<div v-if="mode === 'cash'" class="detail-line sub">
|
<div v-if="mode === 'cash'" class="detail-line sub">
|
||||||
现金:管理费{{ fmt(inputs.admin_avg) }} − 折旧{{ fmt(inputs.dep_avg) }} = {{ fmt(current.admin_avg) }}万 ÷ {{ fmt(inputs.cm_pct, 2) }}% = 保本{{ fmt(current.bep_revenue) }}万
|
现金:固定成本{{ fmt(inputs.admin_avg) }} − 折旧{{ fmt(inputs.dep_avg) }} = {{ fmt(current.admin_avg) }}万 ÷ {{ fmt(inputs.cm_pct, 2) }}% = 保本{{ fmt(current.bep_revenue) }}万
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-col">
|
<div class="detail-col">
|
||||||
@@ -96,9 +96,10 @@
|
|||||||
<div class="detail-kpis">
|
<div class="detail-kpis">
|
||||||
<div class="detail-title">📊 输入KPI</div>
|
<div class="detail-title">📊 输入KPI</div>
|
||||||
<div class="kpi-chips">
|
<div class="kpi-chips">
|
||||||
<span class="kpi-chip">月管理费 <b>{{ fmt(inputs.admin_avg) }}</b>万</span>
|
<span class="kpi-chip">月固定成本 <b>{{ fmt(inputs.admin_avg) }}</b>万</span>
|
||||||
<span class="kpi-chip">月收入(5001) <b>{{ fmt(inputs.sales_avg) }}</b>万</span>
|
<span class="kpi-chip">月收入 <b>{{ fmt(inputs.sales_avg) }}</b>万</span>
|
||||||
<span class="kpi-chip">渠补率 <b>{{ fmt(inputs.rebate_pct, 2) }}</b>%</span>
|
<span class="kpi-chip">变动成本率 <b>{{ fmt(inputs.vc_pct, 2) }}</b>%</span>
|
||||||
|
<span v-if="inputs.rebate_pct != null" class="kpi-chip">渠补率 <b>{{ fmt(inputs.rebate_pct, 2) }}</b>%</span>
|
||||||
<span class="kpi-chip">折旧 <b>{{ fmt(inputs.dep_avg) }}</b>万/月</span>
|
<span class="kpi-chip">折旧 <b>{{ fmt(inputs.dep_avg) }}</b>万/月</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="kpi-src-note">数据源:voucher_details 凭证聚合(YTD月均口径)· {{ card.period }} · 纯只读</div>
|
<div class="kpi-src-note">数据源:voucher_details 凭证聚合(YTD月均口径)· {{ card.period }} · 纯只读</div>
|
||||||
@@ -149,8 +150,8 @@ const lvlText = computed(() => ({ green: '已保本', yellow: '未达保本', re
|
|||||||
const gapColor = computed(() => (current.value.gap <= 0 ? '#67c23a' : current.value.gap <= 50 ? '#e6a23c' : '#f56c6c'))
|
const gapColor = computed(() => (current.value.gap <= 0 ? '#67c23a' : current.value.gap <= 50 ? '#e6a23c' : '#f56c6c'))
|
||||||
const modeNote = computed(() =>
|
const modeNote = computed(() =>
|
||||||
mode.value === 'cash'
|
mode.value === 'cash'
|
||||||
? '固定成本=管理费−折旧(现金视角)'
|
? '固定成本=固定费用合计−折旧(现金视角)'
|
||||||
: '固定成本=管理费(含折旧,账面视角)'
|
: '固定成本=固定费用合计(含折旧,账面视角)'
|
||||||
)
|
)
|
||||||
|
|
||||||
const insight = computed(() => {
|
const insight = computed(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user