- 끝수 한 곳 `master_cut.py`(B09 `rounddown` 재사용) — 엔진 답에 입힘: 줄 비목마다 0.1 원 버림 · 하위 로직 줄 = 하위 끝수 계 × 수량 · 덧줄 = 버린 줄 합으로 다시 셈 · 비목 합 1 원 버림 · 중간 칸도 버린 값 - 쓰는 곳: M01 시험 계산(`check_master.calc`) · 읽는 식(`master_text.lines`) · 옛 조합 미리보기(`master_combo`) · M02 `run_line`(단가산출 · 일위대가 · 펼침) · 별도계상 뺀 기준 · 켜쌓기율 곱 뒤 · 일위대가 반영 · 반영합 · 비율 줄 · 사전조합 안 줄 - 일위대가 반영 TS 짝(`Labor_Calc.ts`) 같은 끝수 · 거울 헬퍼가 상대 경로 .ts 가져옴 - B09 = `run_line(raw=True)` 엔진 날 값(제 단가로 다시 버림 · % 덧줄 율 비 그대로) — 검증_sub3 B09 247 칸 전후 같음 - `master_text` 꼴 셋 = `common_util_money` 로 갈음(줄 금액 `fmt_line_money` · 식 안 금액 = 버린 0.1 원) - 화면: 상세 줄 금액 · 시험 계산 줄 · 중간 돈 = `formatLineMoney` · 일위대가 반영 칸 0.1 원 · 상세 바닥 · 박스 · 인력재료경비 비목 소계 = 줄 합 1 원 버림 - 테스트1 네 보기 · 서고 목록 = 124,419(옛 124,420 / 124,419.85) · 재료 줄 11,694.6 + 23,425.6 + 1,404.8 = 소계 36,525 - 새 지킴 시험 test_751_3_money_cut - 옛 날 금액(끝수 없음)을 대조하던 시험 24 파일 고침 — 까닭 = 금액 규칙이 줄 0.1 원 · 소계 1 원 버림으로 바뀜(고정 값 → 새 값 · 줄 합 = 계 → 줄 합 아래 비목마다 1 원 안 · 곱 비 = 끝수 폭 안) · 실무 xlsx 영월 엑셀 같음 22 → 23 · 이어 붙이는 TS 시험(704_3 · 717_2b)에 `rounddown` 같은 식 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
126 lines
5.3 KiB
Python
126 lines
5.3 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 full["기준"]["계"] == Decimal("153889") # 751-3 1 원 버림
|
|
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 one["기준"]["계"] == Decimal("44791.1") # 10㎡ 447,911(1 원 버림) ÷10
|
|
|
|
|
|
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") # 화면 = 바탕 몫 그대로
|
|
|
|
|
|
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단위"]["계"]
|
|
# ㎡ 몫 = 100㎡ 값 ÷100 · 비목 합 1 원 버림(751-3)이라 바탕 몫 ÷10 = 0.1 원 안
|
|
assert abs(added - alone * qty / 10) <= Decimal("0.1")
|
|
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)
|