Files
Aislo/resources/tester/test_160_5_b08_picked_deduct.py
T
eomsangdonandClaude Opus 5.5 c6bb3cc5b6 feat(b08): 자재수량 → 총괄 · 콘크리트소요 · 채집석 공제 엑셀 흐름(160-3 · 4 · 5)
- 160-3 맞춤 없는 사본 자재 = 총괄 끝 줄(이름 · 규격 · 단위 그대로 · 알림에 이름 목록) · 맞춤 = 이름 · 규격 · 단위 · 기계 주연료 · 할증(ℓ) 뺌 · 참조 줄(승격 · 사전조합) 안 재료 셈 · 고른 품목 = MT 코드 · 파이썬 · TS 짝
- 160-4 콘크리트(기계) · 모르터 1:3 열 늘 보임(손 단위수량 → 콘크리트소요) · 엑셀 손값과 다른 배합 · 할증은 표 아래 알림 한 줄
- 160-5 채집석 공제(돌 채집 둘 · 고임돌 · 막자갈 합) = 총괄 줄 · 토공 잔토처리 = ROUND 0(잔토 − 공제) · 공제 줄 보임 · TS 짝
- 시험 test_160_3 · 4 · 5 새 · test_143_3 · 143_4 · 69 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sDZSjFvoiDjdGpgj3muc3
2026-10-09 22:53:18 +09:00

99 lines
3.8 KiB
Python

"""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