feat(792-3): run_line · B09 산근 셈이 엔진 새 의미를 따름 — 네 길 같은 금액
- master_cut: 엔진 답 덧칸 `셈법`(새 칸 뗀 작은 로직) · `plain(got, gone)` 이 셈법 있으면 줄 자리 · 합밖 · 단계 · 블록 끝전 · 조정 그대로(켜쌓기율 · 별도계상 뒤 다시 버림이 큰나머지 · 조정을 덮던 흠) · `kept` - M02_Labor_Doc: run_line 답이 단계 · 셈법을 넘김 · drop_apart 가 plain 에 뺄 줄 차례를 줌(686 줄 · 늘지 않음) - 새 B09_Estimation_Sangeun_Sum: 산근 풀이 · 산근 글이 새 의미 로직만 엔진 규칙(표시 · 줄 자리 · 합밖 · 번지 · 단계 · 끝전 · 조정) · 옛 로직 = Q ROUND 2 · ROUNDDOWN 1 · 0 그대로 - B09 산근 글: 로직 줄(중기 세 줄) 금액을 줄 하나로 묶어 차례를 호표와 맞춤(합 같음) - 지킴 시험 test_792_3_engine_paths: 견본 넷(봉화 · 소광 · 영월 · 임도) + 단위 로직 = 엔진 · M02 SK · SC 한 줄 · B09 산근(글 계 포함) 같은 금액 · 다시 버림 셈법 지킴 - test_791_1: 견본 넷을 만들기 함수로 떼어 냄(같은 기대) - test_222_1 낡은 기대 셋 고침: 751-3 끝수 뒤 별도계상 뺀 기준 = 남은 줄 비목 합 원 버림(소수 없음) · 옛 기대는 「원 버린 기준 − 소수 재료 금액」이라 0.6 원 어긋남 - test_145_1: 작업량 Q 이름 규칙이 B09_Estimation_Sangeun_Sum 으로 옮김 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
"""792-3 엔진 새 의미 네 길 지킴 시험 — 같은 로직 JSON 이 ① 엔진 직접(`master_cut.engine`) ·
|
||||
② M02 단가산출 SK 셈(`M02_Sangeun_Calc.run`) · ③ 일위대가 SC 한 줄(수량 1 · `M02_Labor_Doc.calc`) ·
|
||||
④ B09 산근 풀이(`B09_Estimation_Sangeun.Solver` · 단가 = 같은 값의 B09 목록표)에서 같은 금액.
|
||||
|
||||
견본 = 791-1 실무 손 옮김 넷(봉화 D00102 · 울진소광 D01489 · 동부청영월 D00003 · 북부청임도 18 =
|
||||
엑셀 금액) + 합밖 · 제외 · 조정 단위 로직. 켜쌓기율 · 별도계상 뒤 다시 버림(`master_cut.plain`)도
|
||||
엔진 셈법(줄 자리 · 끝전 · 조정)을 지킴. 옛 로직(새 칸 없음) 전후 같음 = `test_791_1` M01 지문.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import importlib
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
import resources.tester.test_791_1_engine_semantics as t
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sg
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as sc
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
|
||||
mcut = importlib.import_module("master_cut")
|
||||
D = Decimal
|
||||
COSTS = ("노무비", "재료비", "경비", "계")
|
||||
PART = {"labor": "노무비", "material": "재료비", "expense": "경비", "sum": "계"}
|
||||
EQ = "EQ990001"
|
||||
|
||||
|
||||
def _units():
|
||||
"""합밖 · 제외 단계 · 조정 · 큰나머지 · 줄 자리를 한 로직에(791-1 단위 견본 바탕)."""
|
||||
master = t._base(
|
||||
덧줄=[
|
||||
{"이름": "공구손료", "식": "단계.'인부 소계'.노무비 * 3 / 100", "비목": "경비"},
|
||||
{"이름": "잡", "식": "번지.A01.재료비 * 1.5 / 100", "비목": "재료비"},
|
||||
{"이름": "덧", "식": "0.6", "비목": "노무비", "자리": {"방법": "올림", "자리": 0}},
|
||||
],
|
||||
단계=[
|
||||
{"이름": "자재 소계", "단": 1, "줄": ["갑", "을"]},
|
||||
{"이름": "인부 소계", "단": 1, "줄": ["보통", "특별"]},
|
||||
{"이름": "경비 소계", "단": 1, "줄": ["공구손료"], "제외": True},
|
||||
],
|
||||
끝전="큰나머지",
|
||||
조정={"배율": {"재료비": D("0.9")}},
|
||||
)
|
||||
row = master.logic("GX990001")
|
||||
row["호표"][1]["합밖"] = True
|
||||
return master, row, None
|
||||
|
||||
|
||||
CASES = {"봉화": t.봉화, "소광": t.소광, "영월": t.영월, "임도": t.임도, "단위": _units}
|
||||
|
||||
|
||||
def _key(row) -> str:
|
||||
return row["키"]
|
||||
|
||||
|
||||
def _engine(master, row) -> dict:
|
||||
got = t._calc(master, _key(row))
|
||||
return {k: got[k] for k in COSTS}
|
||||
|
||||
|
||||
def _sk(master, row) -> dict:
|
||||
doc = {"키": "SK-990001", "이름": "시험", "단위": "식", "바탕": {"로직": _key(row)}}
|
||||
got = sc.run(sd.clean(doc, None, master), master)
|
||||
return {k: got["기준"][k] for k in COSTS}
|
||||
|
||||
|
||||
def _sc(master, row) -> dict:
|
||||
mats = {
|
||||
str(n): {"별도계상": "직접계산"} for n, x in enumerate(row["호표"]) if x["종류"] == "재료"
|
||||
}
|
||||
line = {"로직": _key(row), "입력값": {}, "수량": {"손": 1}, "자재표시": mats}
|
||||
got = labor.calc({"담은로직": [line], "자재": []}, {}, whole=master, pills=[])
|
||||
return {k: got["반영합"][k] for k in COSTS}
|
||||
|
||||
|
||||
def _b09_master(master, row):
|
||||
"""B09 = 기계 줄 단가를 중기목록표에서 — 기계경비 로직이 부르는 EQ 키를 잡게
|
||||
시간당 로직에 한 줄."""
|
||||
hour = copy.deepcopy(master.index["GX"].get("GX990009") or {})
|
||||
if hour:
|
||||
hour["호표"] = [t._mat(EQ, "1", "기계", 비목="경비")]
|
||||
master.index["GX"]["GX990009"] = hour
|
||||
master.index["EQ"] = {EQ: {"키": EQ, "이름": "시험 기계", "값": D(0)}}
|
||||
return master
|
||||
|
||||
|
||||
def _b09(build, row) -> dict:
|
||||
master, row, _ = build()
|
||||
master = _b09_master(master, row)
|
||||
files = master.index
|
||||
machine = next((x["값묶음"] for x in row["호표"] if "값묶음" in x), None)
|
||||
sheets = {
|
||||
"labor": [{"key": k, "price": float(r["값"])} for k, r in files.get("LB", {}).items()],
|
||||
"compare": [
|
||||
{"key": k, "applied": float(r["물가자료"])} for k, r in files.get("MT", {}).items()
|
||||
],
|
||||
"machine": [{"key": EQ, **{p: float(machine[c]) for p, c in PART.items() if c in machine}}]
|
||||
if machine
|
||||
else [],
|
||||
}
|
||||
block = sg.Solver(sheets).add(master, {"code": _key(row), "given": {}}, None)
|
||||
assert block["why"] is None, block["why"]
|
||||
texts = [x for ln in block["lines"] for x in ln.get("text") or []]
|
||||
assert not [x for x in texts if "못" in x or "빈칸" in x], texts # 산근 글도 같은 셈
|
||||
assert block["foot"].endswith(f"= {block['sum']:,.0f}"), block["foot"]
|
||||
return {PART[p]: D(str(block[p])) for p in PART}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", list(CASES))
|
||||
def test_네_길_같은_금액(name):
|
||||
build = CASES[name]
|
||||
master, row, want = build()
|
||||
one = _engine(master, row)
|
||||
if want is not None:
|
||||
assert one == want # 엑셀 금액
|
||||
assert _sk(*build()[:2]) == one
|
||||
assert _sc(*build()[:2]) == one
|
||||
assert _b09(build, row) == one
|
||||
|
||||
|
||||
def test_단위_로직_새_의미가_금액을_바꿈():
|
||||
master, row, _ = _units()
|
||||
got = _engine(master, row)
|
||||
plain = copy.deepcopy(row)
|
||||
for k in ("끝전", "조정", "단계"):
|
||||
plain.pop(k)
|
||||
for x in (*plain["호표"], *plain["덧줄"]):
|
||||
for k in ("합밖", "자리", "번지"):
|
||||
x.pop(k, None)
|
||||
assert got != _engine(t._master([plain], t.LAB, t.MAT), plain) # 시험이 새 길을 거침
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", ["영월", "단위"])
|
||||
def test_다시_버림도_셈법_지킴(name):
|
||||
"""켜쌓기율 · 별도계상 뒤 다시 버림(`plain`) — 셈법이 있으면
|
||||
줄 자리 · 합밖 · 끝전 · 조정 그대로."""
|
||||
master, row, _ = CASES[name]()
|
||||
out = labor.run_line(master, _key(row), {})
|
||||
assert "셈법" in out and out["단계"]
|
||||
again = mcut.plain(out)
|
||||
assert again["기준"] == out["기준"]
|
||||
assert [x["계"] for x in again["단계"]] == [x["계"] for x in out["단계"]]
|
||||
old = mcut.plain({k: v for k, v in out.items() if k != "셈법"})
|
||||
if name == "영월":
|
||||
assert (
|
||||
old["기준"]["계"] == 5470 and again["기준"]["계"] == 5471
|
||||
) # 옛 다시 버림 = 1 원 모자람
|
||||
Reference in New Issue
Block a user