Files
Aislo/B08_Quantity/B08_Quantity_Engine_Handoff_Spoil.py
T
eomsangdonandClaude Opus 5 759343e384 fix(B08): 사토 운반 줄 신설 — 유토곡선이 낸 사토를 아무도 실어 내지 않았음
- 운반 줄이 띠(bands)·이동(transfers)에서만 만들어져 사토는 잔량(residuals)으로 남아
  어디에도 안 실렸음. 그래서 구조물 잔토 126.63㎥ 를 얹어도 덤프가 안 늘었고,
  채집석 공제도 사토를 줄이는 값이라 끝까지 금액에 안 나타났음
- 실무에는 서는 줄(울진 대흥 1공구 토적집계 D32 사토 1,281㎥)
- 운반표에 `spoil`(물량·거리·근거)을 실어 인계가 「사토 운반」 한 줄을 세움
- 물량은 유토곡선 결과 그대로 — 공제·가산이 끝난 값이라 여기서 또 만지지 않음.
  자연방토는 실어 내지 않으므로 뺌
- 거리는 설계 입력 `spoil_site_distance_m` 신설 — 안 정했으면 막고 사유
  (품셈이 정하는 값이 아니라 임의 거리가 그대로 금액이 됨)
- 지반 갈래는 유토곡선이 안분하지 않아 갈래 없이 한 줄, 그 사실을 근거에
- tmp/tests/test_b08_spoil_haul.py 신설(6건)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 23:05:15 +09:00

82 lines
3.5 KiB
Python

"""사토 — 실어 내는 줄 (2026-09-08).
⚠⚠ **유토곡선이 사토를 내는데 아무도 실어 내지 않고 있었다.** 운반 줄은 `blocks[].bands`
와 `transfers` 에서만 만들어지는데, 사토는 `residuals(kind="spoil")` 로 남아 **어느 쪽에도
없다.** 그래서 구조물 잔토를 사토에 얹어도(126.63㎥) **덤프 물량이 하나도 안 늘었다** —
채집석 공제도 사토를 줄이는 값이라 **끝까지 금액에 안 나타났다**.
실무에는 서는 줄이다 — 울진 대흥 1공구 토적집계 `D32 사토 1,281㎥`.
⚠ **거리는 품셈이 정하지 않는다.** 사토장까지 거리는 설계 입력(`spoil_site_distance_m`)이고,
안 정했으면 **막고 사유를 낸다** — 임의 거리를 넣으면 그대로 금액이 된다.
⚠ **지반 갈래를 지어내지 않는다.** 유토곡선이 사토 잔량에 `ea/rr/br` 안분을 안 한다
(실어 내는 흙이 어느 지반에서 나왔는지 모름). 갈래 없이 한 줄로 세우고 그 사실을 적는다.
"""
from __future__ import annotations
from typing import Any
from B08_Quantity.B08_Quantity_Engine_Handoff_Mapping import (
BLOCKED_INPUT_MISSING,
ORIGIN_HAUL,
WorkItemMapping,
)
SPOIL_NAME = "사토 운반"
SPOIL_EQUIPMENT = "dump_truck"
DISTANCE_MISSING = (
"사토장까지 운반거리가 저장에 없어 값이 서지 않음 — 품셈이 정하는 값이 아니라 설계 입력임"
"(임의 거리를 넣으면 그대로 금액이 됨)"
)
GROUND_UNKNOWN = "⚠ 사토의 지반 갈래는 유토곡선이 안분하지 않아 갈래 없이 한 줄로 섬"
def spoil_haul_rows(
haul_table: dict[str, Any] | None, mapping: WorkItemMapping
) -> list[dict[str, Any]]:
"""사토를 실어 내는 줄 하나. 사토가 없으면 줄도 없다."""
spoil = (haul_table or {}).get("spoil") or {}
volume = float(spoil.get("volume_m3") or 0.0)
if volume <= 0:
return []
distance = spoil.get("distance_m")
entry = mapping.for_haul(SPOIL_EQUIPMENT) or {}
code = entry.get("work_item_code")
blocked = distance is None or float(distance) <= 0
reason = DISTANCE_MISSING if blocked else ""
note = spoil.get("note") or ""
return [
{
"work_item_code": code,
"name": SPOIL_NAME,
"spec": f"{float(distance):g}m" if not blocked else "",
"unit": "㎥",
"quantity": volume,
"quantity_gross": spoil.get("volume_gross_m3"),
"application_ratio_pct": None,
"application_ratio_breakdown": None,
"quantity_breakdown": None,
"ground_class": None,
"haul_distance_m": None if blocked else float(distance),
"haul_equipment": SPOIL_EQUIPMENT,
"station_from": None,
"station_to": None,
"excavation_method": None,
"spec_detail": " · ".join(part for part in (note, GROUND_UNKNOWN) if part),
"composite_parts": None,
"structure_kind": None,
"blocked_kind": BLOCKED_INPUT_MISSING if blocked else None,
"blocked_reason": reason,
"variant_axis": None,
"variant_value": None,
"secondary_axes": None,
"spec_class": None,
"spec_class_basis": "",
"composite_not_ready": None,
"in_bill": not blocked and code is not None,
"in_bill_reason": reason,
"origin": ORIGIN_HAUL,
}
]