fix(b09): 공사원가계산서 직접재료비 이동 대상을 총괄 가. 순공사비계(direct)로 (261-2)

- 이동 대상 키 material 이 총괄 줄에 없어 빨강 상자가 안 뜸
- 시험 추가 - 모든 원가 줄 이동 대상이 총괄 줄 키에 있는지

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SE9MxeXTu2uaDZ55BJowAY
This commit is contained in:
2026-10-11 15:16:12 +09:00
co-authored by Claude Sonnet 5.5
parent 6e53acfcac
commit f0f35c5749
2 changed files with 55 additions and 1 deletions
@@ -271,7 +271,7 @@ export const buildTotal: TabBuild = (ctx): TabTable => {
/** 원가계산서 줄 [키(cost · 총괄 줄), 구분, 비목, 세목] */
const COST_ROWS: Array<[string, string, string, string, string?]> = [
["direct_material", "순공사원가", "재료비", "직접재료비", "material"],
["direct_material", "순공사원가", "재료비", "직접재료비", "direct"],
["indirect_material", "순공사원가", "재료비", "간접재료비"],
["scrap", "순공사원가", "재료비", "작업설 · 부산물 등(△)"],
["material", "순공사원가", "재료비", "소계", "direct"],
@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
"""261-2 B09 공사원가계산서 줄 이동 대상 — 모든 줄의 대상 키가 총괄 줄 키에 있음(빈 대상 = 이동 없음 허용).
직접재료비 → 총괄 「가. 순공사비계」(direct) · 직접노무비 · 산출경비와 같은 자리.
"""
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
B09 = ROOT / "B09_Estimation"
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
SUMS = {"material": 194234417, "labor": 346800554, "expense": 169324165, "supply_net": 69474220}
BODY = f"""
const user = {{ estimate: {{}} }};
const ctx = {{ sheets: {{ total: computeTotal({json.dumps(SUMS)}, user.estimate) }}, payload: {{}}, user, setUser: () => {{}} }};
const T = buildTotal(ctx), C = buildCost(ctx);
console.log(JSON.stringify({{
totalKeys: T.rows.map((r) => r.key),
cost: C.rows.map((r, i) => [r.key, C.link(i)]),
}}));
"""
def _run() -> dict:
names = ("Calc_Base", "Calc_Total", "UI_Tabs", "UI_Tabs_Total")
script = "".join(
IMPORTS.sub("", (B09 / f"B09_Estimation_{n}.ts").read_text(encoding="utf-8")) for n in names
)
result = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript"],
input=script + BODY, capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert result.returncode == 0, result.stderr
return json.loads(result.stdout)
def test_모든_원가_줄_이동_대상이_총괄_줄에_있음():
got = _run()
keys = set(got["totalKeys"])
missing = [k for k, link in got["cost"] if link and link["key"] not in keys]
assert not missing, missing
assert len(got["cost"]) > 20
def test_직접재료비_이동_대상_direct():
links = {k: link for k, link in _run()["cost"]}
assert links["direct_material"] == {"tab": "total", "key": "direct"}
assert links["direct_labor"] == links["expense"] == links["direct_material"]