Files
Aislo/resources/tester/test_stmate_smt_tools.py
T
eomsangdonandClaude Opus 5.5 4ff96a6771 feat(stmate-test): REA MCP(Ghidra) 계산 엔진 — 표준시장단가(A 코드 COSTA) 내역 연결 · EXRNM 켜짐 경로 · 손료 반올림 MROUNDUD · 적용기준 콤보 값
- A 코드 = COSTA · S_JDA 참 = 저장 단가 · 거짓 = AAMTn 노무비율 나눔 · ACTTYP·ACTOPT = PRJLST · calcBoq·api_load 연결
- EXRNM 참 = S 줄만 단가×율/1000 · ECD 자리 버림 · 천원 · 전각 $
- 손료 계수 반올림 = 첫 버림 자리 5 이상 올림
- 적용기준 국가 2·지방 0·행자부 1 · 행자부 RATE_M 조회 rateMLookup
- 지식DB 36번

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 15:44:22 +09:00

73 lines
2.9 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.
"""STMATE 재현 앱 계산 도구 — 노무·장비 수량 계산기(wM_Edit_XRate) · 실적→일식 비목율(wM_Conv_A2W).
근거: REA 30 labor_equipment_quantity_rules.json · REA 27 conversion_component_rate_rules.json — 손셈 대조.
수량 계산기 반올림 = MROUNDUD 선택 1(사사오입 · REA 32 §1.1) — 경계 값은 test_stmate_xrate_round.
"""
import json
import subprocess
from pathlib import Path
HELPER = Path(__file__).with_name("helper_stmate_smt_tools.cjs")
def call(*calls):
payload = [{"fn": f, "args": list(a)} for f, *a in calls]
out = subprocess.check_output(
["node", str(HELPER)], input=json.dumps(payload), text=True, encoding="utf-8"
)
return json.loads(out)
def test_xrate_daily_and_applied_quantity():
lab, eq, zero = call(
(
"compute",
{"qty1": 120, "cond": "0.9*1.1", "hrat": 80, "lqty": 3, "labor": True, "point": 4},
),
(
"compute",
{"qty1": 120, "cond": "", "hrat": 100, "lqty": 1, "labor": False, "auto": True},
),
("compute", {"qty1": 0, "cond": "", "hrat": 100, "lqty": 2, "labor": True, "point": 3}),
)
# 120 × 0.99 × 80% = 95.04 · 노무 3 × 1 ÷ 95.04 = 0.031565… → 4 자리 0.0316
assert lab["qday"] == 95.04 and lab["cqty"] == 0.0316 and abs(lab["cond"] - 0.99) < 1e-12
# 장비 1 × 8 ÷ 120 = 0.0666… · 자동 자리 = 「120」 3자리 + 1 = 4 → 0.0667
assert eq["point"] == 4 and eq["cqty"] == 0.0667
assert zero["qday"] == 0 and zero["cqty"] == 0
def test_xrate_auto_point_and_condition_chars():
p1, p2, p3, bad, paren, power = call(
("autoPoint", 5.5),
("autoPoint", 123456.0),
("autoPoint", 1234),
("condFactor", "0.9 * a"),
("condFactor", "(1+2)"),
("condFactor", "2^3/4"),
)
assert (p1, p2, p3) == (2, 6, 5)
assert not bad["ok"] and not paren["ok"] and bad["value"] == 1
assert power == {"ok": True, "value": 2}
def test_a2w_editor_row_and_bulk():
e1, e2, e3, r1, r2 = call(
("normalizeEditor", 70, 50, "L"),
("normalizeEditor", 70, 50, "M"),
("normalizeEditor", 130, 0, "L"),
("normalizeRow", 70, 50, 10),
("normalizeRow", 70, 50, 9),
)
assert e1 == {"L": 70, "M": 30, "E": 0} and e2 == {"L": 50, "M": 50, "E": 0}
assert e3 == {"L": 100, "M": 0, "E": 0}
assert r1 == {"L": 50, "M": 50, "E": 0} and r2 == {"L": 70, "M": 30, "E": 0}
rows = [{"sel": True, "rateL": 0}, {"sel": True, "rateL": 80}, {"sel": False, "L": 5}]
plain, indiv = call(
("bulkApply", rows, {"L": 40, "M": 30}, {}),
("bulkApply", rows, {"L": 40, "M": 30}, {"individual": True}),
)
assert [(r["L"], r["M"], r["E"]) for r in plain] == [(40, 30, 30), (40, 30, 30), (0, 0, 0)]
assert [(r["L"], r["M"], r["E"]) for r in indiv] == [(40, 30, 30), (80, 20, 0), (0, 0, 0)]