- ST_Prt 0x44fc34 서식 번호 → 출력 함수 표 · 0609 = Prn_Won_A · 0608 = Prn_Wonga - IFORM = VTS·XLS 파일 바이트 · .XLS·.VTS · 16MB 거절 · ReadDefName 「이름=값」 · QTYn 칸 - 사·관급내역서 관급 구역 품목 줄 누락 수정 - 장부 F3-23 · F3-26 · F5-22 구현 · 지식DB 35번 · stmate 시험 732건 통과 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
145 lines
7.1 KiB
Python
145 lines
7.1 KiB
Python
"""STMATE 재현 앱 보고서 양식 변형(G5 · 견본 없는 서식) — REA 35 인쇄 분기·인쇄 양식(DFM)에서 나온 규칙 대조.
|
|
|
|
원본 출력 견본(엑셀)이 실무 폴더에 없는 서식만 — 정답 = 규칙(서식 → Prn_* 함수 · DFM 머리 칸 차례 ·
|
|
Prn_Won_A 구성비 = 금액 ÷ 총공사비 × 100) 과 같은 STC 로 만든 원본 대조 시트(공사원가계산서 0608 · 설계내역서 0603).
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import warnings
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT / "tools/stmate-test"))
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
from test_stmate_report_coll import _forms # noqa: E402
|
|
from test_stmate_report_sheets import book # noqa: E402
|
|
|
|
warnings.filterwarnings("ignore")
|
|
NAMES = ("봉화2024", "영월2024", "대흥2024")
|
|
|
|
|
|
def _node(js):
|
|
return json.loads(subprocess.check_output(["node", "-e", js], cwd=ROOT, encoding="utf-8"))
|
|
|
|
|
|
def test_print_dispatch_table_follows_st_prt_switch():
|
|
"""ST_Prt 0x44fc34 분기(20000 + 서식 번호) — 0609 = Prn_Won_A · 0608 = Prn_Wonga · 0672 = 0601 과 같은 Prn_Jab ·
|
|
0606 엑셀판 = 종양식 Prn_Nae_P_EXL · 0695 엑셀판 없음 · 0675~0677 = Prn_Nae_Col{M,L,X}."""
|
|
p = _node(
|
|
"const F=require('./tools/stmate-test/report_forms.js');"
|
|
"process.stdout.write(JSON.stringify({p:F.FORM_PROC,s:F.FORM_SHEET}))"
|
|
)
|
|
proc, sheet = p["p"], p["s"]
|
|
assert proc["0609"][0] == "Prn_Won_A" and proc["0608"][0] == "Prn_Wonga"
|
|
assert proc["0672"][0] == "Prn_Jab" and proc["0606"][1] == "Prn_Nae_P_EXL"
|
|
assert proc["0695"][0] == proc["0695"][1] == "Prn_Boq_Not"
|
|
assert [proc[k][0] for k in ("0675", "0676", "0677")] == [
|
|
"Prn_Nae_ColM",
|
|
"Prn_Nae_ColL",
|
|
"Prn_Nae_ColX",
|
|
]
|
|
assert set(sheet) <= set(proc)
|
|
|
|
|
|
@pytest.mark.parametrize("name", NAMES)
|
|
def test_total_summary_0609_is_cost_statement_rows_with_share(name):
|
|
"""0609 총괄집계표 = 공사원가계산서와 같은 줄 · 금액 · 구성비(T_PER 0.001% 표시) · 비고 — 원가계산서는 원본 엑셀 일치 시트."""
|
|
f = _forms(name, {})
|
|
won = book(name)["공사원가계산서"]["rows"]
|
|
want = [r for r in won[4:] if r.get("D") is not None]
|
|
got = [r for r in f["총괄집계표"]["rows"] if r.get("_k") == "cost"]
|
|
assert len(got) == len(want) > 10
|
|
grand = f["_grand"]
|
|
for w, g in zip(want, got):
|
|
label = [x for x in (w.get("A"), w.get("B"), w.get("C")) if x and str(x).strip()][-1]
|
|
assert g["A"] == str(label).strip() and g["B"] == w["D"] and g["D"] == (w.get("F") or "")
|
|
share = round(w["D"] / grand * 100, 3) if w["D"] else ""
|
|
assert g["C"] == share or abs(g["C"] - share) < 1e-9
|
|
assert got[-1]["C"] == 100
|
|
head = f["총괄집계표"]["rows"][2]
|
|
assert [head[c] for c in "ABCD"] == ["구 분", "금 액", "구 성 비", "비 고"]
|
|
|
|
|
|
@pytest.mark.parametrize("name", NAMES)
|
|
def test_split_0605_columns_and_full_rate(name):
|
|
"""0605 분할내역서(TPM_NAE_B) — 수량(전체·금회) · 단위 · 단가 · 금액(전체·금회) · 비고 · 100% 이면 금회 = 전체."""
|
|
f = _forms(name, {"splitRate": 100})
|
|
rows = f["분할내역서"]["rows"]
|
|
assert rows[2]["D"] == "수 량" and rows[2]["H"] == "금 액" and rows[2]["J"] == "비고"
|
|
assert rows[3]["D"] == rows[3]["H"] == "전 체" and rows[3]["E"] == rows[3]["I"] == "금 회"
|
|
for r in rows[4:]:
|
|
if r.get("_k") == "item":
|
|
assert r["D"] == r["E"] and r["H"] == r["I"] and r["J"] == r["_code"]
|
|
elif str(r.get("_k", "")).startswith("T") or r.get("_k") == "final":
|
|
assert r["H"] == r["I"], r
|
|
half = _forms(name, {"splitRate": 50})["분할내역서"]["rows"]
|
|
items = [r for r in half if r.get("_k") == "item" and r["F"] != "%"]
|
|
assert all(r["I"] == int(r["G"] * r["E"] + 1e-9) for r in items)
|
|
|
|
|
|
@pytest.mark.parametrize("name", NAMES)
|
|
def test_cost_statement_simple_0695_rows(name):
|
|
"""0695 간이공사비 산출명세서(TPM_BOQ_NOT) — 회사분·도급분·계 × 8칸 · 도급분 = 원가계산서 소계·관리비·이윤·총원가·부가세·도급공사비."""
|
|
f = _forms(name, {})
|
|
rows = [r for r in f["간이명세서"]["rows"] if r.get("_k") == "cost"]
|
|
assert [r["A"] for r in rows] == ["회사분", "도급분", "계"]
|
|
dog = rows[1]
|
|
assert dog["B"] + dog["C"] + dog["D"] + dog["E"] + dog["F"] == dog["G"]
|
|
assert all(rows[2][c] == rows[0][c] + rows[1][c] for c in "BCDEFGHI")
|
|
assert rows[2]["I"] == f["_grand"]
|
|
|
|
|
|
@pytest.mark.parametrize("name", NAMES)
|
|
def test_group_collection_and_plan_forms(name):
|
|
"""0675~0677 집계표(TPM_NAE_COL_ML · TPM_NAE_COL) · 0671·0678·0679 투입계획(Plan_K 공종별 · Plan_A 코드 합)."""
|
|
f = _forms(name, {})
|
|
mat = f["재료비집계표"]["rows"]
|
|
assert [mat[2][c] for c in "ABCEFGHI"][:3] == ["공 종", "작 업 명", "구 분"]
|
|
eq = f["중장비집계표"]["rows"]
|
|
assert eq[2]["G"] == "재 료 비" and eq[2]["M"] == "총 액" and eq[3]["N"] == "금 액"
|
|
for r in eq:
|
|
if r.get("_k") == "item":
|
|
assert r["M"] == r["G"] + r["I"] + r["K"] and r["O"] == r["_code"]
|
|
k = [r for r in f["인력투입계획"]["rows"] if r.get("_k") == "item"]
|
|
a = [
|
|
r for r in _forms(name, {"planAll": True})["인력투입계획"]["rows"] if r.get("_k") == "item"
|
|
]
|
|
by = {}
|
|
for r in k:
|
|
by[r["F"]] = by.get(r["F"], 0) + r["D"]
|
|
assert a and {r["E"]: r["C"] for r in a}.keys() == by.keys()
|
|
assert all(abs(r["C"] - round(by[r["E"]], 2)) < 0.011 * max(1, len(k)) for r in a)
|
|
|
|
|
|
@pytest.mark.parametrize("name", NAMES)
|
|
def test_hand_form_0303_head_rows_and_lines(name):
|
|
"""0303 수공양식(TPM_ILWI_S) — 호표마다 항목번호·공종·단위 세 줄 · 구성 줄에 총액 칸 없음(K = 비고 코드)."""
|
|
f = _forms(name, {})
|
|
rows = f["일위대가표수공양식"]["rows"]
|
|
assert rows[2]["K"] == "비고" and "총" not in "".join(str(v) for v in rows[2].values())
|
|
kinds = [r.get("_k") for r in rows[4:]]
|
|
for i, k in enumerate(kinds):
|
|
if k == "hopyo":
|
|
assert kinds[i + 1] == kinds[i + 2] == "parent"
|
|
lines = [r for r in rows if r.get("_k") == "line"]
|
|
assert lines and all(str(r["K"]).strip() for r in lines)
|
|
|
|
|
|
@pytest.mark.parametrize("name", NAMES)
|
|
def test_gov_and_estimate_forms(name):
|
|
"""0607 사·관급내역서(TPM_BQM) — 「사 급 분」 띠 · 관급 구역 줄 · 계 = 원가 관급 원자재대 / 0615(TPM_NAE_EST) 견적 W 줄만."""
|
|
f = _forms(name, {})
|
|
gov = f["관급내역서"]["rows"]
|
|
assert any(r.get("_k") == "band" for r in gov)
|
|
fin = [r for r in gov if r.get("_k") == "final"][0]
|
|
items = [r for r in gov if r.get("_k") == "item"]
|
|
tot = sum(v for r in items for c, v in r.items() if c in "GIKM" and isinstance(v, (int, float)))
|
|
assert sum(v for c, v in fin.items() if c in "GIKM") == tot
|
|
est = f["견적단가대비표"]["rows"]
|
|
assert est[3]["E"] == "견적단가" and est[3]["F"] == "기준단가"
|
|
assert all(r["_code"][0] == "W" for r in est if r.get("_k") == "item")
|