From 488074151ce952f333ab8ac68ecc70e2af99d070 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 13 Sep 2026 19:20:57 +0900 Subject: [PATCH] =?UTF-8?q?fix(b09):=20=EA=B9=A8=EA=B8=B0=20=EB=8C=80?= =?UTF-8?q?=ED=98=95=EB=B8=8C=EB=A0=88=EC=9D=B4=EC=BB=A4=EC=97=90=20?= =?UTF-8?q?=EA=B5=B4=EC=B0=A9=EA=B8=B0=20=EB=AA=B8=EC=B2=B4=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EB=8F=84=20=EB=B6=99=EC=9E=84=20=E2=80=94=20=EB=B6=80?= =?UTF-8?q?=EC=B0=A9=20=EC=9E=A5=EB=B9=84=EB=9D=BC=20=EB=AA=B8=EC=B2=B4=20?= =?UTF-8?q?=EC=97=86=EC=9D=B4=20=EB=AA=BB=20=EB=8F=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 9-12·9-13 「대형브레이커(㎥/hr)」 줄에 같은 표의 굴착기를 같은 1/Q 로 붙이고 #조합(잡재료 16%)으로 바꿔 닮 - 구조물터파기·측구터파기 암·발파암 갈래 단가가 몸체 장비비만큼 오름 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- ...stimation_MachineProductivity_Reference.py | 28 +++++++++++++++---- resources/tester/test_b09_parent_steps.py | 9 ++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/B09_Estimation/B09_Estimation_MachineProductivity_Reference.py b/B09_Estimation/B09_Estimation_MachineProductivity_Reference.py index 1a412706..91442755 100644 --- a/B09_Estimation/B09_Estimation_MachineProductivity_Reference.py +++ b/B09_Estimation/B09_Estimation_MachineProductivity_Reference.py @@ -212,8 +212,8 @@ def reference_factor_values( _CAPACITY_UNIT = re.compile(r"[((]\s*(㎥|m3|㎡|m2|m|ton|t)\s*/\s*(?:hr|시간)\s*[))]") -def _paired_machine_spec(node: dict[str, Any]) -> str: - """그 표에 함께 나오는 기종의 규격(「유압식백호우 (무한궤도,0.7㎥)」 → 0.7).""" +def _paired_machine(node: dict[str, Any]) -> tuple[str, str, str] | None: + """그 표에 함께 나오는 기종 — (코드, 이름, 규격). 「유압식백호우 (…0.7㎥)」 → 0201-0070.""" from B09_Estimation.B09_Estimation_MachineProductivity import resolve_machine from B09_Estimation.B09_Estimation_MachineCost import load_machine_catalog @@ -226,8 +226,13 @@ def _paired_machine_spec(node: dict[str, Any]) -> str: if found is not None: machine = catalog.machines.get(found[0]) if machine is not None and machine.specification: - return str(machine.specification) - return "" + return found[0], machine.name, str(machine.specification) + return None + + +#: 제 엔진이 없는 **부착 장비** 분류번호 — 리퍼·대형 브레이커·진동콤팩터·부착용 집게. +#: `B09_Estimation_UnitPrice._ATTACHMENT_PREFIXES` 와 같은 번호(그쪽은 `X-` 층 코드로 잡음). +_ATTACHMENT_CODE_PREFIXES = ("0103-", "0230-", "0240-", "7206-") def _machine_by_name(text: str, preferred_spec: str) -> tuple[str, str] | None: @@ -300,7 +305,8 @@ def direct_capacity_rows(node: dict[str, Any]) -> list[dict[str, Any]]: ratio: Decimal | None = None # 같은 표에 짝이 되는 기종이 있으면 **그 규격**을 따른다 — 「대형브레이커」는 규격을 # 안 적고, 실무도 「대형브레이커 + B/H 0.7」처럼 붙는 굴착기 규격으로 잡는다. - paired_spec = _paired_machine_spec(node) + paired = _paired_machine(node) + paired_spec = paired[2] if paired else "" for table in node.get("tables", []): for row in table.get("raw_row") or []: cells = [_clean(cell) for cell in row] @@ -333,6 +339,18 @@ def direct_capacity_rows(node: dict[str, Any]) -> list[dict[str, Any]]: "row_cells": [c for c in cells if c], } ) + # ⚠ 부착 장비(깨기 대형브레이커)는 **굴착기에 달려 돈다** — 몸체 시간도 + # 같은 작업량으로 붙인다(2026-09-13 브레인 판정 · 영월 「브레이커조합」 ÷ Q). + # 안 붙이면 장비비가 몸체만큼 모자란다. 본체는 뒤에서 `#조합`(잡재료 16%)으로. + if machine[0].startswith(_ATTACHMENT_CODE_PREFIXES) and paired: + found.append( + { + **found[-1], + "cell": f"{cell} — 몸체 {paired[1]} {paired[2]}", + "machine_code": paired[0], + "machine_name": paired[1], + } + ) # 절 제목·[주]에만 있는 자리 — 표에서 이미 잡혔으면 **덧붙이지 않는다**(두 번 세지 않게). already = {row["machine_code"] for row in found} diff --git a/resources/tester/test_b09_parent_steps.py b/resources/tester/test_b09_parent_steps.py index b1931757..2e017278 100644 --- a/resources/tester/test_b09_parent_steps.py +++ b/resources/tester/test_b09_parent_steps.py @@ -91,3 +91,12 @@ def test_발파암은_단계_사유로_막히고_고르기형_부모는_오류() } blocked = build_bill(handoff, build=_build()) assert any("갈래 고르기형 부모" in m["reason"] for m in blocked.missing) + + +def test_깨기_브레이커는_굴착기_몸체_시간과_함께_붙는다(): + """9-12·9-13 「깨기 대형브레이커」 — 부착 장비라 몸체도 같은 1/Q 로(2026-09-13 판정).""" + details = _build().book.details["B-FP-09-13-07"] + breaker = next(d for d in details if d.ref_code == "X-0230-0007") + assert breaker.quantity == Decimal(1) / Decimal("3.5") * Decimal("0.9") # 장비 90 % 몫 + breaking = [d for d in details if d.quantity == breaker.quantity] + assert {d.ref_code for d in breaking} == {"X-0230-0007", "X-0201-0070#조합"}