"""160-5 B08 채집석 공제 — 돌(채집) 둘 · 고임돌 · 막자갈 계 합(엑셀 Y68) → 총괄 줄 · 토공집계표 잔토처리에서 뺌(엑셀 토적집계표 H32) · 잔토처리 끝수 = ROUND 0 그대로(브레인 정함) · 거울. """ from __future__ import annotations import asyncio import json import subprocess from pathlib import Path from B08_Quantity import B08_Quantity_Router_Material as router from B08_Quantity.B08_Quantity_Engine_Earthwork_Summary import ( excel_round, haul_lists, summary_rows, ) from B08_Quantity.B08_Quantity_Material_Calc import clean_overrides, compute from resources.tester.test_121_b08_material_sheets import SUMMARY, UNITS, _run_ts ROOT = Path(__file__).resolve().parents[2] BALANCE = 994.4747340425532 # 엑셀 사토운반본선!K19 TOTALS = { "cut_soil_volume_m3": 0.0, "cut_rock_volume_m3": 0.0, "ditch_soil_volume_m3": 0.0, "ditch_rock_volume_m3": 0.0, "adjusted_total_m3": 0.0, "fill_volume_m3": 0.0, "diverted_m3": 0.0, "balance_m3": BALANCE, "benching_area_m2": 0.0, } def _deduct_row(sheets): return next(row for row in sheets["material"]["rows"] if row["key"] == "picked_deduct") def test_총괄_채집석_공제_엑셀_Y68(): over = clean_overrides({"units": UNITS}) sheets = compute(SUMMARY, over) row = _deduct_row(sheets) # 32.85 + 104.4 + 52.47 + 84.94 = 274.66 · 끝수 없음 · 할증 없음 assert (row["group"], row["unit"], row["qty"], row["amount"]) == ( "채집석 공제", "㎥", 274.66, 274.66, ) assert row["surcharge"] is None keys = {r["key"] for r in sheets["material"]["rows"] if r["qty"] is not None} assert not {"stone_pick_35", "stone_pick_45", "bed_stone"} & keys # 채집재는 총괄로 안 감 assert _run_ts(SUMMARY, over) == sheets assert _deduct_row(compute(SUMMARY, clean_overrides({})))["qty"] is None # 채집재 없으면 빈칸 def test_잔토처리에서_뺌_엑셀_사토(): rows = summary_rows(TOTALS, haul_lists(None), 90, 274.66) value = {r["group"]: r["value"] for r in rows} assert excel_round(BALANCE - 274.66, 2) == 719.81 # 엑셀 토적집계표 F32 assert value["잔토처리"] == 720 # ROUND 0 그대로 assert value["채집석 공제"] == 274.66 assert {r["group"]: r["value"] for r in summary_rows(TOTALS, haul_lists(None))}[ "잔토처리" ] == 994 def test_거울_TS_잔토처리(): rows = summary_rows(TOTALS, haul_lists(None), 90, 274.66) calc = (ROOT / "B08_Quantity" / "B08_Quantity_Options_Calc.ts").read_text(encoding="utf-8") script = calc + ( f"\nconst rows = {json.dumps(rows, ensure_ascii=False)};" f"\nconsole.log(JSON.stringify(recountAdjustedSummary(rows, {json.dumps(TOTALS)}, 0)));" ) result = subprocess.run( ["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript", "-e", script], capture_output=True, text=True, encoding="utf-8", timeout=60, ) # fmt: skip assert result.returncode == 0, result.stderr assert json.loads(result.stdout) == json.loads(json.dumps(rows)) def test_서버_공제는_저장본(tmp_path, monkeypatch): async def summary(_project_id): return {**SUMMARY, "rows": [], "notes": []} async def project_root(_project_id): return tmp_path monkeypatch.setattr(router, "structure_summary", summary) monkeypatch.setattr(router, "_project_root", project_root) assert asyncio.run(router.picked_deduction("p", tmp_path)) is None asyncio.run(router.save_material_sheets("p", {"overrides": {"units": UNITS}})) assert asyncio.run(router.picked_deduction("p", tmp_path)) == 274.66 earth = (ROOT / "B08_Quantity" / "B08_Quantity_Router_Earthwork.py").read_text("utf-8") assert 'summary_rows(table["totals"], haul, soft, deduct)' in earth