diff --git a/B09_Estimation/B09_Estimation_MachineProductivity_Dump.py b/B09_Estimation/B09_Estimation_MachineProductivity_Dump.py index f79ff9b5..3b73a5b6 100644 --- a/B09_Estimation/B09_Estimation_MachineProductivity_Dump.py +++ b/B09_Estimation/B09_Estimation_MachineProductivity_Dump.py @@ -207,6 +207,7 @@ def attach_dump_hauls(build: Any, master: dict[str, Any], distances_m: tuple[Dec f"×{_LOADING_E0[code]}/22 = {output}㎥/hr · {_LOADING_NOTES[code]}", output=output, ) + build.formula_variants[title_code] = "산림품셈 10-12 「1. 적재」 본문 식" if not distances_m: return hourly = f"X-{DUMP_TRUCK_CODE}" @@ -236,6 +237,7 @@ def attach_dump_hauls(build: Any, master: dict[str, Any], distances_m: tuple[Dec haul.formula_text + " · " + " · ".join(material.notes), output=haul.hourly_output, ) + build.formula_variants[title_code] = "산림품셈 10-12 「2. 운반」 본문 식 · 운반거리" def dump_haul_distances(payload: dict[str, Any]) -> tuple[str, ...]: diff --git a/B09_Estimation/B09_Estimation_UnitPrice.py b/B09_Estimation/B09_Estimation_UnitPrice.py index cb750bf5..5544a6ec 100644 --- a/B09_Estimation/B09_Estimation_UnitPrice.py +++ b/B09_Estimation/B09_Estimation_UnitPrice.py @@ -141,6 +141,10 @@ class UnitPriceBuild: parent_modes: dict[str, str] = field(default_factory=dict) #: 합산형 부모 → (갈래, 근거) — 내역 줄의 갈래가 표에 없을 때 **원문이 정한** 갈래(9-4-1 [주]① 평균). default_variants: dict[str, tuple[str, str]] = field(default_factory=dict) + #: 갈래가 **어디서 왔나** — 표가 아니라 **식이 낸** 갈래 제목 → 그 식(2026-09-14 브레인 판정). + #: 표에서 온 갈래는 마스터 갈래 키에 있어야 하고(짝 시험), 식이 낸 갈래는 여기 적혀 있어야 함 + #: (덤프 운반·적재 10-12 `#L…m`·`#적재` — 본문 식이라 마스터 표에 갈래가 없음). + formula_variants: dict[str, str] = field(default_factory=dict) def _add_labor_titles(book: PriceBook, wages: dict[str, Decimal]) -> None: diff --git a/resources/tester/test_work_item_master_variants.py b/resources/tester/test_work_item_master_variants.py index 9a46aa3d..0fb13ccc 100644 --- a/resources/tester/test_work_item_master_variants.py +++ b/resources/tester/test_work_item_master_variants.py @@ -18,14 +18,18 @@ def _nodes() -> dict[str, dict]: def test_단가표_갈래_제목은_전부_마스터_갈래_키에_있다() -> None: + """갈래를 **어디서 왔나**로 가름(2026-09-14 브레인 판정) — 표에서 온 갈래만 마스터에 있어야 함. + + 식이 낸 갈래(덤프 운반·적재 10-12)는 마스터가 낼 수 없어 검사 대상이 아니되 + `formula_variants` 에 「식이 냈다」가 적혀 있어야 함(아래 짝 시험). + """ nodes = _nodes() missing = [] - for code in cached_build().book.titles: + build = cached_build(dump_haul_m=("164.23",)) + for code in build.book.titles: if not code.startswith("B-") or "#" not in code: continue - if code.startswith("B-FP-10-12-"): - # 덤프 운반·적재(10-12)는 **표가 아니라 본문 식**이라 마스터 표에 갈래가 없음 — - # 갈래(`#적재`·`#L…m`)는 식이 냄(2026-09-14 · `MachineProductivity_Dump`). + if code in build.formula_variants: continue work_item, variant = code[2:].split("#", 1) keys = { @@ -36,6 +40,18 @@ def test_단가표_갈래_제목은_전부_마스터_갈래_키에_있다() -> N assert not missing, f"마스터를 다시 뽑을 것(B08_Quantity_Build_WorkItemMaster): {missing[:5]}" +def test_식이_낸_갈래는_출처가_적히고_마스터엔_없다() -> None: + build = cached_build(dump_haul_m=("164.23",)) + formula = build.formula_variants + assert "B-FP-10-12-01#적재" in formula and "B-FP-10-12-02#L164.23m" in formula + assert all(code in build.book.titles and why for code, why in formula.items()) + nodes = _nodes() + for code in formula: + work_item, variant = code[2:].split("#", 1) + keys = {normalize_variant_key(k) for k in nodes[work_item].get("variant_keys") or []} + assert variant not in keys # 마스터에 있으면 「표에서 옴」이라 여기 적을 까닭이 없음 + + def test_표_모양대로_갈래를_가르고_못_가르면_빈칸() -> None: nodes = _nodes() felling = nodes["FP-04-02-02"]["tables"][0]["variant_key"]