- 서버 Sangeun_Calc: 배 = 바탕 기준단위 → SK 단위(master_unit.swap) · 성격 다름 = 문서 환산 {계수 | 단위중량} · 없으면 셈 · 저장 막음(까닭 글) · 「식」 · 빈 단위 = 옛 셈 그대로(일위대가 줄 모드)
- 화면 셈 답 줄 · 계 = SK 1 단위 몫(per_unit · 화면배) · 머리 띠 「100본 당」 · 「1 본 당」
- 서버 Sangeun_Doc: 환산 칸 검사(계수 양수 · 단위중량 종별 · 형상)
- B09 Labor_Source: SK 참조 배 = 같은 ratio_of(옛 바탕 앞 수 대신 · 기존 문서 같은 값)
- 화면 Sangeun_Detail: SK 단위 처음 값 = 기초 기준단위(「100본」) · 둘째 기초 수량 처음값 = 바탕 기준단위 ÷ 그 기초 기준단위(swapUnit) · 계 칸 원/SK 단위
- 새 Sangeun_Unit: 근거 글 · 환산 계수 칸 · 단위중량 표(CC000337) 고르기
- Cover_Model: 화면 품 × 화면배 = 문서 품(덮개 · 더한줄 · 세부) · 율 그대로
- Find_Pick: 돈 아닌 기초(결과단위 「원」 아님 21 개) 목록에서 뺌
- 낡은 시험 test_715_sangeun_engine(바탕 원/주 + SK ㎡) · test_727(바탕 원/㎡ + SK ㎥) = 성격 다른 단위가 이제 계수 없으면 막혀 SK 단위를 바탕 단위로 고침(금액 같음)
- 새 시험 test_728_2_sk_unit(100본 → 본 1,538.89 · ㎡ 44,791.32 · 계수 · 단위중량 · 식 · 둘째 기초 · 돈 아닌 기초 · 고침 꼴)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
5.2 KiB
Python
125 lines
5.2 KiB
Python
"""728-2 지킴 — SK 단위 환산: 바탕 기준단위 → SK 단위 배 · 화면 줄 · 계 = SK 1 단위 몫 ·
|
|
계수 없으면 셈 막음 · 「식」 = 옛 셈 · 둘째 기초 수량 처음값 · 돈 아닌 기초 고르기 창에서 뺌 ·
|
|
고침 꼴 화면배 되돌림."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from decimal import Decimal
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
HERE = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(ROOT))
|
|
sys.path.insert(0, str(ROOT / "resources/master_data/scripts"))
|
|
|
|
import master_unit as mu # noqa: E402
|
|
|
|
from M01_MasterData import M01_MasterData_Store as m01 # noqa: E402
|
|
from M02_MasterTemplete import M02_Sangeun_Calc as sc # noqa: E402
|
|
from M02_MasterTemplete import M02_Sangeun_Doc as sd # noqa: E402
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def whole():
|
|
return m01.loaded()[1]
|
|
|
|
|
|
def _doc(base, unit, **more):
|
|
return {"키": "SK-999999", "이름": "sub3임시", "단위": unit, "바탕": {"로직": base}, **more}
|
|
|
|
|
|
def _shown(whole, doc):
|
|
clean = sd.clean(doc, None, whole)
|
|
return sc.per_unit(sc.run(clean, whole), clean)
|
|
|
|
|
|
def test_단목베기_100본_제안_본으로_바꾸면_1본_계(whole):
|
|
full = _shown(whole, _doc("GF000287", "100본"))
|
|
assert full["배"] == 1 and round(full["기준"]["계"], 2) == Decimal("153889.40")
|
|
assert sc.head_of({"단위": "100본"}) == "100본 당"
|
|
one = _shown(whole, _doc("GF000287", "본"))
|
|
assert one["배"] == 100 and round(one["기준"]["계"], 2) == Decimal("1538.89")
|
|
assert sc.head_of({"단위": "본"}) == "1 본 당"
|
|
pairs = [(x["수량"], y["수량"]) for x, y in zip(full["줄"], one["줄"], strict=True)]
|
|
assert all(b == a / 100 for a, b in pairs) and one["화면배"] == 100 # 세부 품 ÷100
|
|
|
|
|
|
def test_사석_수상고르기_10제곱미터_에서_1제곱미터(whole):
|
|
one = _shown(whole, _doc("GC000421", "㎡"))
|
|
assert round(one["기준"]["계"], 2) == Decimal("44791.32")
|
|
|
|
|
|
def test_성격_다르면_계수_칸_없으면_셈_막음(whole):
|
|
with pytest.raises(sd.DocError, match="환산 계수"):
|
|
_shown(whole, _doc("GF000099", "m"))
|
|
base = _shown(whole, _doc("GF000099", "㎥"))["기준"]["계"]
|
|
two = _shown(whole, _doc("GF000099", "m", 환산={"계수": 2}))
|
|
assert two["배"] == 2 and two["기준"]["계"] == base / 2
|
|
with pytest.raises(sd.DocError, match="단위중량"):
|
|
_shown(whole, _doc("GF000099", "톤"))
|
|
rock = _shown(
|
|
whole, _doc("GF000099", "톤", 환산={"단위중량": {"종별": "암석", "형상": "화강암"}})
|
|
)
|
|
assert rock["배"] == Decimal("2.7") # 1 ㎥ = 2.7 톤(2,700 ㎏/㎥)
|
|
with pytest.raises(sd.DocError, match="0 보다"):
|
|
sd.clean(_doc("GF000099", "m", 환산={"계수": 0}), None, whole)
|
|
|
|
|
|
def test_식_빈_단위는_옛_셈(whole):
|
|
old = _shown(whole, _doc("GF000287", "식"))
|
|
assert old["배"] == 100 and round(old["1단위"]["계"], 2) == Decimal("1538.89")
|
|
assert "화면배" not in old and old["기준"]["계"] == Decimal("153889.4") # 화면 = 바탕 몫 그대로
|
|
|
|
|
|
def test_둘째_기초_수량_처음값_배(whole):
|
|
"""바탕 10㎡(GC000421) + 둘째 기초 100㎡(GF000041) — 처음값 0.1 = 바탕 기준단위 몫."""
|
|
qty = mu.swap("10㎡", "100㎡")["배"]
|
|
assert qty == Decimal("0.1")
|
|
alone = sc.run(sd.clean(_doc("GF000041", "100㎡"), None, whole), whole)["1단위"]["계"]
|
|
one = {"종류": "로직", "로직": "GF000041", "입력값": {}, "수량": float(qty)}
|
|
got = sc.run(sd.clean(_doc("GC000421", "㎡", 더한줄=[one]), None, whole), whole)
|
|
bare = sc.run(sd.clean(_doc("GC000421", "㎡"), None, whole), whole)
|
|
added = got["1단위"]["계"] - bare["1단위"]["계"]
|
|
assert abs(added - alone * qty / 10) < Decimal("0.01") # ㎡ 몫 = 100㎡ 값 ÷100
|
|
src = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "swapUnit(" in src and "addBase(" in src and "baseUnitOf(" in src
|
|
|
|
|
|
def test_돈_아닌_기초_고르기_창에서_뺌(whole):
|
|
money = [
|
|
r
|
|
for tid, d in whole.index.items()
|
|
if tid in ("GC", "GF", "QC", "QF")
|
|
for r in d.values()
|
|
if isinstance(r, dict)
|
|
and "결과단위" in r
|
|
and not str(r["결과단위"]).strip().startswith("원")
|
|
]
|
|
assert len(money) == 21
|
|
src = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts").read_text(encoding="utf-8")
|
|
assert '!logic.결과단위.trim().startsWith("원")' in src
|
|
|
|
|
|
def test_고침_꼴_화면배_되돌림():
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(HERE / "helper_728_2_cover_unit.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
out = json.loads(proc.stdout)
|
|
assert out["was"]["qty"] == pytest.approx(0.2) # 바탕 값 = 문서 몫(100본)
|
|
assert out["cover"]["labor:LB000101#0"]["qty"] == pytest.approx(0.3)
|
|
assert out["added"] == pytest.approx(60)
|
|
assert out["shown"]["0"]["qty"] == pytest.approx(0.003) # 다시 그리면 화면 몫
|
|
assert out["shown"]["1"]["qty"] == pytest.approx(0.6)
|