fix(dashboard): bep-card terms 分支补字段别名映射(修 289cc8f 引入的 e2 500)
ci/woodpecker/push/main Pipeline was successful

289cc8f(§4-1 博海固定成本仅借方)在 dashboard._bep_sum_field 新增 terms 分支,
把 bep_caliber 的 terms 记法字段名(debit/credit)原样传给 getattr(VoucherDetail, field)
⇒ AttributeError: type object 'VoucherDetail' has no attribute 'debit'
⇒ GET /api/cma/dashboard/bep-card(entity=2 博海)稳定 500(entity=1 走 filters 分支不受影响)。

修复:terms 分支内做字段名映射 debit→debit_amount / credit→credit_amount(与
profit_lines.FIELD_MAP、kpi_bep_deriver._TERM_FIELD 同式),再递归聚合。

回归测试:tests/test_profit_summary_real_caliber_20261014.py 新增
test_dashboard_card_e2_terms_mode_field_alias(修复前 RED:复现同一 AttributeError;
修复后 GREEN:e2 固定成本 98,000 元 / 卡面 9.80 万)。
This commit is contained in:
Hermes Fullstack
2026-10-16 18:28:50 +08:00
committed by Hermes CI Fix
parent 68ca27109e
commit a52faa90a6
2 changed files with 31 additions and 1 deletions
+10 -1
View File
@@ -72,6 +72,13 @@ BEP_DEP_SUBJECT = "560213"
# (酣客 e1 = 84.61;博海 e2 = None,因无渠补科目)。端点各处直接取口径值,不做回落。
# bep_caliber 的带号科目项(terms)以 `debit`/`credit` 命名,本模块聚合用的是模型列名
# `debit_amount`/`credit_amount` ⇒ 必须做字段名映射。
# 2026-10-16 修复(P0):289cc8f2 引入的 terms 分支把 'debit' 原样传入 ⇒
# `getattr(VoucherDetail, 'debit')` AttributeError ⇒ e2 博海 `GET /api/cma/dashboard/bep-card` 500。
_BEP_FIELD_ALIAS = {"debit": "debit_amount", "credit": "credit_amount", "net": "net"}
def _bep_sum_field(db, entity_id: int, period_from: str, period_to: str,
field: str, subject_filter=None, *, exclude_closing: bool = False,
subject_filters=None, terms=None) -> float:
@@ -91,8 +98,10 @@ def _bep_sum_field(db, entity_id: int, period_from: str, period_to: str,
if terms:
total = 0.0
for sign, code, tfield in terms:
# terms 记法字段名 → 模型列名(缺此映射 ⇒ getattr(VoucherDetail,'debit') AttributeError)
col_field = _BEP_FIELD_ALIAS.get(str(tfield), str(tfield))
total += (1.0 if sign == "+" else -1.0) * _bep_sum_field(
db, entity_id, period_from, period_to, tfield,
db, entity_id, period_from, period_to, col_field,
("like", str(code)[:-1]) if str(code).endswith("%") else ("eq", code),
exclude_closing=exclude_closing)
return total
@@ -228,6 +228,27 @@ class TestCaliberDriven:
D._sum_field(db, 1, PERIOD, PERIOD, "net", ("like", "5602")) == 120_000.0
assert PL.sum_field(db, 1, PERIOD, PERIOD, "credit_amount", ("eq", "5001")) == 2_634_900.0
def test_dashboard_card_e2_terms_mode_field_alias(self, db: Session):
"""289cc8f 回归(P0):e2 固定成本走 terms(仅借方)时,`dashboard._bep_sum_field`
必须把 terms 记法字段名(debit/credit)映射到模型列名(debit_amount/credit_amount);
否则 `getattr(VoucherDetail, 'debit')` AttributeError ⇒ `GET /dashboard/bep-card` 500。
"""
seed_e2(db)
from app.api.dashboard import _bep_sum_field, _bep_inputs
from app.utils.bep_caliber import fixed_cost_spec
cal2 = ENTITY_CALIBERS[2]
mode, spec = fixed_cost_spec(cal2)
assert mode == "terms", spec
raw = _bep_sum_field(db, 2, PERIOD, PERIOD, "net", terms=spec,
exclude_closing=cal2["exclude_closing"])
# §4-1 仅借方:6601 60,000 + 6602 30,000 + 6603 8,000 = 98,000 元
# (6602 贷 5,000 / 6603 贷 20,000 不进固定;summary 含「结转」腿被剔除)
assert raw == 98_000.0, raw
inp = _bep_inputs(db, 2, cal2, PERIOD, PERIOD, 1)
assert inp["sales_avg"] == 100.00, inp
assert inp["admin_avg"] == 9.80, inp
# ── L2 勾稽自检 ───────────────────────────────────────────────────────────