fix(b08,b09): 한 줄 「암」 막힘 까닭 바로잡음 — 「공종코드 없음」이 아니라 암 갈래 구성비 입력 필요
- B08: 갈래로 안 나뉜 암 총량은 시공법만 골라서는 안 풀림 → 사유 「암 갈래 구성비(%)가 아직 없어…」 · 시공법 안내 목록에서 뺌 - B09 내역: 공종을 못 고른 줄에 B08 막힘 까닭이 있으면 그 문구 그대로(입력 필요 ↔ 매핑 구멍 가름) - 배정 프로젝트 흙깎기 암 3,104.57㎥ · 측구터파기 암 160.48㎥ 가 이 자리 · 시험 1건 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
@@ -185,7 +185,7 @@ def build_handoff(
|
|||||||
str(row.get("ground_class"))
|
str(row.get("ground_class"))
|
||||||
for row in work_items
|
for row in work_items
|
||||||
if row.get("ground_class")
|
if row.get("ground_class")
|
||||||
and row.get("ground_class") != "토사"
|
and row.get("ground_class") not in ("토사", "암") # 「암」은 구성비가 빠진 것
|
||||||
and row.get("work_item_code") is None
|
and row.get("work_item_code") is None
|
||||||
and row.get("origin") == ORIGIN_EARTHWORK
|
and row.get("origin") == ORIGIN_EARTHWORK
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,12 @@ ORIGIN_PIPE = "pipe"
|
|||||||
#: 갈래 이름(연암·보통암…)만으로는 공종을 못 고른다(2026-09-07 일감 9 실서버에서 드러남).
|
#: 갈래 이름(연암·보통암…)만으로는 공종을 못 고른다(2026-09-07 일감 9 실서버에서 드러남).
|
||||||
METHOD_TO_GROUND = {"ripping": "리핑암", "blasting": "발파암"}
|
METHOD_TO_GROUND = {"ripping": "리핑암", "blasting": "발파암"}
|
||||||
NOTE_METHOD_MISSING = "시공법 미지정으로 공종을 못 고름"
|
NOTE_METHOD_MISSING = "시공법 미지정으로 공종을 못 고름"
|
||||||
|
#: 암 총량이 갈래로 안 나뉘어 한 줄 「암」으로 선 경우 — 시공법은 **갈래마다** 고르므로 이 줄은
|
||||||
|
#: 시공법만 골라서는 안 풀린다. 빠진 입력은 구성비다(2026-09-14 흙깎기·측구터파기 암).
|
||||||
|
NOTE_ROCK_RATIO_MISSING = (
|
||||||
|
"암 갈래 구성비(%)가 아직 없어 암 총량이 한 줄 「암」으로 섬 — 산출 조건 「암 갈래 구성비」를 "
|
||||||
|
"넣고 갈래마다 시공법(리핑·발파)을 고르면 공종이 섬"
|
||||||
|
)
|
||||||
|
|
||||||
#: 철근으로 보는 성분 이름 조각. **정확한 낱말이 아니라 앞머리**로 본다 —
|
#: 철근으로 보는 성분 이름 조각. **정확한 낱말이 아니라 앞머리**로 본다 —
|
||||||
#: 「이형철근 D13」·「원형철근」처럼 규격이 뒤에 붙기 때문이다. `철근콘크리트`는 성분 이름이
|
#: 「이형철근 D13」·「원형철근」처럼 규격이 뒤에 붙기 때문이다. `철근콘크리트`는 성분 이름이
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from B08_Quantity.B08_Quantity_Engine_Handoff_Mapping import (
|
|||||||
METHOD_TO_GROUND,
|
METHOD_TO_GROUND,
|
||||||
NOTE_HAUL_IN_SUMMARY,
|
NOTE_HAUL_IN_SUMMARY,
|
||||||
NOTE_METHOD_MISSING,
|
NOTE_METHOD_MISSING,
|
||||||
|
NOTE_ROCK_RATIO_MISSING,
|
||||||
ORIGIN_EARTHWORK,
|
ORIGIN_EARTHWORK,
|
||||||
ORIGIN_HAUL,
|
ORIGIN_HAUL,
|
||||||
ORIGIN_SLOPE,
|
ORIGIN_SLOPE,
|
||||||
@@ -54,6 +55,8 @@ def _mapping_ground(ground: str | None, methods: dict[str, str | None]) -> tuple
|
|||||||
"""
|
"""
|
||||||
if ground is None or ground == "토사":
|
if ground is None or ground == "토사":
|
||||||
return ground, ""
|
return ground, ""
|
||||||
|
if ground == "암":
|
||||||
|
return None, NOTE_ROCK_RATIO_MISSING # 갈래로 안 나뉜 총량 — 시공법만으론 안 풀림
|
||||||
method = methods.get(ground)
|
method = methods.get(ground)
|
||||||
mapped = METHOD_TO_GROUND.get(method or "")
|
mapped = METHOD_TO_GROUND.get(method or "")
|
||||||
if mapped:
|
if mapped:
|
||||||
|
|||||||
@@ -546,12 +546,18 @@ def build_bill(
|
|||||||
if item.origin == "structure"
|
if item.origin == "structure"
|
||||||
else "공종을 못 골랐습니다 — B08 인계에 공종코드가 없습니다."
|
else "공종을 못 골랐습니다 — B08 인계에 공종코드가 없습니다."
|
||||||
)
|
)
|
||||||
|
# ⚠ B08 이 막힘 까닭을 실어 보냈으면 **그 문구를 그대로** — 「코드가 없다」로 덮으면
|
||||||
|
# 입력하면 풀리는 자리(암 갈래 구성비·시공법)가 매핑 구멍으로 읽힘(2026-09-14 흙깎기 암).
|
||||||
|
if item.blocked_reason:
|
||||||
|
kind = item.blocked_kind or _NOT_OUR_ROW
|
||||||
|
reason = f"{_BLOCKED_LABELS.get(kind, '막힘')} — {item.blocked_reason}"
|
||||||
result.missing.append(
|
result.missing.append(
|
||||||
{
|
{
|
||||||
"name": item.display_name,
|
"name": item.display_name,
|
||||||
"unit": item.unit,
|
"unit": item.unit,
|
||||||
"quantity": str(item.quantity),
|
"quantity": str(item.quantity),
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
|
**({"blocked_kind": item.blocked_kind} if item.blocked_kind else {}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -367,6 +367,20 @@ def test_시공법을_안_정하면_찍지_않고_드러냄() -> None:
|
|||||||
assert any("시공법" in item for item in handoff["unmatched_work_items"])
|
assert any("시공법" in item for item in handoff["unmatched_work_items"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_갈래로_안_나뉜_암은_구성비가_빠졌다고_말함() -> None:
|
||||||
|
"""2026-09-14 — 한 줄 「암」은 시공법만 골라서는 안 풀림. B09 내역도 이 까닭을 그대로 실음."""
|
||||||
|
handoff = build_handoff(summary_table=집계표(집계줄("흙깎기", "암", 100.0)))
|
||||||
|
row = handoff["work_items"][0]
|
||||||
|
assert row["work_item_code"] is None and row["blocked_kind"] == "input_missing"
|
||||||
|
assert "암 갈래 구성비" in row["blocked_reason"]
|
||||||
|
assert handoff["missing_method_classes"] == [] # 시공법 안내에 「암」을 안 띄움
|
||||||
|
|
||||||
|
from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill
|
||||||
|
|
||||||
|
bill = build_bill(handoff)
|
||||||
|
assert any("입력이 필요합니다 — 암 갈래 구성비" in m["reason"] for m in bill.missing)
|
||||||
|
|
||||||
|
|
||||||
def test_토사는_시공법이_필요없음() -> None:
|
def test_토사는_시공법이_필요없음() -> None:
|
||||||
handoff = build_handoff(summary_table=집계표(집계줄("흙깎기", "토사", 100.0)))
|
handoff = build_handoff(summary_table=집계표(집계줄("흙깎기", "토사", 100.0)))
|
||||||
assert handoff["work_items"][0]["work_item_code"] == "FP-09-03-02"
|
assert handoff["work_items"][0]["work_item_code"] == "FP-09-03-02"
|
||||||
|
|||||||
Reference in New Issue
Block a user