B08 자재 길 답 = {source, sheets: {material}} 인데 B09 가 한 층 위를 읽어 총괄 줄 · 운반 수량이 늘 빈칸 · 시험 가짜 답도 같은 꼴로 바로잡음
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sDZSjFvoiDjdGpgj3muc3
117 lines
5.0 KiB
Python
117 lines
5.0 KiB
Python
"""166-3 재료비목록표 = B08 총괄 합계 × 적용 단가 = 총액 ROUND 0 · 공제 거름 · MT 합침 · 거울."""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
if str(ROOT) not in sys.path:
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from B09_Estimation import B09_Estimation_Prices as prices # noqa: E402
|
||
from B09_Estimation.B09_Estimation_Calc_Base import compute_base # noqa: E402
|
||
from resources.tester.test_144_b09_base_calc import _run_ts # noqa: E402
|
||
|
||
MATERIALS = [
|
||
{"key": "MT1", "name": "부직포", "prices": {"물가자료": 1234.5}, "qty": 10.25},
|
||
{"key": "MT2", "name": "수량 없는 사본 줄", "prices": {"물가자료": 500}},
|
||
{"key": "b08:cement", "name": "시멘트", "prices": {}, "qty": 3.333},
|
||
{"key": "b08:sand", "name": "모래", "prices": {}, "qty": 7},
|
||
]
|
||
INPUTS = {"labor": [], "machines": [], "materials": MATERIALS}
|
||
USER = {"materials": {"b08:cement": {"applied": 7000.5}}}
|
||
|
||
|
||
def test_총액_ROUND_0_빈칸():
|
||
rows = {r["key"]: r for r in compute_base(INPUTS, USER)["material"]}
|
||
assert (rows["MT1"]["qty"], rows["MT1"]["price"], rows["MT1"]["amount"]) == (
|
||
10.25,
|
||
1234.5,
|
||
12654,
|
||
) # 12,653.6 → 반 올림
|
||
assert rows["b08:cement"]["amount"] == 23333 # 3.333 × 7000.5 = 23,332.67
|
||
assert rows["MT2"]["qty"] is None and rows["MT2"]["amount"] is None # 수량 빈칸
|
||
assert rows["b08:sand"]["price"] is None and rows["b08:sand"]["amount"] is None # 단가 빈칸
|
||
|
||
|
||
def test_거울_TS():
|
||
for user in ({}, USER):
|
||
assert _run_ts(INPUTS, user) == compute_base(INPUTS, user)
|
||
|
||
|
||
def test_공제_거름_MT_합침(monkeypatch):
|
||
sheets = {
|
||
"material": {
|
||
"rows": [
|
||
{"key": "cement", "group": "시멘트", "unit": "포", "amount": 12.5},
|
||
{"key": "picked_deduct", "group": "채집석 공제", "unit": "㎥", "amount": 4},
|
||
{"key": "MT1", "group": "부직포", "unit": "㎡", "amount": 30.1},
|
||
{"key": "MT9", "group": "사본에 없는 MT", "unit": "개", "amount": 2},
|
||
{"key": "empty", "group": "수량 없음", "amount": None},
|
||
]
|
||
}
|
||
}
|
||
keys = [r["key"] for r in prices.b08_material_rows(sheets)]
|
||
assert keys == ["b08:cement", "b08:MT1", "b08:MT9"]
|
||
|
||
monkeypatch.setattr(prices, "_master", lambda: None)
|
||
monkeypatch.setattr(
|
||
prices,
|
||
"demand",
|
||
lambda master, keys: {"labor": [], "machines": [], "materials": ["MT1", "MO1"],
|
||
"fuel_keys": {}, "missing": []},
|
||
) # fmt: skip
|
||
book = {"rows": {"MT1": {"key": "MT1", "name": "부직포"}, "MO1": {"key": "MO1"}}}
|
||
got = prices.build_inputs(book, [], sheets)["materials"]
|
||
assert [r["key"] for r in got] == ["MT1", "MO1", "b08:cement", "b08:MT9"]
|
||
assert got[0]["qty"] == 30.1 and "qty" not in book["rows"]["MT1"] # 책은 안 바꿈
|
||
assert got[2]["qty"] == 12.5
|
||
|
||
|
||
def test_167_2_B08_답_한_층_아래_총괄_줄(monkeypatch):
|
||
"""B08 길 답 = {source, sheets: {material: {rows}}} — sheets 를 읽어야 총괄 줄이 들어옴."""
|
||
import asyncio
|
||
|
||
from B09_Estimation import B09_Estimation_Router as router_mod
|
||
|
||
rows = [
|
||
{"key": "pipe_1200", "group": "파형강관", "spec": "Φ1200mm", "unit": "m", "amount": 7},
|
||
{
|
||
"key": "coupling_1200",
|
||
"group": "커플링밴드",
|
||
"spec": "Φ1200mm",
|
||
"unit": "개",
|
||
"amount": 0,
|
||
},
|
||
{"key": "합판(준내수)|㎡", "group": "합판(준내수)", "unit": "㎡", "amount": 7},
|
||
{"key": "picked_deduct", "group": "채집석 공제", "amount": None},
|
||
]
|
||
|
||
async def materials(_pid):
|
||
return {"source": {"columns": []}, "notes": [], "sheets": {"material": {"rows": rows}}}
|
||
|
||
async def earthwork(_pid):
|
||
return None
|
||
|
||
monkeypatch.setattr(router_mod, "material_sheets", materials)
|
||
monkeypatch.setattr(router_mod, "earthwork_table", earthwork)
|
||
b08 = asyncio.run(router_mod._b08(None, []))
|
||
assert b08["summary"] == {"columns": []}
|
||
got = prices.b08_material_rows(b08["materials"])
|
||
assert [(r["key"], r["qty"], r.get("supply")) for r in got] == [
|
||
("b08:pipe_1200", 7, "관급"),
|
||
("b08:coupling_1200", 0, None),
|
||
("b08:합판(준내수)|㎡", 7, None),
|
||
]
|
||
s = compute_base({"labor": [], "machines": [], "materials": got}, USER_PIPE)
|
||
rows_out = {r["key"]: r for r in s["material"]}
|
||
assert rows_out["b08:pipe_1200"]["supply"] == "별도" # 사용자 표시가 앞섬
|
||
plain = compute_base({"labor": [], "machines": [], "materials": got}, {})
|
||
assert plain["material"][0]["supply"] == "관급" # 파형강관 기본 = 관급
|
||
assert rows_out["b08:합판(준내수)|㎡"]["amount"] == 7 * 12000
|
||
assert rows_out["b08:coupling_1200"]["price"] is None # 단가 없음 = 빈칸(견적 칸으로)
|
||
assert _run_ts({"labor": [], "machines": [], "materials": got}, USER_PIPE) == s
|
||
|
||
|
||
USER_PIPE = {"materials": {"b08:합판(준내수)|㎡": {"quote": 12000},
|
||
"b08:pipe_1200": {"supply": "별도"}}} # fmt: skip
|