Files
Aislo/resources/tester/test_155_b09_sources.py
T
eomsangdonandClaude Opus 5.5 82a3cb5e7d feat(b09): 호표 갈래 뒤집기 — 일위대가화 줄 = 대가 · 단가산출 줄 = 산근(155-3 · 155-4)
- 일위대가목록 = 구조물 조합 + 사전조합 + 일위대가화(승격 · 옛 SP) 줄 · 단가산출 줄 = 산근 줄
- 산근 줄 수량 = 수량 그대로(배 ≠ 1 로직 금액 1/배 흠) · 대가 줄만 ÷ 배
- 품셈 블록 로직 줄 판정 = 산근 풀이와 같은 call_of(기계경비 = 중기 · 그 밖 = 하위 산근)
- 비고 「산근 n호표」(Calc_Labor 파이썬 · TS 짝) · 산근 줄 누름 = 단가산출근거 탭
- 산근목록 원천 = 단가산출 줄 + 사전조합 안 줄 + 일위대가화 줄 하위 로직(노무 · 자재 · 중기 줄 제외)
- 산근 자재 자리표시 키(MO:경유:…) = 자재단가대비표 키로
- 시험: 145-1 다섯 · 145-2 셋 새 갈래로(머리에 근거 한 줄) · 새 test_155_b09_sources

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
2026-10-09 19:20:39 +09:00

110 lines
5.6 KiB
Python

