Files
Aislo/resources/tester/test_704_settle.py
T
eomsangdonandClaude Sonnet 5.5 3c2c82c78e fix(M02 기준 목록·계산서): 끝수 = B09 산근 규칙(줄마다 버림 · 비목마다 버림)으로 맞춤 — 계산서 5,654 대 B09 5,651 어긋남 (PLAN 704-2 딸림)
- 새 `M02_Logic_Settle` — 엔진 구조 그대로 줄 ROUNDDOWN 1 · 비목 ROUNDDOWN 0 · 작업량 Q ROUND 2 · 하위 로직 같은 규칙
- 기준 목록 금액 · 계산서 줄 · 소계 · 계가 같은 값 · 목록 저장본 꼴 번호 올림 · 머리 글에 지식DB 단수처리 근거
- 시험: 거울 test_704_settle(B09 _money · block_totals) · 옛 목록 시험은 끝수 차 3 원 안으로 고침

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 18:19:09 +09:00

76 lines
3.0 KiB
Python

"""704-2 딸림 — 기준 목록 · 계산서 끝수 = B09 산근 규칙(줄마다 ROUNDDOWN 1 · 비목마다 ROUNDDOWN 0).
거울: 엔진 줄(수량 · 단가)에 B09 `_money` · `block_totals` 를 그대로 씌운 값 = `settle` 값.
"""
import re
import sys
from decimal import Decimal
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B09_Estimation import B09_Estimation_Sangeun as sg # noqa: E402
from M01_MasterData import M01_MasterData_Store as m01 # noqa: E402
from M02_MasterTemplete import M02_Logic_Settle as st # noqa: E402
from M02_MasterTemplete import M02_Logic_Standard as ls # noqa: E402
mf = m01.cm.mf
PART_OF = {"노무비": "labor", "재료비": "material", "경비": "expense"}
def _plain(row: dict) -> bool:
"""로직 줄 · 덧줄 · 작업량 Q 가 없는 돈 로직 — 엔진 줄만으로 B09 값을 낼 수 있음."""
return (
str(row.get("결과단위") or "").startswith("원")
and not row.get("덧줄")
and not row.get("끝수")
and not any(i.get("종류") == "로직" for i in row.get("호표") or [])
and not any(re.fullmatch(r"Q\d*", s["이름"]) for s in row.get("중간") or [])
)
def test_줄마다_버림_비목마다_버림_B09와_같음():
files, whole = m01.loaded()
checked = 0
for _, data in m01._logic_files(files):
for row in data.get("줄") or []:
if not _plain(row) or checked >= 60:
continue
base, _ = ls.axes(whole, row)
given = mf.fill_empty(row, base)
got = mf.run(whole, row["키"], given)
lines = []
for line in got["줄"]:
one = dict.fromkeys(PART_OF.values(), 0.0)
for part, money in line["비목"].items():
one[PART_OF[part]] = sg._money(1.0, float(money)) if money is not None else None
lines.append(one)
if any(v is None for one in lines for v in one.values()):
continue
want = sg.block_totals(lines)
mine = st.settle(whole, row["키"], given)
for name, part in PART_OF.items():
assert float(mine[name]) == want[part], (row["키"], name)
assert float(mine["계"]) == want["sum"], row["키"]
checked += 1
assert checked >= 30
def test_계산서_줄_소계_계가_맞물림():
files, whole = m01.loaded()
key = "GF000089"
got = ls.sheet(key, {})
assert got["묶음"], key
for group in got["묶음"]:
lines = [Decimal(str(x["금액"] or 0)) for x in group["줄"]]
assert all(v == v.quantize(Decimal("0.1")) for v in lines), group["비목"]
assert group["소계"] == int(group["소계"])
assert group["소계"] <= sum(lines) < group["소계"] + 1 + Decimal("0.05"), group["비목"]
assert got["계"] == sum(g["소계"] for g in got["묶음"])
assert got["계"] == ls._money(
whole, whole.logic(key), ls._given(whole, whole.logic(key), {})[0]
)