"""9-21 제근 굴착기 크기 칸 — 2026-09-14 브레인 판정(산출 조건 칸 · 제안 0.7㎥ · 비면 사유). 9-21 품은 굴착기 0.2·0.7 × 임목축적 등급(소·중·밀) 여섯 갈래인데 B08 은 등급만 보내 B09 가 못 고름. ⇒ 매핑 `variant_template` 이 크기·등급 두 입력을 한 값(「0.7·소림」)으로 엮고, 범위 별칭(FP-09-21)이 그 값을 B09 갈래(「굴착기(무한궤도) 0.7 · 소」)로 잇는다 — 갈래 키 표기는 별칭표 한 곳만 앎. """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import ( # noqa: E402 ROOT_REMOVAL_EXCAVATOR_SIZES, ROOT_REMOVAL_EXCAVATOR_SUGGESTED, ) from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff # noqa: E402 from B08_Quantity.B08_Quantity_Engine_Preparation import STAND_VOLUME_CLASSES # noqa: E402 def _row(**inputs) -> dict: summary = {"rows": [{"group": "지장목제거", "item": "뿌리뽑기", "spec": "", "unit": "㎡", "amount": 1000.0, "application_ratio_pct": 100.0}]} # fmt: skip rows = build_handoff(summary_table=summary, **inputs)["work_items"] return next(r for r in rows if r["work_item_code"] == "FP-09-21") def test_크기와_등급을_한_갈래_값으로_엮어_내역_금액이_섬() -> None: from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill inputs = {"stand_volume_class": "소림", "root_removal_excavator_m3": "0.7"} row = _row(**inputs) assert row["variant_value"] == "0.7·소림" and not row["blocked_kind"], row summary = {"rows": [{"group": "지장목제거", "item": "뿌리뽑기", "spec": "", "unit": "㎡", "amount": 1000.0}]} # fmt: skip bill = build_bill(build_handoff(summary_table=summary, **inputs)) line = next(r for r in bill.rows if r.code == "FP-09-21") assert line.price_code == "B-FP-09-21#굴착기(무한궤도)0.7·소", line.note assert line.amount_krw and int(line.amount_krw) > 0, line.note def test_크기가_비면_금액_없이_입력_사유_등급은_그대로_실음() -> None: row = _row(stand_volume_class="중림") assert row["variant_value"] == "중림" # 판정 Ⓑ — 등급 갈래를 잃지 않음 assert row["blocked_kind"] == "input_missing" and "굴착기" in row["blocked_reason"], row assert _row()["blocked_kind"] == "input_missing" def test_선택지는_마스터_갈래_키의_크기_제안은_0_7_근거_10_12_1() -> None: from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master node = next( n for n in load_work_item_master()["work_items"] if n["work_item_code"] == "FP-09-21" ) sizes = {key.split(")")[1].split("·")[0].strip() for key in node["variant_keys"]} assert sizes == set(ROOT_REMOVAL_EXCAVATOR_SIZES) value, basis = ROOT_REMOVAL_EXCAVATOR_SUGGESTED assert value == "0.7" and "10-12-1" in basis # 크기 × 등급 여섯이 모두 별칭으로 B09 갈래에 닿음 from common_util.common_util_aliases import alias_target, load_aliases rows = load_aliases("variant") for size in ROOT_REMOVAL_EXCAVATOR_SIZES: for grade in STAND_VOLUME_CLASSES: target = alias_target(rows, f"{size}·{grade}", "FP-09-21", "2026-01-01") assert target in node["variant_keys"], (size, grade, target)