From d0978d24220062ba700b34028ae07f95ed0e40cb Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 04:43:58 +0900 Subject: [PATCH] =?UTF-8?q?feat(B08):=20=ED=81=B0=EB=8F=8C=EC=8C=93?= =?UTF-8?q?=EA=B8=B0=20=EB=A9=94/=EC=B0=B0=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=ED=8C=90=EC=A0=95=20+=20=EC=A0=84=EB=A9=B4=20=EA=B8=B0?= =?UTF-8?q?=EC=9A=B8=EA=B8=B0=20=EA=B7=BC=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 랩탑이 B06 에서 상세 제원을 받게 하고 `bond` 칸을 더해 준 것을 받음. - `bond`(메쌓기/찰쌓기) → 품셈 `FP-13-06-01`/`FP-13-06-02` 자동 판정. 미입력이면 `blocked_kind: input_missing` 과 사람 말 사유로 드러냄 — 랩탑이 「빈 값을 미확보로 계속 드러내 달라」고 한 그대로. 첫 항목을 슬쩍 고르면 근거 없는 값이 수량·단가로 흘러감. - `pending_user` 에서 큰돌쌓기를 뺌 — 칸이 생겨 자동으로 갈림. 전면 기울기 1:0.3 의 근거를 찾아 데이터·주석에 적음. 지어낸 값이 아니라 **교본 7-3 돌흙막이 기준**임 — 「돌 찰쌓기 3.0m 이하 1:0.3 / 돌 메쌓기 2.0m 이하 1:0.3 / 큰돌쌓기 1:0.3 이상(전도 방지)」(지식DB 돌쌓기.md §1). ⚠ 큰돌쌓기는 「1:0.3 **이상**」이라 더 완만하게 잡을 수 있음 — 칸을 만든다면 그 범위를 보여야 한다는 것도 데이터에 적음. 검증 — 전체 642 passed. Co-Authored-By: Claude Opus 5 (1M context) --- B08_Quantity/B08_Quantity_Engine_Handoff.py | 17 +++++++++++++++-- .../B08_Quantity_Engine_UnitQuantity.py | 3 +++ .../masonry_class_2026-01-01.json | 19 +++++++++++++++++++ .../work_item_mapping_2026-01-01.json | 19 ++++++++++--------- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff.py b/B08_Quantity/B08_Quantity_Engine_Handoff.py index 2ca0bba3..87aefef8 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff.py @@ -602,9 +602,22 @@ def _structure_rows( type_id = str(structure.get("type_id") or "") entry = mapping.for_structure(type_id) or {} code = entry.get("work_item_code") - # 돌쌓기는 **뒷길이 갈래**로 단가가 갈린다 — 저장 제원에서 자동으로 고른다. + # 돌쌓기는 **뒷길이 갈래**로, 큰돌쌓기는 **메/찰**로 단가가 갈린다 — + # 둘 다 저장 제원에서 자동으로 고른다(사용자 칸을 따로 만들지 않는다). class_key: str | None = None class_basis = "" + if entry.get("class_from") == "bond": + bond = str((structure.get("options") or {}).get("bond") or "").strip() + bond_codes = entry.get("bond_codes") or {} + if bond in bond_codes: + code = bond_codes[bond] + class_key = bond + class_basis = f"쌓기 방식 「{bond}」 → 품셈 13-6 {code.split('-')[-1]}" + else: + class_basis = ( + "큰돌쌓기 쌓기 방식이 아직 입력되지 않았습니다 — 구조물 상세 입력에서 " + "메쌓기·찰쌓기 중 하나를 고르면 공종이 정해집니다" + ) if code and entry.get("class_from") == "back_length": class_key, class_basis = masonry_class(structure.get("options") or {}) if class_key: @@ -618,7 +631,7 @@ def _structure_rows( blocked_kind, blocked_reason = blocked_of(structure, class_basis) if code is None and composite is None: unmatched.append(f"{wording_type_label(type_id)} — 품셈 공종을 아직 못 이었습니다") - elif entry.get("class_from") == "back_length" and class_key is None: + elif entry.get("class_from") in ("back_length", "bond") and class_key is None: unmatched.append(f"{wording_type_label(type_id)} — {class_basis}") length = float(structure.get("length_m") or 0.0) rows.append( diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 13117703..214aedfe 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -293,6 +293,9 @@ def stone_masonry( # ⚠ `face_slope_ratio` 는 **레지스트리에 없는 키**다 — 즉 지금은 늘 기본 0.3 으로 돈다. # 상수로 두는 것이 아니라 「칸이 생기면 바로 받는다」는 뜻으로 남겨 둔다. # (키 이름 어긋남으로 저장값이 안 닿던 `back_len_cm` 사고와 구별할 것 — 이쪽은 **칸 자체가 없다**.) + # **기본 0.3 의 근거** — 교본 7-3 돌흙막이 기준: 「돌 찰쌓기 3.0m 이하 **1:0.3** / + # 돌 메쌓기 2.0m 이하 **1:0.3** / 큰돌쌓기 **1:0.3 이상**(전도 방지)」 + # (`지식DB 02_상세설계/구조물/돌쌓기.md §1`, 값은 `data_masonry` 의 `face_slope`). slope_ratio = _num(options.get("face_slope_ratio"), 0.3) # 전면 기울기 1:0.3 (교본 7-3) constants = STONE_MASONRY diff --git a/resources/data_masonry/masonry_class_2026-01-01.json b/resources/data_masonry/masonry_class_2026-01-01.json index 9026c746..6ed74845 100644 --- a/resources/data_masonry/masonry_class_2026-01-01.json +++ b/resources/data_masonry/masonry_class_2026-01-01.json @@ -31,5 +31,24 @@ "60~80", "80~100" ] + }, + "face_slope": { + "note": "전면 기울기(1:n) — **교본 7-3 돌흙막이 기준**이 형식별로 정해 둔다. 코드의 기본 0.3 은 지어낸 값이 아니라 이 표에서 온 것이다.", + "source": "resources/knowledge/technical_info/01_임도/02_상세설계/구조물/돌쌓기.md §1 (교본 7-3)", + "quote": "돌 찰쌓기 3.0m 이하 1:0.3 / 돌 메쌓기 2.0m 이하 1:0.3 / 큰돌쌓기 1:0.3 이상(전도 방지)", + "by_type": { + "masonry_wet": 0.3, + "masonry_dry": 0.3, + "boulder_masonry": 0.3 + }, + "pending": "⚠ 큰돌쌓기는 「1:0.3 **이상**」이라 더 완만하게 잡을 수 있음 — 칸을 만든다면 그 범위를 보여야 함. 지금은 하한 0.3 으로 감." + }, + "bond": { + "note": "큰돌쌓기 쌓기 방식 — 저장 제원 `bond`. 품셈 13-6-1(메)·13-6-2(찰)로 그대로 갈린다.", + "option_key": "bond", + "codes": { + "메쌓기": "FP-13-06-01", + "찰쌓기": "FP-13-06-02" + } } } diff --git a/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json b/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json index c6bafe55..46d8d9b1 100644 --- a/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json +++ b/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json @@ -139,6 +139,16 @@ "type_id": "ford_pavement", "work_item_code": "FP-12-06", "master_name": "콘크리트 포장(인력시공)" + }, + { + "type_id": "boulder_masonry", + "work_item_code": null, + "class_from": "bond", + "bond_codes": { + "메쌓기": "FP-13-06-01", + "찰쌓기": "FP-13-06-02" + }, + "class_note": "저장 제원 `bond`(메쌓기/찰쌓기)로 품셈 13-6-1·13-6-2 를 고름 — 자동 판정" } ], "pending_user": { @@ -160,15 +170,6 @@ "FP-09-05 발파암" ], "why": "설계자가 넣는 암 갈래 이름(풍화암·연암·보통암·경암)이 리핑이냐 발파냐를 말하지 않음. 갈래마다 시공법을 지정하는 칸이 필요함" - }, - { - "type_id": "boulder_masonry", - "candidates": [ - "FP-13-06-01 메쌓기", - "FP-13-06-02 찰쌓기" - ], - "why": "저장 제원(`boulder_masonry`)에 **메/찰 구분 칸이 없어** 어느 쪽인지 못 고름. 레지스트리(`B05_Profile_Structure_Types.json`)는 다른 창 소관이라 여기서 못 고침 — 칸이 생기면 `structure` 에 옮긴다.", - "class_axis": "직경(`stone_cm`) 40~60·60~80·80~100 — 품셈 13-6 축과 글자까지 같음" } ] },