Files
cma-management/backend/tests/test_bep_expense_net_caliber.py
Hermes CI Fix 6be903fbc9
ci/woodpecker/push/main Pipeline failed
fix(bep-caliber): expense and profit sums use net (debit minus credit) under carry_forward=0
ASK-20261002-06 fix04u: admin(5602%) and depreciation(560213) take net amount in both the dashboard card helper and the BEP deriver; sales (5001) keeps credit_amount. Verbatim per proposal section 2. No DB writes, no kpi_values backfill.
2026-10-02 11:22:38 +08:00

98 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""口径4「费用取净额」单测 — ASK-20261002-06(2026-10-02)
口径依据: /srv/hermes/wisdom/_decisions/cma-expense-profit-net-caliber-20261002.md
「任何费用/利润取数一律净额(借−贷)」
覆盖两条实现面取数助手(改前均只认 debit/credit 单边字段):
· app/api/dashboard.py::_bep_sum_field(卡片现算面)
· scripts/kpi_bep_deriver.py::_sum_field(派生落库写入面)
断言要点(含负对照,退回 debit_amount 口径时本文件必红):
1. field="net" = 借−贷(行域上存在贷方冲销行 ⇒ 净额 ≠ 借方合计);
2. carry_forward==0 过滤仍在位(结转腿行不参与);
3. 收入侧 sales 仍取 credit_amount,未改为净额(改净额会变负)。
4. `5602%` LIKE 前缀天然包含 `560213`(账面口径含折旧),故 admin 净额 = 两科目之和。
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import datetime # noqa: E402
from app.models import VoucherDetail # noqa: E402
from app.api.dashboard import _bep_sum_field # noqa: E402
from scripts import kpi_bep_deriver as D # noqa: E402
D_DATE = datetime.datetime(2026, 8, 31)
PERIOD = "2026-08"
def _row(db, no, code, debit=0.0, credit=0.0, cf=0):
db.add(VoucherDetail(entity_id=1, voucher_no=str(no), voucher_date=D_DATE,
subject_code=code, subject_name="单测科目",
debit_amount=debit, credit_amount=credit,
summary="口径4 单测", carry_forward=cf, period=PERIOD))
db.flush()
def _fixture(db):
"""借贷双侧行域 + 一条结转腿(carry_forward=1)行。
560202 : 借 1000 / 贷 200 → 净额 800(借方合计 1000)
560213 : 借 500 / 贷 100 → 净额 400(借方合计 500)
5001 : 贷 8000 / 借 0 → 收入仍取贷方 8000(取净额 = -8000)
560202 cf=1 : 借 9999 → 结转腿,任何口径下都不得计入
LIKE '5602%' 命中 560202 + 560213(账面口径含折旧)⇒ admin 净额 = 1200。
"""
_row(db, 91001, "560202", debit=1000.0)
_row(db, 91002, "560202", credit=200.0)
_row(db, 91003, "560213", debit=500.0)
_row(db, 91004, "560213", credit=100.0)
_row(db, 91005, "5001", credit=8000.0)
_row(db, 91006, "560202", debit=9999.0, cf=1)
def test_dashboard_net_caliber_admin_and_dep(db):
"""改后:dashboard._bep_sum_field 支持 field='net'=借−贷;借方口径仍可用(负对照)。"""
_fixture(db)
net_admin = _bep_sum_field(db, 1, PERIOD, PERIOD, "net", ("like", "5602"))
debit_admin = _bep_sum_field(db, 1, PERIOD, PERIOD, "debit_amount", ("like", "5602"))
net_dep = _bep_sum_field(db, 1, PERIOD, PERIOD, "net", ("eq", "560213"))
debit_dep = _bep_sum_field(db, 1, PERIOD, PERIOD, "debit_amount", ("eq", "560213"))
assert abs(net_admin - 1200.0) < 1e-6, net_admin # (1000-200) + (500-100)
assert abs(net_dep - 400.0) < 1e-6, net_dep # 500 - 100
# 负对照:退回 debit_amount 口径时上面的 net 断言必红(1500/500 ≠ 1200/400)
assert abs(debit_admin - 1500.0) < 1e-6, debit_admin
assert abs(debit_dep - 500.0) < 1e-6, debit_dep
def test_dashboard_net_excludes_carry_forward_rows(db):
"""carry_forward==0 过滤在位:结转腿行(cf=1,借 9999)不参与。"""
_fixture(db)
net_admin = _bep_sum_field(db, 1, PERIOD, PERIOD, "net", ("like", "5602"))
assert abs(net_admin - 1200.0) < 1e-6, net_admin # 含 cf=1 行则会是 1200+9999
def test_dashboard_sales_still_credit_amount(db):
"""收入侧未改:5001 仍取 credit_amount(若改净额 → 负数)。"""
_fixture(db)
sales_credit = _bep_sum_field(db, 1, PERIOD, PERIOD, "credit_amount", ("eq", "5001"))
sales_net = _bep_sum_field(db, 1, PERIOD, PERIOD, "net", ("eq", "5001"))
assert abs(sales_credit - 8000.0) < 1e-6, sales_credit
assert sales_net < 0, sales_net # 这正是「收入不得取净额」的反证
def test_deriver_net_caliber_parity(db):
"""派生器 _sum_field 与卡片助手同口径(两文件同批改的目的:避免同页两口径)。"""
_fixture(db)
d_net_admin = D._sum_field(db, 1, PERIOD, PERIOD, "net", ("like", "5602"))
d_net_dep = D._sum_field(db, 1, PERIOD, PERIOD, "net", ("eq", "560213"))
d_net_rebate = D._sum_field(db, 1, PERIOD, PERIOD, "net", ("eq", "571103"))
assert abs(d_net_admin - 1200.0) < 1e-6, d_net_admin
assert abs(d_net_dep - 400.0) < 1e-6, d_net_dep
assert abs(d_net_rebate - 0.0) < 1e-6, d_net_rebate # 本夹具无 571103 行
assert abs(d_net_admin - _bep_sum_field(db, 1, PERIOD, PERIOD, "net", ("like", "5602"))) < 1e-6