feat(axis): 이중계상 경계를 갈 곳 칸으로 판정 — 어기면 내역서가 멈춤

① 구조물 터파기·되메우기는 토공집계로만 — 토공·사토 공제·참고로 가는 성분을 구조물 줄·묶음 조각·타설·기초잡석이 또 집으면 오류.
② 시멘트·모래·자갈이 자재총괄에 뜨면 오류. ③ 원단위표가 할증을 붙였거나, 자재 합계가 순수량 × (1+율) 과 다르거나,
할증 포함 재료량을 준 공종에 자재가 일위대가 재료비로 붙으면 오류.
판정은 B08 인계(double_count_violations), 멈춤은 B09 build_bill 한 자리. 배정 프로젝트 실측 어긴 자리 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
2026-09-13 22:18:00 +09:00
co-authored by Claude Opus 5
parent fd4f00ab7e
commit 938eef94bd
7 changed files with 311 additions and 0 deletions
@@ -29,7 +29,9 @@ from B09_Estimation.B09_Estimation_Guards import (
check_drain_pipe_not_double_counted,
check_included_materials_not_listed,
check_free_haul_not_priced,
check_handoff_boundaries,
check_haul_volume_within_cut,
check_material_surcharge_once,
)
from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master
from B09_Estimation.B09_Estimation_QuantityDigits import round_quantity
@@ -472,6 +474,10 @@ def build_bill(
# ── 4) 검사 — `in_bill=false` 줄에 금액이 붙지 않았는가 ──────────────────────
check_excluded_rows_not_priced(rows=[r.as_dict() for r in result.excluded])
# 이중계상 경계 ①②③(명세 6장) — B08 이 갈 곳 칸으로 판정한 어긴 자리 · 자재 할증 한 번.
check_handoff_boundaries(payload.get("double_count_violations"))
check_material_surcharge_once(materials)
# ㉦ 큰돌쌓기 품에 포함된 자재(고임돌·채움콘크리트)를 따로 세지 않았는가.
check_included_materials_not_listed(
work_item_codes=[row.code or "" for row in result.rows],