feat(B08): 사토 운반을 지반 갈래별로 나눠 세움 — 못 붙인 몫은 따로 막음
- 유토곡선 잔량이 갈래별 물량(ea/rr/br)을 들고 오므로 갈래마다 한 줄로 세움 (덤프 단가가 토사·암으로 갈려 합쳐 세우면 한쪽 단가로 다 물림) - 갈래를 못 붙인 몫(ground_unknown_m3)은 따로 한 줄로 세우고 막음 — 토사로 눅이면 임의 단가가 됨 - 갈래가 안 오는 옛 저장분에서는 종전처럼 한 줄 - tmp/tests 3건 추가 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,9 @@
|
||||
|
||||
⚠ **거리는 품셈이 정하지 않는다.** 사토장까지 거리는 설계 입력(`spoil_site_distance_m`)이고,
|
||||
안 정했으면 **막고 사유를 낸다** — 임의 거리를 넣으면 그대로 금액이 된다.
|
||||
⚠ **지반 갈래를 지어내지 않는다.** 유토곡선이 사토 잔량에 `ea/rr/br` 안분을 안 한다
|
||||
(실어 내는 흙이 어느 지반에서 나왔는지 모름). 갈래 없이 한 줄로 세우고 그 사실을 적는다.
|
||||
⚠ **지반 갈래는 유토곡선이 준 것만 쓴다.** 잔량이 갈래별 물량(`ea/rr/br`)을 들고 오면
|
||||
**갈래마다 한 줄**로 세운다 — 덤프 단가가 토사·암으로 갈리기 때문이다. 갈래를 못 붙인
|
||||
몫(`ground_unknown_m3`)은 **따로 한 줄**로 세우고 막는다. 토사로 눅이면 임의 단가가 된다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -29,7 +30,12 @@ DISTANCE_MISSING = (
|
||||
"사토장까지 운반거리가 저장에 없어 값이 서지 않음 — 품셈이 정하는 값이 아니라 설계 입력임"
|
||||
"(임의 거리를 넣으면 그대로 금액이 됨)"
|
||||
)
|
||||
GROUND_UNKNOWN = "⚠ 사토의 지반 갈래는 유토곡선이 안분하지 않아 갈래 없이 한 줄로 섬"
|
||||
GROUND_UNKNOWN = (
|
||||
"지반 갈래를 못 붙인 몫 — 구조물 잔토 가운데 걸친 측점의 지반이 섞여 못 가른 것."
|
||||
" ⚠ 토사로 눅이면 덤프 단가가 임의로 정해짐"
|
||||
)
|
||||
#: 잔량이 들고 오는 갈래 키 ↔ 우리 갈래 이름(흙깎기·운반이 쓰는 그 낱말).
|
||||
GROUND_KEYS = {"ea_m3": "토사", "rr_m3": "리핑암", "br_m3": "발파암"}
|
||||
|
||||
|
||||
def spoil_haul_rows(
|
||||
@@ -46,24 +52,66 @@ def spoil_haul_rows(
|
||||
blocked = distance is None or float(distance) <= 0
|
||||
reason = DISTANCE_MISSING if blocked else ""
|
||||
note = spoil.get("note") or ""
|
||||
return [
|
||||
{
|
||||
# 갈래별로 나눠 세운다 — 덤프 단가가 토사·암으로 갈린다. 갈래가 안 오면 종전처럼 한 줄.
|
||||
by_ground = {
|
||||
GROUND_KEYS[key]: float(value)
|
||||
for key, value in (spoil.get("by_ground_m3") or {}).items()
|
||||
if key in GROUND_KEYS and float(value or 0.0) > 0
|
||||
}
|
||||
unknown = float(spoil.get("ground_unknown_m3") or 0.0)
|
||||
if by_ground or unknown > 0:
|
||||
rows: list[dict[str, Any]] = []
|
||||
for label, amount in sorted(by_ground.items()):
|
||||
rows.append(
|
||||
_spoil_row(code, amount, distance, blocked, reason, note, ground=label, extra="")
|
||||
)
|
||||
if unknown > 0:
|
||||
rows.append(
|
||||
_spoil_row(
|
||||
code,
|
||||
unknown,
|
||||
distance,
|
||||
True,
|
||||
GROUND_UNKNOWN,
|
||||
note,
|
||||
ground=None,
|
||||
extra=GROUND_UNKNOWN,
|
||||
)
|
||||
)
|
||||
return rows
|
||||
return [_spoil_row(code, volume, distance, blocked, reason, note, None, GROUND_UNKNOWN)]
|
||||
|
||||
|
||||
def _spoil_row(
|
||||
code: str | None,
|
||||
volume: float,
|
||||
distance: Any,
|
||||
blocked: bool,
|
||||
reason: str,
|
||||
note: str,
|
||||
ground: str | None,
|
||||
extra: str,
|
||||
) -> dict[str, Any]:
|
||||
"""사토 운반 줄 하나 — 갈래마다 같은 모양으로 낸다."""
|
||||
return {
|
||||
"work_item_code": code,
|
||||
"name": SPOIL_NAME,
|
||||
"spec": f"{float(distance):g}m" if not blocked else "",
|
||||
"spec": " · ".join(
|
||||
part for part in (ground or "", "" if blocked else f"{float(distance):g}m") if part
|
||||
),
|
||||
"unit": "㎥",
|
||||
"quantity": volume,
|
||||
"quantity_gross": spoil.get("volume_gross_m3"),
|
||||
"quantity": round(volume, 3),
|
||||
"quantity_gross": None,
|
||||
"application_ratio_pct": None,
|
||||
"application_ratio_breakdown": None,
|
||||
"quantity_breakdown": None,
|
||||
"ground_class": None,
|
||||
"ground_class": ground,
|
||||
"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),
|
||||
"spec_detail": " · ".join(part for part in (note, extra) if part),
|
||||
"composite_parts": None,
|
||||
"structure_kind": None,
|
||||
"blocked_kind": BLOCKED_INPUT_MISSING if blocked else None,
|
||||
@@ -78,4 +126,3 @@ def spoil_haul_rows(
|
||||
"in_bill_reason": reason,
|
||||
"origin": ORIGIN_HAUL,
|
||||
}
|
||||
]
|
||||
|
||||
@@ -166,6 +166,19 @@ def _spoil_of(plan: dict[str, Any] | None, settings: dict[str, Any]) -> dict[str
|
||||
total = float(source.get("spoil_m3") or 0.0)
|
||||
natural = float(source.get("natural_spoil_m3") or 0.0)
|
||||
volume = max(total - natural, 0.0)
|
||||
# 지반 갈래 — 사토 잔량이 갈래별 물량을 들고 온다(2026-09-08 랩탑 메인). 갈래를 못 붙인
|
||||
# 몫은 `ground_unknown_m3` 로 따로 온다. **여기서 안분하지 않는다** — 근거 없는 몫을
|
||||
# 토사로 눅이면 덤프 단가가 임의로 정해진다.
|
||||
grounds: dict[str, float] = {}
|
||||
unknown = 0.0
|
||||
for residual in source.get("residuals") or []:
|
||||
if str(residual.get("kind") or "") != "spoil":
|
||||
continue
|
||||
for key in ("ea_m3", "rr_m3", "br_m3"):
|
||||
value = float(residual.get(key) or 0.0)
|
||||
if value > 0:
|
||||
grounds[key] = grounds.get(key, 0.0) + value
|
||||
unknown += float(residual.get("ground_unknown_m3") or 0.0)
|
||||
note_parts = [f"사토 {total:,.2f}㎥"]
|
||||
if natural > 0:
|
||||
note_parts.append(f"자연방토 {natural:,.2f}㎥ 뺀 값")
|
||||
@@ -179,6 +192,8 @@ def _spoil_of(plan: dict[str, Any] | None, settings: dict[str, Any]) -> dict[str
|
||||
"volume_m3": round(volume, 3),
|
||||
"distance_m": settings.get("spoil_site_distance_m"),
|
||||
"note": " · ".join(note_parts),
|
||||
"by_ground_m3": {key: round(value, 3) for key, value in grounds.items()},
|
||||
"ground_unknown_m3": round(unknown, 3),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user