"""155-3 · 155-4 B09 호표 갈래 — 사용자 뜻(10-9 둘째 · 145 정함 뒤집음 · M02 구분 짝 · 엑셀 꼴):
일위대가화(승격) 줄 = 일위대가 호표(대가 블록) · 단가산출 줄 = 산근 ·
일위대가화 줄의 하위 로직 줄 = 산근(대가 블록 산근 줄 키 = 산근 목록 키) ·
산근 줄 수량 = 수량 그대로(배 10 로직 금액 1/배 흠) · 비고 「산근 n호표」(파이썬 · TS 짝) ·
산근 자재 자리표시 키(「MO:경유:…」) = 자재단가대비표 키.
"""
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
for path in (ROOT, ROOT / "resources" / "tester"):
if str(path) not in sys.path:
sys.path.insert(0, str(path))
import test_145_2_b09_labor_calc as t145 # noqa: E402
from B09_Estimation import B09_Estimation_Prices as prices # noqa: E402
from B09_Estimation import B09_Estimation_Sangeun as sg # noqa: E402
from B09_Estimation.B09_Estimation_Calc_Base import compute_base, rounddown # noqa: E402
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor # noqa: E402
from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs # noqa: E402
from B09_Estimation.B09_Estimation_Linked import linked_resource_keys, used_keys # noqa: E402
from B09_Estimation.B09_Estimation_Router import compute_sheets # noqa: E402
from M02_MasterTemplete import M02_Item_Prices as item_prices # noqa: E402
PILE = "GF000233" # 말뚝 다듬기 — 원/10개(배 10) · 인력 줄 둘
PIPE = "GF000174" # 흄관 부설 — 하위 로직 줄(GF000155 · GF000160) = 하위 산근
FUEL = "GF000011" # 재료 줄 「MO:경유:{지역}」 자리표시
def _sheets_of(root):
inputs = prices.build_inputs(None, linked_resource_keys(root))
inputs["daega"] = daega_inputs(root)
inputs["hopyo"] = {}
inputs["bill"] = {"items": []}
return inputs, compute_sheets(root, inputs, {})
def _blk(sheets, key):
return next(b for b in sheets["daega_blocks"] if b["key"] == key)
def test_155_3_배_10_로직_산근_줄_수량_그대로_대가_줄만_나눔(tmp_path):
root = t145._project(tmp_path / "p", [(100, "SS000901"), (200, "SS000902")])
t145._snap(root, "pipe@100.0", "SS000901", [{"로직": PILE, "수량": {"손": 20}}], {})
t145._snap(
root, "pipe@200.0", "SS000902", [{"로직": PILE, "승격": True, "수량": {"손": 20}}], {}
)
inputs, sheets = _sheets_of(root)
owners = sheets["daega_owners"]
(sg_line,) = _blk(sheets, owners["pipe@100.0"])["lines"]
(dg_line,) = _blk(sheets, owners["pipe@200.0"])["lines"]
assert (sg_line["kind"], sg_line["qty"]) == ("sangeun", 20) # 산근 단가 = 1 개
assert (dg_line["kind"], dg_line["qty"]) == ("daega", 2) # 대가 블록 = 10 개
row = next(r for r in sheets["sg_list"] if r["key"] == sg_line["key"])
assert sg_line["ref_no"] == f"산근 {row['no']}호표" and dg_line["ref_no"].startswith("대가")
# 같은 일 · 같은 수량 = 금액 거의 같음(끝수 자리만 다름 · 예전 흠 = 1/10)
assert sg_line["amount"] is not None and dg_line["amount"] is not None
assert abs(sg_line["amount"] - dg_line["amount"]) <= 0.02 * dg_line["amount"]
def test_155_4_일위대가화_하위_로직_줄_산근_목록에(tmp_path):
root = t145._project(tmp_path / "p", [(100, "SS000901")])
t145._snap(
root, "pipe@100.0", "SS000901", [{"로직": PIPE, "승격": True, "수량": {"손": 1}}], {}
)
inputs, sheets = _sheets_of(root)
(call,) = _blk(sheets, sheets["daega_owners"]["pipe@100.0"])["lines"]
pipe = _blk(sheets, call["key"])
assert pipe["kind"] == "품셈" and call["kind"] == "daega"
subs = [ln for ln in pipe["lines"] if ln["kind"] == "sangeun"]
listed = {r["key"]: r for r in sheets["sg_list"]}
assert {ln["key"][:11] for ln in subs} >= {"SG:GF000155", "SG:GF000160"}
for ln in subs: # 대가 블록 산근 줄 = 산근 목록 줄(같은 키) · 비고 · 단가 이음
assert ln["key"] in listed and ln["ref_no"] == f"산근 {listed[ln['key']]['no']}호표"
assert ln["prices"]["labor"] == listed[ln["key"]]["labor"]
assert all(r["logic"] != PIPE for r in sheets["sg_list"]) # 일위대가화 줄 자체 = 산근 아님
def test_155_4_산근_자재_자리표시_키_대비표로(tmp_path):
folder = tmp_path / "a"
folder.mkdir()
files = {
"단가.json": item_prices.to_json(item_prices.collect([FUEL])),
"일위대가.json": {"줄": {"종류": "일위대가", "담은로직": [{"로직": FUEL}]}},
"입력값.json": {},
}
for name, data in files.items():
(folder / name).write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
sheets = compute_base(prices.build_inputs(None, used_keys(folder)), {})
(block,) = sg.collect([("s1", folder)], sheets)["sg"]
fuel = [ln for ln in block["lines"] if ln["kind"] == "material"]
assert fuel and all(ln["ref"]["key"].startswith("MO0") for ln in fuel)
applied = {r["key"]: r["applied"] for r in sheets["compare"]}
for ln in fuel:
assert ln["material"] == rounddown(applied[ln["ref"]["key"]] * ln["qty"], 1)
def test_155_3_비고_산근_n호표_거울():
sangeun = {k: {**v, "no": n} for n, (k, v) in enumerate(t145.SANGEUN.items(), 1)}
py = json.loads(json.dumps(compute_labor(t145.EXCEL, t145.SHEETS, sangeun), ensure_ascii=False))
ts = t145._ts(t145.EXCEL, t145.SHEETS, sangeun)
assert t145._same(py, ts["out"])
notes = [
ln.get("ref_no") for b in py["daega_blocks"] for ln in b["lines"] if ln["kind"] == "sangeun"
]
assert notes and notes[0] == "산근 1호표" and all(n and n.startswith("산근 ") for n in notes)