fix(B08): 운반이 두 축에 실려 내역 줄이 두 벌이던 것 막음

- 토공집계표는 실무 토적집계 모양이라 무대·도자운반·덤프운반을 함께 싣는데, 인계본에는
  운반표(FP-10-11·FP-10-12)가 같은 물량으로 또 실렸음 — 둘 다 in_bill 이었음
  (실측: 도자 17.389·61.130 · 덤프 37.511·122.417 이 두 축에 각각)
- 집계 쪽 운반 줄을 in_bill=False 로 두고 사유를 적음. 값은 남김 — 검산
  (무대+도자+덤프 = 총 운반토량)이 그 값을 씀(무대를 그렇게 둔 것과 같은 규칙)
- 성토 같은 다른 집계 줄은 안 건드림
- tmp/tests/test_b08_haul_double_count.py 신설(5건)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-09 08:37:44 +09:00
co-authored by Claude Opus 5
parent a22bf97964
commit 68ca189220
2 changed files with 19 additions and 4 deletions
@@ -139,6 +139,18 @@ SLOPE_GROUPS = frozenset({"성토면다짐", "초류종자살포", "지장목제
#: 집계 합계 줄 — 내역 줄이 아니라 검산용이다.
SUBTOTAL_GROUPS = frozenset({"보정량계"})
#: ⚠⚠ **토공집계표에도 운반 줄이 있다 — 그런데 내역 줄은 운반표 쪽이다**(2026-09-09 실측).
#: 집계표는 실무 토적집계 모양이라 「무대·도자운반·덤프운반」을 함께 싣는데, 인계본에는
#: 운반표(`_haul_rows`, FP-10-11·FP-10-12)가 **같은 물량으로 또 실렸다** — 같은 운반이
#: 두 줄이었다(실측: 도자 17.389·61.130, 덤프 37.511·122.417 이 두 축에 각각).
#: ⇒ 집계 쪽은 **값은 내되 내역 줄이 아니다**. 빼지 않는 까닭은 검산(무대+도자+덤프 = 총
#: 운반토량)이 그 값을 쓰기 때문이다 — 무대를 그렇게 둔 것과 같은 규칙이다.
HAUL_SUMMARY_GROUPS = frozenset({"무대(종방향유용토)", "도자운반", "덤프운반"})
NOTE_HAUL_IN_SUMMARY = (
"집계 값 — 내역 줄은 운반표 쪽(FP-10-11·FP-10-12)이 세움. 여기서 또 세우면 같은 운반이"
" 두 줄이 됨(검산용으로만 실림)"
)
def _latest_dataset_path(directory: Path | None = None) -> Path | None:
folder = directory or DATASET_DIR
@@ -14,7 +14,9 @@ from B08_Quantity.B08_Quantity_Engine_Handoff_Mapping import (
BLOCKED_FORMULA_MISSING,
BLOCKED_INPUT_MISSING,
BLOCKED_UNIT_DATA_MISSING,
HAUL_SUMMARY_GROUPS,
METHOD_TO_GROUND,
NOTE_HAUL_IN_SUMMARY,
NOTE_METHOD_MISSING,
ORIGIN_EARTHWORK,
ORIGIN_HAUL,
@@ -114,7 +116,8 @@ def _earthwork_rows(
continue
ground = row.get("item") or None
origin = ORIGIN_SLOPE if group in SLOPE_GROUPS else ORIGIN_EARTHWORK
is_subtotal = group in SUBTOTAL_GROUPS
# ⚠ 운반은 **집계에도 오르고 운반표에도 오른다** — 내역 줄은 운반표 쪽 하나뿐이다.
is_subtotal = group in SUBTOTAL_GROUPS or group in HAUL_SUMMARY_GROUPS
lookup_ground, method_note = _mapping_ground(ground, methods)
entry = mapping.for_earthwork(group, lookup_ground) if method_note == "" else None
code = (entry or {}).get("work_item_code")
@@ -169,9 +172,9 @@ def _earthwork_rows(
# 합계 줄과 무대 줄은 값은 내되 내역에 안 선다.
"in_bill": bool(row.get("in_bill", True)) and not is_subtotal and mismatch is None,
"excavation_method": methods.get(ground) if ground else None,
"in_bill_reason": "집계 합계 줄 — 검산용"
if is_subtotal
else str(row.get("note") or ""),
"in_bill_reason": NOTE_HAUL_IN_SUMMARY
if group in HAUL_SUMMARY_GROUPS
else ("집계 합계 줄 — 검산용" if is_subtotal else str(row.get("note") or "")),
"origin": origin,
}
)