"""산출기초 — 줄에 달린 근거를 한 장으로 (2026-09-09). 별표2 (5)(가) 열셋째. **새로 세는 장이 아니다** — 이미 줄마다 달려 있는 근거를 모은다. ⚠ 겨누는 것 다섯 ① 어느 판으로 계산했나 — **장부(`_manifest.json`)를 그대로** 읽는다(목록을 따로 안 적음) ② 고른 값만 싣는다 — 안 고른 칸은 기본값이라 줄이 안 선다 ③ 근거는 **줄에 달린 문구 그대로** — 여기서 새로 짓지 않는다 ④ 못 채운 자리는 **사유째** 모은다(0 으로 때운 자리가 아님) ⑤ 여기서 **값을 다시 계산하지 않는다** — 일위대가가 낸 값과 두 벌이 되면 안 된다 """ from __future__ import annotations import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) from B09_Estimation.B09_Estimation_BasisSheet import ( # noqa: E402 basis_sheet, chosen_conditions, dataset_versions, work_item_basis, ) from B09_Estimation.B09_Estimation_UnitPrice import cached_build # noqa: E402 def test_판_목록은_장부를_그대로_읽는다() -> None: manifest = json.loads( (ROOT / "resources" / "data_cost_input_value" / "_manifest.json").read_text( encoding="utf-8" ) ) ledger = {str(row["file"]) for row in manifest["files"]} rows = dataset_versions() assert ledger <= {row["file"] for row in rows} # 공종 마스터도 함께 실린다 — 품셈 원문을 정규화한 판이다. assert any(row["dataset_id"] == "work_item_master" for row in rows) for row in rows: assert row["effective_date"], row def test_고른_값만_싣는다() -> None: assert chosen_conditions({}) == [] assert chosen_conditions({"misc_material_percent": "", "fuel_region": ""}) == [] picked = chosen_conditions({"fuel_region": "01", "transport_distance_km": "12"}) assert [row["value"] for row in picked] == ["01", "12"] def test_품_할증은_고른_것을_풀어_적는다() -> None: picked = chosen_conditions({"labor_surcharge": {"1-4-1": "1-4-1:0"}}) assert len(picked) == 1 assert "10%" in picked[0]["item"] assert "작업시기" in picked[0]["value"] def test_근거는_줄에_달린_문구_그대로다() -> None: build = cached_build() rows = {row["code"]: row for row in work_item_basis(build)} assert rows, "근거가 달린 공종이 하나도 없습니다" for code, row in rows.items(): notes = { detail.note.strip() for detail in build.book.details.get(code, []) if detail.note and detail.note.strip() } # 줄에 없는 문구를 지어내지 않는다(공종 단위 출처 한 줄만 더 붙을 수 있다). assert notes <= set(row["notes"]) def test_못_채운_자리는_사유째_모은다() -> None: sheet = basis_sheet(cached_build(), {}) assert sheet["gaps"], "못 채운 자리가 하나도 없다고 나옵니다 — 셈이 이상합니다" for row in sheet["gaps"]: assert row["kind"] and row["code"] and row["reason"] def test_값을_다시_계산하지_않는다() -> None: """⑤ 이 장에는 금액 칸이 없다 — 금액은 일위대가·내역서 몫이다.""" sheet = basis_sheet(cached_build(), {}) text = json.dumps(sheet, ensure_ascii=False) assert "총액" not in text and "합계 금액" not in text for row in sheet["work_items"]: assert set(row) == {"code", "name", "unit", "notes"}