"""운반 물량의 **상태** — 내역서 수량은 자연상태다 (2026-09-09). 「운반거리의 산정 시에 모든 수량은 다짐상태로 환산하여 계산하고, 내역서에 적용하는 수량은 자연상태로 한다」(설계실무 요령 — `config_system_design` 5-4-3 인용문). ⚠ 이 시험이 잠그는 것은 **방향**이다. 곱하면 토사가 0.9배가 되어 뒤집힌다. ⚠ `L`(1.3·1.35·1.625)을 쓰지 않는다는 것도 함께 잠근다 — 품셈이 `f = 1/L` 을 스스로 곱한다. """ from __future__ import annotations import sys from pathlib import Path import pytest PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import ( # noqa: E402 SummaryInput, build_table as build_summary, ) from B08_Quantity.B08_Quantity_Engine_HaulSummary import ( # noqa: E402 build_table, natural_m3, summary_input_rows, ) from config.config_system_design import EARTHWORK_CONVERSION_FACTORS # noqa: E402 _PLAN = { "blocks": [ { "bands": [ { "equipment": "dozer", "haul_distance_m": 40.0, "haul_from_m": 0.0, "haul_to_m": 40.0, "ea_m3": 90.0, "rr_m3": 115.0, "br_m3": 0.0, }, { "equipment": "dump_truck", "haul_distance_m": 300.0, "haul_from_m": 0.0, "haul_to_m": 300.0, "ea_m3": 0.0, "rr_m3": 0.0, "br_m3": 130.0, }, ] } ], "transfers": [], } def _row(table: dict, equipment: str, ground: str) -> dict: return next( row for row in table["rows"] if row["equipment"] == equipment and row["ground"] == ground ) def test_나누기다_곱하기가_아니다() -> None: for ground, kind in (("토사", "soil"), ("리핑암", "ripping_rock"), ("발파암", "blasting_rock")): factor = EARTHWORK_CONVERSION_FACTORS[kind]["compacted"] assert natural_m3(100.0, ground) == pytest.approx(100.0 / factor) # 방향이 뒤집히면 이 줄이 잡는다. assert natural_m3(100.0, ground) != pytest.approx(100.0 * factor) # 토사는 늘고(÷0.9) 암은 준다(÷1.15·÷1.30) — 부호가 갈래마다 다르다. assert natural_m3(100.0, "토사") > 100.0 assert natural_m3(100.0, "리핑암") < 100.0 assert natural_m3(100.0, "발파암") < 100.0 def test_L_은_쓰지_않는다() -> None: """품셈 10-11·10-12 가 `f = 1/L` 을 스스로 곱하므로 우리가 또 들면 두 번 환산이다.""" for ground, loose in (("토사", 1.3), ("리핑암", 1.35), ("발파암", 1.625)): assert natural_m3(100.0, ground) != pytest.approx(100.0 / loose) def test_갈래를_모르면_환산하지_않는다() -> None: assert natural_m3(100.0, "지반모름") is None assert natural_m3(100.0, "") is None def test_네_줄이_두_상태를_함께_낸다() -> None: table = build_table(_PLAN) dozer_soil = _row(table, "dozer", "토사") assert dozer_soil["volume_m3"] == pytest.approx(90.0) assert dozer_soil["volume_basis"] == "compacted" assert dozer_soil["natural_m3"] == pytest.approx(100.0) # 90 ÷ 0.90 assert dozer_soil["conversion_c"] == pytest.approx(0.90) assert _row(table, "dozer", "리핑암")["natural_m3"] == pytest.approx(100.0) # 115 ÷ 1.15 assert _row(table, "dump_truck", "발파암")["natural_m3"] == pytest.approx(100.0) # 130 ÷ 1.30 # 거리는 다짐 기준 그대로 — 환산이 거리를 건드리면 안 된다. assert dozer_soil["average_distance_m"] == pytest.approx(40.0) def test_집계표는_자연상태로_싣는다() -> None: haul = build_table(_PLAN) summary = build_summary( SummaryInput( earthwork_totals={}, slope_totals={}, haul_rows=summary_input_rows(haul), rock_classes=[], rock_ratios_pct={}, application_ratios={}, ) ) rows = [row for row in summary["rows"] if row["group"] in ("도자운반", "덤프운반")] assert rows, "운반 줄이 서야 한다" for row in rows: assert row["amount"] == pytest.approx(100.0), row assert "자연상태 환산" in row["note"] def test_검산은_다짐상태끼리_한다() -> None: """환산값으로 검산하면 늘 어긋난다 — 계획이 다짐이기 때문이다.""" from B08_Quantity.B08_Quantity_Engine_HaulSummary import check_against_plan plan = dict(_PLAN, hauled_m3=335.0, transferred_m3=0.0) check = check_against_plan(build_table(plan), plan) assert check.difference_m3 == pytest.approx(0.0)