ci/woodpecker/push/main Pipeline failed
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.
98 lines
4.7 KiB
Python
98 lines
4.7 KiB
Python
"""口径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
|