Files
Aislo/resources/tester/test_820_1_small_machines.py
T
eomsangdonandClaude Sonnet 5.5 bce7e245e0 fix(820-2): 시간당 이음 글 빈칸 든 입력 이름 따옴표 — 파워트로웰 EST main 504 번 오류 고침
- M02_San_Link.hourly_text 가 「전력 계약종별=…」 를 따옴표 없이 적어 가져온 식 읽기(_link)가 못 읽음 → 이름을 따옴표로(읽는 쪽은 이미 받음)
- 지킴 시험(test_820_1) 왕복 + EST main 504 번 = 저장 금액 · 우리 단가 셈 추가

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 06:07:14 +09:00

174 lines
8.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""820-1 지킴 — 품셈에 없는 소형 · 보조 기계 13 줄(EST 구성 씨앗 · 가격 갱신 필요 · 요구 785 ~ 787).
(a) 새 줄 · 값 — 발전기 손료계수 2362 공통(품셈 발전기 계열) · 브레이커 0.18 = 0.2㎥ 계수 · 가격 = 씨앗 ·
파이프 = 달러 × 환율(비고에 $ 값) · 비고 「갱신 필요」
(b) 계산식 — 손료 = 버림(가격 × 1000 × 손료계수 ÷ 10⁷) · 손료만 = GC000273 · 발전기 5.5 = GC000268 ·
파워트로웰 = GC001195(휘발유 + 잡품(주연료 줄의 %) + 전력) · 옛 엔진 = 새 수식 문서 · 시간당 이음 한 길
(c) 이음 — 가져온 이름 · 규격 → 새 줄 · 옛 기계 그대로
(d) 다른 문서 그대로 — 새 고르기는 목록 끝에만 · 기본값 그대로
"""
from __future__ import annotations
import importlib
import json
import sys
from decimal import Decimal
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
for p in (ROOT, ROOT / "resources/master_data/scripts"):
if str(p) not in sys.path:
sys.path.insert(0, str(p))
from test_815_1_import_link import no_aliases # noqa: E402 — 진짜 마스터 별칭 빼고
from M01_MasterData import M01_MasterData_Store as m01 # noqa: E402
from M02_MasterTemplete import M02_Doc_Import as di # noqa: E402
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
from M02_MasterTemplete import M02_Machine_Hourly as mh # noqa: E402
ml = importlib.import_module("master_doc_link")
mc = importlib.import_module("master_doc_calc")
b802 = importlib.import_module("build_수식문서_802_2")
meq = importlib.import_module("master_equation")
NEW = {f"EQ{n:06d}" for n in range(656, 669)}
SEED = { # 키 → (이름, 규격, 가격, 손료계수, 시간당 식)
"EQ000656": ("발전기", "5.5㎾", 1063, 2362, "GC000268"),
"EQ000657": ("발전기", "7㎾", 1828.9, 2362, "GC000273"),
"EQ000658": ("발전기", "10㎾", 3213, 2362, "GC000273"),
"EQ000659": ("대형 브레이커", "0.18㎥", 4559, 6601, "GC000273"),
"EQ000660": ("에어 호스", "1.91㎝", 105, 5625, "GC000273"),
"EQ000661": ("모노펌프", "5-20㎥/hr", 4900, 4346, "GC000273"),
"EQ000662": ("파워트로웰", "3.73㎾", 2680, 5313, "GC001195"),
}
USD = {"150㎜×1m": 25, "150㎜×2m": 37, "150㎜×3m": 46,
"곡관 150㎜ 45°": 67, "곡관 150㎜ 60°": 84, "곡관 150㎜ 90°": 103} # fmt: skip
def _eq() -> dict[str, dict]:
return m01.loaded()[1].index["EQ"]
def _docs():
docs = ml.load_all()
return docs, ml.Links(m01.loaded()[1], docs)
def _loss(price, factor) -> Decimal:
return (Decimal(str(price)) * 1000 * Decimal(factor) / 10_000_000).quantize(Decimal(1), "ROUND_DOWN") # fmt: skip
def test_rows_and_values() -> None:
eq = _eq()
assert NEW <= set(eq)
for key, (name, spec, price, factor, _) in SEED.items():
row = eq[key]
assert (row["이름"], row["규격"]) == (name, spec), key
assert row["값"]["가격"] == Decimal(str(price)) and row["값"]["손료계수"] == factor, key
assert "갱신 필요" in row["비고"] and row["출처"]["가격"] == "자체", key
gens = [r for r in eq.values() if r.get("상세구분") == "발전기"]
assert {int(r["값"]["손료계수"]) for r in gens} == {2362} # 발전기 계열 공통
small, big = eq["EQ000659"]["값"], eq["EQ000031"]["값"]
for k in ("가격", "상각비계수", "정비비계수", "관리비계수", "손료계수"):
assert small[k] == big[k], k
pipes = {r["규격"]: r for r in eq.values() if r.get("상세구분") == "콘크리트펌프용 파이프"}
assert set(pipes) == set(USD)
rate = m01.loaded()[1].index["FX"]["FX000001"]["값"]
for spec, usd in USD.items():
row = pipes[spec]
assert row["값"]["손료계수"] == 4500 and f"${usd} 표기" in row["비고"]
assert abs(float(row["값"]["가격"]) - usd * float(rate) / 1000) < 1e-3
trowel = eq["EQ000662"]["값"]
assert (trowel["주연료종류"], trowel["주연료"], trowel["잡재료율"], trowel["전력"]) == ("휘발유", 1, 10, Decimal("3.73")) # fmt: skip
def test_formula_loss_and_one_way() -> None:
docs, links = _docs()
files, whole = m01.loaded()
logic = {r["키"]: r for _, r in b802.rows_of(files)}
for key in sorted(NEW):
row = _eq()[key]
got = mh.hourly(key)
assert got["ok"], (key, got)
want = SEED.get(key, (None, None, None, None, "GC000273"))[4]
assert got["로직"] == want, key
lg = logic[want]
given = meq.given_of(whole, lg)
changed = {"기계": row["원문번호"]} if mh.takes_machine(lg) else None
new = b802.new_answer(links, docs[want], changed)
old = b802.old_answer(whole, lg, {**given, **(changed or {})})
assert old == new and float(new["계"]) == got["계"], key # 옛 엔진 = 새 문서 = 시간당 이음
assert new["경비"] == _loss(row["값"]["가격"], row["값"]["손료계수"]), (
key
) # 손료 = 가격 × 계수
if want == "GC000273":
assert new["노무비"] == new["재료비"] == 0, key
assert di.hourly_link("EQ000662", "재료비")["키"] == "GC001195"
assert di.hourly_link("EQ000656", "노무비")["입력"]["기계"] == "'자체 발전기 5.5㎾'"
def test_trowel_lines() -> None:
docs, links = _docs()
got = mc.calc(docs["GC001195"], links)
lines = {x["이름"]: x for x in got["줄"]}
assert set(lines) == {"주연료", "전력", "잡재료비", "기계손료"}
assert lines["전력"]["수량"] == Decimal("3.73") and lines["주연료"]["수량"] == 1
assert lines["잡재료비"]["금액"] == (lines["주연료"]["금액"] * Decimal("0.1")).quantize(Decimal("0.1"), "ROUND_DOWN") # fmt: skip
assert got["노무비"] == 0 and got["경비"] == _loss(2680, 5313)
def test_find_links() -> None:
cases = {
("발전기", "5.5kW"): "EQ000656", ("발전기", "7kW"): "EQ000657",
("발전기", "10kW"): "EQ000658", ("발전기", "25kW"): "EQ000452",
("에어 호스", "1.91cm"): "EQ000660", ("에어호스", "1.91cm"): "EQ000660",
("모노펌프", "5-20㎥/hr"): "EQ000661", ("모노펌프", ""): "EQ000661",
("브레이카", "0.18㎥"): "EQ000659", ("대형브레이카", "0.2㎥"): "EQ000031",
("파워트로웰", "3.73kW"): "EQ000662",
("콘크리트펌프용 파이프", "150mm×1m"): "EQ000663",
("콘크리트펌프용 파이프", "150㎜×3m"): "EQ000665",
("콘크리트펌프용 파이프", "곡관 45˚"): "EQ000666",
("콘크리트펌프용 파이프", "곡관 150㎜ 90°"): "EQ000668",
} # fmt: skip
with no_aliases("EQ"):
for (name, spec), key in cases.items():
got = mf.find(name, spec)
assert (got["판정"], got["키"]) == (mf.LINKED, key), (name, spec, got)
def test_other_docs_same() -> None:
"""새 고르기는 기계경비식 목록 끝에만 · 기본값은 옛 기계 그대로."""
text = (ROOT / "resources/master_data/로직_건설품셈_08장_건설기계.json").read_text("utf-8")
codes = {r["원문번호"] for k, r in _eq().items() if k in NEW}
for row in json.loads(text)["줄"]:
for spec in row.get("입력") or []:
pick = [str(c) for c in spec.get("고르기") or []]
mine = [i for i, c in enumerate(pick) if c in codes]
if mine:
assert mine == list(range(len(pick) - len(mine), len(pick))), row["키"]
assert pick[0] not in codes, row["키"]
old = [k for k, r in _eq().items() if k not in NEW]
assert not [k for k in old if str(_eq()[k].get("원문번호") or "").startswith("자체 ")]
def test_blank_input_name_link_roundtrip() -> None:
"""820-2 — 빈칸 든 입력 이름(「전력 계약종별」)이 이음 글 → 읽기 왕복 · EST main 504 번 = 저장 금액 · 우리 단가 셈."""
from M02_MasterTemplete import M02_San_Link as sl
from M02_MasterTemplete import M02_Sangeun_Imported as imp
text = sl.hourly_text("EQ000662")
assert "'전력 계약종별'=" in text
node = di.dc.cv.mf.parse(text)
assert node[0] == "logic" and "전력 계약종별" in [k for k, _ in node[2]]
assert imp._link(text, "로직", "시험", imp._whole()) == text
import test_810_2_native_doc as t
out = t.fn.emit(t.native("EST", "main"), 504)
sk, _, got = t.doc_and_money(out)
assert float(got["계"]) == 546 # 원파일 저장 금액
sk[imp.KEY]["단가기준"] = "우리"
ours = di.run(di.derive(sk, None, t.whole()), sk, t.whole())
assert ours.get("셈됨") is not False and ours["계"] is not None