- smt_eval.js 새 산근 평가기 — 봉화에만 맞던 옛 해석기(161/242) 대체 - 총액우선 차액 보정(Start8x.Ini CHGNM=0 · OPTNM 순서 · 소수부 십진 비교) · 비목우선은 성분 절사 - 다른 산근 참조 = 재·노·경 나눠 가져옴 · 위상순서 재계산 · 변수 대소문자 무시 - % 단위 자원(인건비의 % · 운반비 계) = 누계 밑수 · (-=)(-==) 합계 제외 · RS_ 직전 구간 · 라벨 묶음 PA010203 - 운반비 경비 귀속 = COSTN SBMOK=S — 원본 저장 금액으로 판정하던 부분 걷음 - 낙찰률 단가 = 2칸 원단가 × NAGCD 원 미만 버림(7건 127개) - 지킴 시험 test_stmate_smt_eval.py · 장부 진행률 30.6% → 32.2% Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
3.2 KiB
Python
71 lines
3.2 KiB
Python
"""STMATE 재현 앱 단가산출근거(D코드) 재계산 — COSTN.SMT 식을 다시 셈해 실무 STC 저장 단가와 1원 대조.
|
|
|
|
정답 = STC 의 COSTN 저장 단가(낙찰률 단가는 2칸 원단가). 참조 단가(X·L·M·S)는 저장값을 씀.
|
|
변경설계 파일(영덕 변경 · 소광 변경1차)의 산근은 설계변경 기능 몫 — 낙찰률 규칙만 대조.
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT / "tools/stmate-test"))
|
|
import server # noqa: E402
|
|
|
|
HELPER = Path(__file__).with_name("helper_stmate_smt.cjs")
|
|
DOCS = ROOT / "resources/knowledge/original/실무문서"
|
|
ORIGINAL = {
|
|
"봉화2024": "04.산불 봉화/01. 내역서/13-2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km.STC",
|
|
"울진2025": "25년 산불진화임도(기번8)(울진소광)/2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외).STC",
|
|
"영월2024": "05. 동부청 영월/내역서/2024년 임도시설사업2차(산불진화_기번6)-영월.STC",
|
|
"대흥2024": "2024년 간선임도(기번3-울진.대흥)(공통)/1. 설계원본/01 내역자료/2024년 간선임도사업(기번3).STC",
|
|
"소광원설계": "25년 산불진화임도(기번8)(울진소광)/1. 설계원본/03-내역/22-2025년 산불진화임도사업(울진 금강송 소광).STC",
|
|
}
|
|
CHANGED = {
|
|
"영덕변경": "20250527 2025년 계류보전사업(기번1-영덕.병곡.영리.산214-1)(변경)/01.내역자료/2025년 계류보전사업(기번1 영덕.병곡.영.산214)(변경).STC",
|
|
"소광변경1차": "25년 산불진화임도(기번8)(울진소광)/변경 1차분-2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외).STC",
|
|
}
|
|
|
|
|
|
def run(rel):
|
|
loaded = server.api_load(server.LoadReq(source_path=str(DOCS / rel)))
|
|
payload = {
|
|
"costn_rows": loaded["costn_rows"],
|
|
"memos": {k: v["text"] for k, v in loaded["costn_memos"].items()},
|
|
"start_ini": loaded["start_ini"],
|
|
}
|
|
out = subprocess.check_output(
|
|
["node", str(HELPER)],
|
|
input=json.dumps(payload, ensure_ascii=False),
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
return loaded, json.loads(out)
|
|
|
|
|
|
@pytest.mark.parametrize("name", list(ORIGINAL))
|
|
def test_d_codes_match_stored_unit_prices(name):
|
|
loaded, out = run(ORIGINAL[name])
|
|
rows = {r["CODE"].strip(): r for r in loaded["costn_rows"]}
|
|
assert out["smt"], f"{name} 산근 없음"
|
|
diffs = {}
|
|
for code, got in out["smt"].items():
|
|
r = rows[code]
|
|
slot = 2 if float(r.get("NAGCD") or 0) > 0 else 1
|
|
exp = [float(r[f"{k}AMT{slot}"]) for k in "MLS"]
|
|
if any(abs(g - e) >= 0.5 for g, e in zip(got, exp)):
|
|
diffs[code] = (got, exp)
|
|
assert not diffs, f"{name} 산근 불일치 {len(diffs)}/{len(out['smt'])} {list(diffs.items())[:5]}"
|
|
|
|
|
|
@pytest.mark.parametrize("name", list(ORIGINAL) + list(CHANGED))
|
|
def test_bid_rate_unit_prices(name):
|
|
loaded, out = run({**ORIGINAL, **CHANGED}[name])
|
|
rows = {r["CODE"].strip(): r for r in loaded["costn_rows"]}
|
|
for code, m, l, s in out["bid"]:
|
|
exp = [float(rows[code][f"{k}AMT1"]) for k in "MLS"]
|
|
assert [m, l, s] == exp, (name, code, [m, l, s], exp)
|