feat(axis): 이중계상 ④ — 양식 호표가 품은 성분을 모음 줄이 또 세면 멈춤

기초잡석 줄·콘크리트 타설 줄은 여러 구조물 성분을 이름으로 모아 한 줄로 세워 누구 것을 뺐는지 안 남김.
그래서 물량 보존으로 봄 — 모음 줄 + 양식 호표(B-AX-ST)가 품은 몫 + 묶음 조각이 원단위 합을 넘으면 오류.
빌더의 건너뛰기를 따라 짜지 않아, 다른 구조물에서 건너뛰기를 빠뜨리면 여기서 걸림. 배정 프로젝트 어긴 자리 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:55:43 +09:00
co-authored by Claude Opus 5
parent 679f54853c
commit fb3442709f
3 changed files with 89 additions and 1 deletions
@@ -120,6 +120,29 @@ def test_할증_포함_재료량_공종에_자재가_재료비로_붙으면_오
check_materials_before_surcharge(book)
def test_양식_호표가_품은_기초잡석을_모음_줄이_또_세면_오류() -> None:
"""④ 검증 프로젝트 찰쌓기 실화 — 호표가 품은 기초잡석이 인계 줄로도 나가 8.3㎥(실제 3.5㎥)."""
table = {
"structures": [
{**_structure("masonry_wet", ("기초잡석", "unit_price", 4.8)), "structure_id": "s1"},
{**_structure("masonry_dry", ("기초잡석", "unit_price", 3.5)), "structure_id": "s2"},
]
}
priced = [{"structure_ids": ["s1"], "covered": ["기초잡석", "돌쌓기", "모르터"]}]
mapping = load_mapping()
def rubble(quantity: float) -> list[dict]:
return [{"name": "기초잡석", "quantity": quantity, "composite_parts": None}]
ok = verify_double_count_boundaries(table, {"rows": []}, mapping, priced, rubble(3.5))
assert not [f for f in ok if f.startswith("④")]
doubled = verify_double_count_boundaries(table, {"rows": []}, mapping, priced, rubble(8.3))
assert any(f.startswith("④ 「기초잡석」") and "4.8" in f for f in doubled)
# 호표가 없으면 8.3 은 정상 — 판정은 물량 보존이라 빌더의 건너뛰기를 따라 짜지 않음
plain = verify_double_count_boundaries(table, {"rows": []}, mapping, [], rubble(8.3))
assert not [f for f in plain if f.startswith("④")]
def test_구조물도_일위대가는_토공으로_가는_줄을_안_넣는다() -> None:
template = {"code": "AX-ST-00000001", "unit_price": {"rows": [{"seq": 1, "from_row": 12}]}}
sheet = {"rows": [{"no": 12, "unit": "㎥", "unit_amount": 1.2, "destination": "earthwork"}]}