"""719-4 B09 단가산출근거 = 공용 계산서 꼴 — 서버 줄 덧칸 · 화면 묶음 옮김(셈 없음) · 금액 같음. - 서버 산근 줄 `sheet`{쓰는입력 · 기준단위 · 환산근거}(714-1 `master_sheet_line` 한 벌) · 블록 `기준단위` - 화면 `sheetGroups` = 노무비 · 재료비 · 경비 묶음(소계 = 블록 비목 총계 · 줄 금액 = 서버 값) → 공용 `bundleLines` = 인력 · 재료 · 경비(장비 한 줄 + 펼침 = 중기사용료 넉 줄) · 하위(산근 n호표) - 덧칸만 더함 — 줄 값 · 총계 · 목록표는 덧칸 빼면 옛 풀이와 같음(`text` 줄 셈 그대로) """ import json import re import subprocess import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] for p in (ROOT, ROOT / "resources" / "tester"): if str(p) not in sys.path: sys.path.insert(0, str(p)) from test_719_3_sangeun_cover import _inputs, _user # noqa: E402 from B09_Estimation import B09_Estimation_Sangeun as sangeun # noqa: E402 from B09_Estimation.B09_Estimation_Calc_Base import compute_base # noqa: E402 IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE) @pytest.fixture(scope="module") def made(): sheets = compute_base(_inputs(), {}) return sheets, sangeun.collect([], sheets, sangeun.hand_sources(_user())) def _ts(sg, machines): files = [ "common_util/common_util_money.ts", "B09_Estimation/B09_Estimation_Calc_Base.ts", "A00_Common/calc_sheet.ts", "B09_Estimation/B09_Estimation_UI_Tabs_Sangeun_Sheet.ts", ] src = "".join(IMPORTS.sub("", (ROOT / f).read_text("utf-8")) for f in files) body = ( "\nconst currentLanguageIndex = 0;" f"\nconst sg = {json.dumps(sg, ensure_ascii=False)};" f"\nconst machines = {json.dumps(machines, ensure_ascii=False)};" "\nconst noOf = Object.fromEntries(sg.map((b) => [b.key, b.no]));" "\nconsole.log(JSON.stringify(sg.map((b) => {" "\n const groups = sheetGroups(b, machines, noOf);" "\n return { key: b.key, groups, bundles: bundleLines(groups) };" "\n})));" ) result = subprocess.run( ["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript", "-"], input=src + body, capture_output=True, text=True, encoding="utf-8", timeout=60, ) # fmt: skip assert result.returncode == 0, result.stderr return {x["key"]: x for x in json.loads(result.stdout)} def test_서버_줄_덧칸(made): _sheets, sg = made for b in sg["sg"]: assert "기준단위" in b for ln in b["lines"]: assert set(ln["sheet"]) >= {"쓰는입력", "기준단위", "환산근거"}, ln assert "_goods" not in ln net = next(b for b in sg["sg"] if b["logic"] == "GC000337") assert net["기준단위"] == "㎡" machine = next(ln for ln in net["lines"] if ln["kind"] == "machine") assert "크레인" in machine["sheet"]["쓰는입력"] def test_화면_묶음_옮김_금액_그대로(made): sheets, sg = made got = _ts(sg["sg"], sheets["machine_blocks"]) for b in sg["sg"]: groups = got[b["key"]]["groups"] cost = {"노무비": "labor", "재료비": "material", "경비": "expense"} for g in groups: part = cost[g["비목"]] assert g["소계"] == b[part] assert [x["금액"] for x in g["줄"]] == [ ln[part] for ln in b["lines"] if part in _parts(ln) ] net = next(b for b in sg["sg"] if b["logic"] == "GC000337") bundles = {x["묶음"]: x for x in got[net["key"]]["bundles"]} assert set(bundles) == {"인력", "재료", "경비"} equip = next(x for x in bundles["경비"]["줄"] if "몫" in x) assert len(equip["몫"]) == 3 # 장비 한 줄 = 노 · 재 · 경 unfold = equip["몫"][0]["줄"]["펼침"] assert unfold["단위"] == "시간" and len(unfold["줄"]) == 4 # 손료 · 운전사 · 연료 · 잡품 parent = next(b for b in sg["sg"] if b["logic"] == "GF000169") sub = {x["묶음"]: x for x in got[parent["key"]]["bundles"]}["하위"] assert all("산근" in x["이름"] and "호표" in x["이름"] for x in sub["줄"]) def _parts(ln): if ln["kind"] == "labor": return ["labor"] if ln["kind"] in ("machine", "sangeun"): return ["labor", "material", "expense"] own = [p for p in ln.get("price") or {} if p in ("labor", "material", "expense")] return own or [p for p in ("labor", "material", "expense") if ln.get(p) is None] or ["material"]