fix(B08): 운반 인계 줄을 자연상태로 세움 — 두 자가 섞여 있던 자리

- 유토곡선은 다짐상태로 쌓고(운반거리를 그 기준으로 재야 맞음) 내역서 수량은 자연상태임
  (config_system_design 5-4-3). 인계 줄이 다짐값을 그대로 읽고 있었음
- 되돌린 값(natural_m3)이 오면 그 값으로 서고, 쓴 계수(÷C)를 근거에 적음
- ⚠ 되돌릴 계수가 없으면(갈래 못 붙임) 다짐 그대로 두고 사유를 냄 — 토사 계수로 눅이면
  근거 없이 금액이 움직임
- 거리는 안 바뀜(다짐 기준이 맞음)
- tmp/tests/test_b08_haul_state.py 신설(4건)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-09 07:13:17 +09:00
co-authored by Claude Opus 5
parent dae51cc940
commit 9b9367a118
@@ -191,13 +191,33 @@ def _haul_rows(
in_bill = bool(row.get("in_bill", True)) and entry.get("in_bill", True)
if code is None and in_bill:
unmatched.append(f"운반({equipment})")
# ⚠⚠ **내역서 수량은 자연상태다** — 유토곡선은 다짐상태로 쌓고(운반거리를 그 기준으로
# 재야 맞는다) 내역에 오르는 수량은 되돌린 값이다(`config_system_design` 5-4-3
# 「운반거리 산정 시 모든 수량은 다짐상태로 환산해 계산하고, **내역서에 적용하는
# 수량은 자연상태로 한다**」). 되돌린 값이 없으면 갈래를 못 붙인 것이라 **다짐 그대로
# 두고 사유를 낸다** — 토사 계수로 눅이면 근거 없이 금액이 움직인다.
compacted = float(row.get("volume_m3") or 0.0)
natural = row.get("natural_m3")
state_note = ""
if isinstance(natural, (int, float)) and float(natural) > 0:
quantity, factor = float(natural), row.get("conversion_c")
state_note = (
f"다짐 {compacted:,.2f}㎥ ÷ C {factor} = 자연상태"
if factor
else f"다짐 {compacted:,.2f}㎥ 을 되돌린 자연상태"
)
else:
quantity = compacted
state_note = (
"⚠ 다짐상태 그대로 — 갈래를 못 붙여 되돌릴 계수가 없음(내역 수량은 자연상태여야 함)"
)
rows.append(
{
"work_item_code": code,
"name": f"{equipment} 운반",
"spec": str(row.get("ground") or ""),
"unit": "",
"quantity": float(row.get("volume_m3") or 0.0),
"quantity": quantity,
# 운반에는 반영률 개념이 없다 — 그래서 `None` 이다(0 이 아니다).
"quantity_gross": None,
"application_ratio_pct": None,
@@ -209,7 +229,7 @@ def _haul_rows(
"excavation_method": None,
"station_from": None,
"station_to": None,
"spec_detail": "",
"spec_detail": state_note,
"composite_parts": None,
"structure_kind": None,
# 토공·운반 줄에는 갈래 축이 없다 — 그래도 **칸은 둔다**(계약이 한 모양).