refactor(stmate-test): 원본에 없는 「자재 기준자료 엑셀 내보내기·불러오기」 걷음
- 원본 wM_JajeXLS = 재료비 수량집계표 엑셀 내보내기 한 방향뿐(REA 37 · TWM_JAJEXLS 0x5658bc) — 추측으로 만든 자재 기준자료 왕복 메뉴·API·시험 삭제 - 남김: 「재료비 수량집계표를 엑셀파일로 만들기」(conv_jaje · scr_jaje) - 낡은 시험 test_jaje_xlsx_roundtrip_and_edit · conv_api 의 jaje 왕복 삭제 — 원본에 없는 기능 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@ def test_boq_xlsx_download_reimports(work):
|
||||
assert Source(r["saved_stc"]).files["PRJLST.DBF"] == Source(str(STC)).files["PRJLST.DBF"]
|
||||
|
||||
|
||||
def test_code_and_split_and_jaje_api(work):
|
||||
def test_code_and_split_api(work):
|
||||
r = A.api_code_change(
|
||||
A.CodeChgReq(source_path=str(STC), mapping={"M00001": "M99991"}, target_folder_name="cc")
|
||||
)
|
||||
@@ -68,10 +68,5 @@ def test_code_and_split_and_jaje_api(work):
|
||||
assert p.get(0, "K_ORD") == "1" and p.get(0, "TCOST2") == p.get(0, "TCOST1") == _prj(
|
||||
str(STC)
|
||||
).get(0, "TCOST1")
|
||||
x = A.api_jaje_xlsx(A.SrcReq(source_path=str(STC))).body
|
||||
r3 = A.api_jaje_import(
|
||||
A.JajeImportReq(source_path=str(STC), xlsx=A.FileIn(b64=base64.b64encode(x).decode()))
|
||||
)
|
||||
assert r3["count"] == 0
|
||||
with pytest.raises(Exception):
|
||||
A.api_jb_read(A.JbReq(path=str(STC))) # 설치 폴더 JB_* 만
|
||||
|
||||
@@ -19,15 +19,12 @@ from conv_xls_apply import import_boq, register # noqa: E402
|
||||
from conv_xls_boq import boq_from_xlsx, fill_from_costn, load_book, read_initial # noqa: E402
|
||||
from conv_xls_out import ( # noqa: E402
|
||||
export_boq_xlsx,
|
||||
export_jaje_xlsx,
|
||||
import_jaje_xlsx,
|
||||
)
|
||||
from conv_xls_res import comp_from_san, comp_from_sheets, costn_from_lists, memos_from_sheet # noqa: E402
|
||||
from dbf_rw import DbfTable # noqa: E402
|
||||
from smt_parser import extract_costn_memos, parse_dbf_bytes # noqa: E402
|
||||
from stc_project import write_output # noqa: E402
|
||||
from stc_read import Source # noqa: E402
|
||||
from stc_save import apply_save # noqa: E402
|
||||
|
||||
DOCS = ROOT / "resources/knowledge/original/실무문서"
|
||||
SO = (
|
||||
@@ -196,26 +193,6 @@ def test_own_boq_xlsx_roundtrip(stc):
|
||||
assert not bad, f"{tb} {bad[:5]}"
|
||||
|
||||
|
||||
def test_jaje_xlsx_roundtrip_and_edit(tmp_path):
|
||||
import openpyxl
|
||||
|
||||
files, _xl, _tb = _pair("봉화")
|
||||
x = export_jaje_xlsx(files)
|
||||
assert import_jaje_xlsx(files, x) == []
|
||||
p = tmp_path / "j.xlsx"
|
||||
p.write_bytes(x)
|
||||
wb = openpyxl.load_workbook(p)
|
||||
ws = wb.worksheets[0]
|
||||
code = ws.cell(2, 1).value
|
||||
old = float(ws.cell(2, 10).value or 0)
|
||||
ws.cell(2, 10).value = old + 100
|
||||
wb.save(p)
|
||||
ups = import_jaje_xlsx(files, p.read_bytes())
|
||||
assert ups == [{"CODE": code, "AMT_61": old + 100, "MAMT1": old + 100, "TAMT1": old + 100}]
|
||||
out, log = apply_save(files, {"costn_updates": ups})
|
||||
assert {x["field"] for x in log if x["table"] == "COSTN.DBF"} <= {"AMT_61", "MAMT1", "TAMT1"}
|
||||
|
||||
|
||||
# 영월 BDQTY 는 복호 칸이 어긋남(UNIT1 깨짐 · TPRPT 전부 4) — 비교 제외
|
||||
@pytest.mark.parametrize("name", ["봉화", "대흥", "울진", "영덕변경"])
|
||||
def test_comp_rows_follow_xls_2_bdrec(name):
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
- [x] F4-24 엑셀 → 내역 불러오기 — wM_From_XLS · 현재: 구현(conv_xls_boq · 원본 엑셀 5권 → STC 내역 행 전 칸 일치(test_stmate_conv_xls))
|
||||
- [x] F4-25 엑셀 역등록(CONV_XLS_2_ST·INITIAL)·엑셀 → BDQTY — 24번 §3 · 현재: 구현(REA 37 — ST_P_XLS 0x5f1078 PRPT「속성」·PRIN「대상」·PROUT「저장」 = PR/RR 줄만 · 원본 BDQTY 대조 4건 일치)
|
||||
- [x] F4-30 BDQTY.DBF 구성표 복호 읽기 — STC_난독화_상태 · 현재: 구현(bdqty.py 자리별 회전·더하기 표 283자리 · 사용자 승인 · 글자 칸 제외)
|
||||
- [x] F4-26 자재 기준자료 XLS 입출력 — wM_JajeXLS · 현재: 구현(REA 37 — 원본 = 재료비 수량집계표 엑셀 내보내기만(TWM_JAJEXLS) · 주요건설자재현황 시트 · 웹 들여오기는 원본에 없음 · 파일 이름 기본값 미확인)
|
||||
- [x] F4-26 자재 기준자료 XLS 입출력 — wM_JajeXLS · 현재: 구현(REA 37 — 원본 = 재료비 수량집계표 엑셀 내보내기만(TWM_JAJEXLS) · 주요건설자재현황 시트(conv_jaje·scr_jaje) · 원본에 없는 「자재 기준자료 엑셀 내보내기·불러오기」 걷음 · 파일 이름 기본값 미확인)
|
||||
- [x] F4-27 기준 BOQ·코드 변경 엑셀 왕복 — 24번 §3 · ST_U_ChgCode · 현재: 구현(stc_codechg 내역 8표 엑셀 왕복 · 코드 변경 되돌리면 7건 원래 바이트)
|
||||
- [x] F4-28 구버전 → Win8 자료 변환 — ST_U_Convert ST_ConvToWin8 · 현재: 구현(REA 37 — 판 가리기(Chk_Data 0x459280) · 7.x/DOS COSTN·BDQTY·PRJLST·KONGJONG·BOQ·Start8x.Ini 대응 · B→X 조건 = 단위 X · 합성 자료 시험 · 간접비 이관·RATE·SYSINFO·IFORM 미확인(틀 유지))
|
||||
- [x] F4-29 자동백업·자료 복원 — wM_D_Restore · wM_ST_Restore · 현재: 구현(api_file 자동 백업·복원)
|
||||
|
||||
@@ -21,9 +21,7 @@ from conv_xls_apply import import_boq, register
|
||||
from conv_xls_out import (
|
||||
export_boq_xlsx,
|
||||
export_code_xlsx,
|
||||
export_jaje_xlsx,
|
||||
import_code_xlsx,
|
||||
import_jaje_xlsx,
|
||||
)
|
||||
from conv_xml import xml_to_project
|
||||
from conv_xml_cfg import apply_settings, options, validate
|
||||
@@ -118,11 +116,6 @@ class SrcReq(BaseModel):
|
||||
table: str = ""
|
||||
|
||||
|
||||
class JajeImportReq(BaseModel):
|
||||
source_path: str
|
||||
xlsx: FileIn
|
||||
|
||||
|
||||
class CodeChgReq(BaseModel):
|
||||
source_path: str
|
||||
mapping: dict[str, str] = {}
|
||||
@@ -256,20 +249,6 @@ def api_boq_xlsx(req: SrcReq):
|
||||
return _xlsx(_guard(export_boq_xlsx, s.files, req.table), f"{s.path.stem}_기준BOQ.xlsx")
|
||||
|
||||
|
||||
@router.post("/api/conv/jaje-xlsx")
|
||||
def api_jaje_xlsx(req: SrcReq):
|
||||
s = _src(req.source_path)
|
||||
return _xlsx(_guard(export_jaje_xlsx, s.files), f"{s.path.stem}_자재기준자료.xlsx")
|
||||
|
||||
|
||||
@router.post("/api/conv/jaje-import")
|
||||
def api_jaje_import(req: JajeImportReq):
|
||||
"""자재 기준자료 엑셀 → costn_updates(바뀐 칸만) — 화면이 /api/save 로 넘김."""
|
||||
s = _src(req.source_path)
|
||||
ups = _guard(import_jaje_xlsx, s.files, _blob(**req.xlsx.model_dump()))
|
||||
return {"costn_updates": ups, "count": len(ups)}
|
||||
|
||||
|
||||
@router.post("/api/conv/code-xlsx")
|
||||
def api_code_xlsx(req: SrcReq):
|
||||
s = _src(req.source_path)
|
||||
|
||||
@@ -17,7 +17,7 @@ import io
|
||||
import re
|
||||
|
||||
from conv_xls_boq import COLS, INITIAL, LEVEL_NAME, _numv, load_book
|
||||
from smt_parser import parse_dbf_bytes, parse_ini_bytes
|
||||
from smt_parser import parse_dbf_bytes
|
||||
from stc_save import STRUCT_RE, pick_boq_table
|
||||
|
||||
BOQ_HEAD = [
|
||||
@@ -146,63 +146,6 @@ def export_boq_xlsx(files: dict, table: str = "") -> bytes:
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
# ── 자재 기준자료 ──
|
||||
JAJE_HEAD = ["코드", "명 칭", "규 격", "단위"]
|
||||
|
||||
|
||||
def export_jaje_xlsx(files: dict) -> bytes:
|
||||
"""COSTN M 코드 → 자재 기준자료 xlsx(출처 5칸 · 적용 단가)."""
|
||||
ini = parse_ini_bytes(files.get("START8X.INI") or files.get("STINI.MEM") or b"")
|
||||
src = [ini.get(f"JMOK{i}", f"출처{i}") for i in range(1, 6)]
|
||||
wb = _wb()
|
||||
ws = wb.active
|
||||
ws.title = "자재기준자료"
|
||||
ws.append(JAJE_HEAD + src + ["적용 단가", "BQCODE", "JDCODE"])
|
||||
for r in parse_dbf_bytes(files["COSTN.DBF"])[1]:
|
||||
if not r["CODE"].strip().startswith("M"):
|
||||
continue
|
||||
ws.append(
|
||||
[r["CODE"].strip(), r["NAME"], r["SIZE"], r["UNIT"].strip()]
|
||||
+ [_numv(r.get(f"AMT_{i}1")) for i in range(1, 7)]
|
||||
+ [r.get("BQCODE", "").strip(), r.get("JDCODE", "").strip()]
|
||||
)
|
||||
buf = io.BytesIO()
|
||||
wb.save(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def import_jaje_xlsx(files: dict, src) -> list[dict]:
|
||||
"""자재 기준자료 xlsx → 바뀐 칸만 담은 costn_updates(없는 코드는 새 행)."""
|
||||
ws = load_book(src).worksheets[0]
|
||||
cur = {r["CODE"].strip(): r for r in parse_dbf_bytes(files["COSTN.DBF"])[1]}
|
||||
head = [str(v or "").strip() for v in next(ws.iter_rows(max_row=1, values_only=True))]
|
||||
fields = (
|
||||
["CODE", "NAME", "SIZE", "UNIT"] + [f"AMT_{i}1" for i in range(1, 7)] + ["BQCODE", "JDCODE"]
|
||||
)
|
||||
out = []
|
||||
for r in ws.iter_rows(min_row=2, values_only=True):
|
||||
if not r or not r[0]:
|
||||
continue
|
||||
vals = dict(zip(fields, r[: len(fields)]))
|
||||
code = str(vals["CODE"]).strip()
|
||||
old = cur.get(code)
|
||||
upd = {"CODE": code}
|
||||
for k, v in vals.items():
|
||||
if k == "CODE" or k not in fields[: len(head)]:
|
||||
continue
|
||||
o = old.get(k) if old else None
|
||||
if k.startswith("AMT_"):
|
||||
if old is None or abs(_numv(o) - _numv(v)) > 1e-9:
|
||||
upd[k] = _numv(v)
|
||||
elif old is None or str(o or "").strip() != str(v or "").strip():
|
||||
upd[k] = "" if v is None else str(v)
|
||||
if "AMT_61" in upd: # 적용 단가 = 1차 재료비 단가
|
||||
upd["MAMT1"] = upd["TAMT1"] = upd["AMT_61"]
|
||||
if len(upd) > 1:
|
||||
out.append(upd)
|
||||
return out
|
||||
|
||||
|
||||
# ── 코드 변경표 ──
|
||||
def export_code_xlsx(files: dict) -> bytes:
|
||||
wb = _wb()
|
||||
@@ -234,8 +177,6 @@ def level_label(code: str) -> str:
|
||||
__all__ = [
|
||||
"INITIAL",
|
||||
"export_boq_xlsx",
|
||||
"export_jaje_xlsx",
|
||||
"import_jaje_xlsx",
|
||||
"export_code_xlsx",
|
||||
"import_code_xlsx",
|
||||
"level_label",
|
||||
|
||||
@@ -152,27 +152,6 @@
|
||||
`${pname(ctx)}_기준BOQ.xlsx`,
|
||||
);
|
||||
},
|
||||
async jajeXlsx(ctx) {
|
||||
download(
|
||||
await req("/api/conv/jaje-xlsx", { source_path: curPath(ctx) }, true),
|
||||
`${pname(ctx)}_자재기준자료.xlsx`,
|
||||
);
|
||||
},
|
||||
async jajeImport(ctx) {
|
||||
const f = await pickFile(".xlsx");
|
||||
const r = await req("/api/conv/jaje-import", {
|
||||
source_path: curPath(ctx),
|
||||
xlsx: { b64: f.b64 },
|
||||
});
|
||||
const rows = ctx.dbfData.costn_rows || (ctx.dbfData.costn_rows = []);
|
||||
for (const u of r.costn_updates) {
|
||||
const row = rows.find((x) => String(x.CODE).trim() === u.CODE);
|
||||
if (row) Object.assign(row, u);
|
||||
else rows.push(Object.assign({}, u));
|
||||
}
|
||||
app().cascadeAndRender && app().cascadeAndRender();
|
||||
alert(`[자재 기준자료 불러오기] 바뀐 자재 ${r.count}건 — [저장]으로 반영`);
|
||||
},
|
||||
async codeXlsx(ctx) {
|
||||
download(
|
||||
await req("/api/conv/code-xlsx", { source_path: curPath(ctx) }, true),
|
||||
@@ -236,8 +215,6 @@
|
||||
["Act_ConvXlsBoq", "엑셀 → 내역 불러오기 (wM_From_XLS)", "xlsBoq"],
|
||||
["Act_ConvXlsRegister", "엑셀 역등록 — 새 공사 (CONV_XLS_2_ST)", "xlsRegister"],
|
||||
["Act_ConvBoqXlsx", "기준 BOQ 엑셀로 내보내기 (MK_BSCBOQ_EXL)", "boqXlsx"],
|
||||
["Act_ConvJajeXlsx", "자재 기준자료 엑셀로 내보내기 (wM_JajeXLS)", "jajeXlsx"],
|
||||
["Act_ConvJajeImport", "자재 기준자료 엑셀 불러오기 (wM_JajeXLS)", "jajeImport"],
|
||||
["Act_ConvCodeXlsx", "코드 변경표 엑셀로 내보내기 (ST_U_ChgCode)", "codeXlsx"],
|
||||
["Act_ConvCodeChange", "코드 일괄 변경 (ST_U_ChgCode)", "codeChange"],
|
||||
["Act_ConvSplitChange", "분리형 변경 STC 만들기 — 차수 인계", "splitChange"],
|
||||
|
||||
Reference in New Issue
Block a user