"""거푸집 사용횟수 검사 — 품셈 1-7-1 · 2026-09-07 ⑩. ⚠ 사용횟수는 **관측값이 아니라 법**이다 — 품셈 1-7-1 이 구조물 종류별로 정해 둔다. ⚠⚠ **횟수별 재료 환산은 여기서 하지 않는다.** 품셈 12-4 의 비율(%)은 일위대가 재료비에 걸리는 값이라, B08 이 곱해 넘기면 B09 가 또 곱해 두 번 준다. """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) from B08_Quantity.B08_Quantity_Engine_Formwork import ( # noqa: E402 FORMWORK_NAMES, NOTE_REUSE_MISSING, FormworkTable, annotate, load_formwork_table, shoring_status, ) from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table # noqa: E402 def 옹벽() -> dict: return { "structure_id": "w1", "type_id": "retaining_wall", "start_m": 100.0, "end_m": 110.0, "options": {"form": "반중력식", "height_m": 2.0, "length_m": 10.0}, } def test_원문에_이름이_있는_구조물은_횟수가_붙음() -> None: """품셈 1-7-1 의 3회 줄에 「옹벽」이 그대로 있다.""" table = build_table([옹벽()], {"retaining_wall": "옹벽"}) forms = [c for s in table["structures"] for c in s["components"] if c["name"] in FORMWORK_NAMES] assert forms assert all(c["reuse_count"] == 3 for c in forms) assert all("1-7-1" in c["reuse_note"] for c in forms) assert table["formwork_reuse_missing"] == [] def test_횟수별_비율을_여기서_곱하지_않을것() -> None: """⚠ 이 시험이 이 파일의 핵심 — 합판 3회 46.1 % 를 여기서 곱하면 B09 와 겹쳐 두 번 준다. B08 이 내는 것은 **접촉 면적 그대로**이고 횟수는 옆에 적기만 한다.""" table = build_table([옹벽()], {"retaining_wall": "옹벽"}) euroform = next( c for s in table["structures"] for c in s["components"] if c["name"] == "유로폼" ) assert euroform["amount"] == 32.0 # 3.20 ㎡/m × 10m — 46.1 % 를 곱하지 않았다 # 비율표는 데이터에 있되 값에 안 걸린다. assert load_formwork_table().reuse_ratio_pct["plywood"]["3"] == 46.1 def test_모르는_종류는_지어내지_않고_미확보() -> None: structures = [ { "type_id": "듣도보도못한구조물", "name": "무엇", "components": [{"name": "합판거푸집", "unit": "㎡", "amount": 5.0}], } ] notes, missing = annotate(structures) assert missing == ["듣도보도못한구조물"] assert structures[0]["components"][0]["reuse_count"] is None assert structures[0]["components"][0]["reuse_note"] == NOTE_REUSE_MISSING def test_거푸집이_없는_구조물은_건드리지_않음() -> None: structures = [ { "type_id": "retaining_wall", "components": [{"name": "콘크리트", "unit": "㎥", "amount": 1.0}], } ] notes, missing = annotate(structures) assert notes == [] and missing == [] assert "reuse_count" not in structures[0]["components"][0] def test_이름은_정확히_일치로만_본다() -> None: """부분일치면 「거푸집씻기」(공사용수 항목)가 거푸집으로 잡힌다.""" structures = [ { "type_id": "retaining_wall", "components": [{"name": "거푸집씻기", "unit": "㎥", "amount": 1.0}], } ] annotate(structures) assert "reuse_count" not in structures[0]["components"][0] def test_파일이_없으면_전부_미확보() -> None: structures = [ { "type_id": "retaining_wall", "components": [{"name": "유로폼", "unit": "㎡", "amount": 1.0}], } ] notes, missing = annotate(structures, FormworkTable()) assert missing == ["retaining_wall"] # ── 동바리 ────────────────────────────────────────────────────────── def test_동바리는_대상이_없으면_없다고_말할것() -> None: """0 으로 적으면 「대상이 없음」과 「값이 0」이 구별되지 않는다.""" status = shoring_status() assert status["applicable"] is False assert status["reason"] assert set(status["pending_types"]) == {"box_culvert", "ford_bridge"} def test_표에도_동바리_상태가_실림() -> None: assert build_table([옹벽()])["shoring"]["applicable"] is False