Files
Aislo/resources/tester/test_stmate_kanjub_rate.py
T
eomsangdonandClaude Opus 5.5 3d8fddfc34 feat(stmate-test): 보고서·제잡비·식언어·수량산출·화면 묶음 합침 · 재계산 후 저장 무변경
- 보고서: 원본 STMATE 엑셀과 셀 대조(목록표·자재단가대비·중기·일위대가·산근 목록·설계내역서·원가계산서) · 조달청 XML 공식 XSD 검증 · PM_Prt 화면
- 제잡비: 원본 계산 순서 4형식(T/S/TA/SA) · 실무 7건 저장 결과키 전수 1원 · 요율표 조회 · 선택지 계산 · 제잡비·제비율 화면
- 식언어·수량: 함수·조건식·금액변수·문법 검사·수량변수·QCODE · 산근·구성·중기공식 편집 화면 · 순환 알림
- 화면: 원본 메뉴 체계·단축키 · 설계내역서 편집(행 넣기·지우기·이동·레벨·보관함) · 초기값 · 단가목록표
- 저장: 초기값(Start8x.Ini) 바뀐 키 저장 · 노임 환산 직종 = 초기값 CONLD · 산근·구성 결과에 적용단가 칸 채우지 않음(원본 0)
- DBF 글 칸 앞 빈칸 보존 · 시험 도우미 표준입력 utf8(한글 조각 깨짐 원인)
- 실무 5건 재계산 후 저장 = 바뀐 칸 0 · 시험 332건 통과

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 19:12:04 +09:00

109 lines
4.4 KiB
Python

"""STMATE 재현 앱 요율 조회 — 제비율표(RATE_R · JB_R) 좌표 규칙으로 실무 7건 저장 요율(n_R_* · n_C_ANJUN_G)을 되찾음.
표 = 그 STC 안 RATE_R 또는 저장 시각(PRJLST.SMT 압축 시각) 이전에 나온 기준 JB_R(설치 폴더에 있으면) 중 가장 잘 맞는 것.
사용자가 표를 거치지 않고 고친 요율은 지식DB 15번 §4 대로 수동값 — 아래 MANUAL 에 까닭과 함께 적음(그 밖 불일치는 실패).
"""
import json
import re
import subprocess
import sys
import zipfile
from datetime import datetime
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
from smt_parser import parse_dbf_bytes # noqa: E402
from test_stmate_kanjub_stc import DOCS, STCS # noqa: E402
HELPER = Path(__file__).with_name("helper_stmate_kanjub_rate.cjs")
JB_DIR = Path("C:/경영정보(주)/System/ST_BSC")
# 조회 항목 → 저장키
ITEMS = {
"KANNO": "R_KANNO",
"KITA": "R_T_KITA",
"ILBAN": "R_ILBAN",
"IYUN": "R_IYUN",
"SANBO": "R_SANBO",
"KUNBO": "R_KUNBO",
"YUNBO": "R_YUNBO",
"SLVBO": "R_SLVBO",
"KOBO": "R_KOBO",
"ANJUN": "R_ANJUN",
"ANJUN1": "R_ANJUN1",
"ANJUN2": "R_ANJUN2",
"HWAN": "R_HWAN",
"EQIP": "R_EQIPSU",
}
# 표를 거치지 않은 수동 요율(15번 §4 · 이번 대조)
MANUAL = {
# 공사기간 0(6개월 이하)인데 7~12개월 칸 값 · 고용 5등급인데 7등급 값 · 환경 조경(13)인데 기타토목 값 — 다른 공사 설정 복사
"대흥2024": {"KANNO", "KITA", "KOBO", "HWAN"},
# RATE_R 10행에 사용자가 손으로 넣은 16.5/5.2(같은 행 나머지 칸 0) — 공사기간 칸과 어긋남
"울진2025": {"KANNO", "KITA"},
"소광원설계": {"KANNO", "KITA"},
# 위와 같음 + 이윤 13.4355%(표에 없음 · 15번 §4)
"소광변경1차": {"KANNO", "KITA", "IYUN"},
# 이윤 13.887%(표에 없음) · 건설기계 구분 5 인데 일반공사 0.4%
"영덕변경": {"IYUN", "EQIP"},
}
def _jb_tables(before: datetime) -> list[tuple[str, list]]:
out = []
for p in sorted(JB_DIR.glob("*.JB_R")) if JB_DIR.exists() else []:
m = re.match(r"(\d{4})\((?:상|하)_(\d{2})(\d{2})", p.name)
if m and datetime(int(m[1]), int(m[2]), int(m[3])) <= before:
out.append((p.name, parse_dbf_bytes(p.read_bytes())[1]))
return out
def _cases(name: str):
src = DOCS / STCS[name]
loaded = server.api_load(server.LoadReq(source_path=str(src)))
kv, prj = loaded["prjlst_smt"], loaded["prjlst"]
with zipfile.ZipFile(src) as z:
saved = datetime(
*next(i.date_time for i in z.infolist() if i.filename.upper().endswith("PRJLST.SMT"))
)
tables = [("STC RATE_R", loaded["rate_tables"]["RATE_R.DBF"])] + _jb_tables(saved)
for n in sorted({k[0] for k in kv if k[1:] == "_R_KANNO"}):
g = lambda k: float(kv.get(f"{n}_{k}") or 0) # noqa: E731
opts = {
"kongType": g("I_KONG_TYPE"),
"kikan": g("I_KONG_KIKAN"),
"scale": float(prj[f"TCOST{n}"]),
"anjunType": g("I_ANJUN_TYPE"),
"anjunScale": float(prj[f"MCOST{n}"]) + float(prj[f"LCOST{n}"]) + g("C_KN_SA"),
"koboType": g("I_KOBO_TYPE"),
"hwan": g("I_HWAN"),
"eqip": g("I_EQIP"),
"tbugum": g("I_TBUGUM"),
}
want = {c: g(k) for c, k in ITEMS.items()} | {"C_ANJUN_G": g("C_ANJUN_G")}
yield n, opts, want, tables
@pytest.mark.parametrize("name", list(STCS))
def test_rates_come_from_table(name):
for n, opts, want, tables in _cases(name):
req = [{"matrix": rows, "opts": opts} for _, rows in tables]
res = json.loads(
subprocess.check_output(
["node", str(HELPER)], input=json.dumps(req), text=True, encoding="utf-8"
)
)
scored = []
for (tname, _), r in zip(tables, res):
got = r["rates"] | r["amounts"]
bad = {c for c, v in want.items() if abs(float(got.get(c, 0)) - v) > 1e-6}
scored.append((len(bad), tname, bad))
scored.sort(key=lambda x: x[0])
best = scored[0]
# 불일치 = 정확히 수동 목록(더 많으면 규칙 오류 · 적으면 MANUAL 이 버그를 숨김)
assert best[2] == MANUAL.get(name, set()), f"{name} {n}차수 표 {best[1]} 불일치 {best[2]}"