# -*- coding: utf-8 -*- """분리형 변경 STC 만들기 · 차수 인계 — 앞 파일의 현재안이 새 파일 BOQ11 차수1, 차수2 = 이번 변경 출발점. 근거: 16_골든셋_회귀_결과.md §4.5(한 파일에 BOQ11·BOQ12 → BOQ12 가 현재안) · §4.6(분리형: 새 파일 BOQ11 차수1 = 앞 파일 BOQ12 · 차수2 = 이번 변경분) · 08번 §2(통합형 / 분리형) · stc_project(해당분만 압축 · 차수 칸 복사) 규칙: - 현재안 = 내역 표가 있는 PRJLST 행 중 PCODE 가 가장 큰 행(지정 가능) · 그 행 K_ORD k → 현재 차수 s = k+1 - 새 파일 = 그 공사만 담은 STC(BOQ11 · PRJLST 한 행 · 그 메모) · 내역 s 차 칸 → 1차 칸(s ≠ 1 일 때) → 2차 칸 복사 - PRJLST ?COSTs·COSTWs → 1차 칸 · 1차 → 2차 칸 · K_ORD = 1 · 메모 [s_KANJUB] → [1_KANJUB] · [1_KANJUB] → [2_KANJUB] """ from __future__ import annotations import re from dbf_memo import BLANK_PTR, SixMemo, ini_items, memo_text_bytes from dbf_rw import DbfTable from stc_project import SLOT_COL, _copy_slot, _rename_section, pack_selected PRJ_SLOT = ("MCOST", "LCOST", "SCOST", "TCOST", "ACOST", "COSTW") def current_pcode(files: dict) -> str: """현재안 공사 — BOQ 표가 있는 PRJLST 행 중 PCODE 최대(통합형 BOQ12 > BOQ11).""" prj = DbfTable(files["PRJLST.DBF"]) codes = [ prj.get(i, "PCODE") for i in range(len(prj)) if f"BOQ{prj.get(i, 'PCODE')}.DBF" in files ] if not codes: raise ValueError("내역 표가 있는 공사 없음") return max(codes, key=lambda c: int(c) if c.isdigit() else 0) def _move_section(text: str, src: int, dst: int) -> str: """[src_KANJUB] 섹션을 [dst_KANJUB] 로 이름·키 접두를 바꿔 옮김(dst 가 이미 있으면 그대로).""" if src == dst: return text secs = {s for s, _k, _v in ini_items(text)} if f"{src}_KANJUB" not in secs or f"{dst}_KANJUB" in secs: return text out, cur = [], "" for ln in text.split("\r\n"): s = ln.strip() if s.startswith("[") and s.endswith("]"): cur = s[1:-1] if cur == f"{src}_KANJUB": ln = f"[{dst}_KANJUB]" elif cur == f"{src}_KANJUB": ln = re.sub(rf"^{src}_", f"{dst}_", ln) out.append(ln) return "\r\n".join(out) def split_change(files: dict, pcode: str = "") -> tuple[dict, dict]: """앞 파일 files → 다음 변경 차수 파일 files · 보고.""" pc = pcode or current_pcode(files) prj0 = DbfTable(files["PRJLST.DBF"]) si = next(i for i in range(len(prj0)) if prj0.get(i, "PCODE") == pc) k = int(float(prj0.get(si, "K_ORD") or 0)) if "K_ORD" in prj0.fields else 0 s = k + 1 out = pack_selected(files, pc) bt = DbfTable(out["BOQ11.DBF"]) if s != 1: _copy_slot(bt, str(s), "1") _copy_slot(bt, "1", "2") bt.dirty = True out["BOQ11.DBF"] = bt.to_bytes() prj = DbfTable(out["PRJLST.DBF"]) for c in PRJ_SLOT: a, one, two = f"{c}{s}", f"{c}1", f"{c}2" if a in prj.fields and one in prj.fields: prj.set_raw(0, one, prj0.raw(si, a)) if one in prj.fields and two in prj.fields: prj.set_raw(0, two, prj.raw(0, one)) if "K_ORD" in prj.fields: prj.set(0, "K_ORD", 1) memo = SixMemo(out.get("PRJLST.SMT")) text = memo.read(prj.raw(0, "PRJ_MEMO")).decode("cp949", "replace") text = _rename_section(_move_section(text, s, 1), 1, 2) fresh = SixMemo(None, memo.bs) prj.set_raw(0, "PRJ_MEMO", fresh.write(BLANK_PTR, memo_text_bytes(text)) if text else BLANK_PTR) out["PRJLST.DBF"] = prj.to_bytes() out["PRJLST.SMT"] = fresh.to_bytes() slot_cols = sorted({SLOT_COL.match(c).group(1) for c in bt.order if SLOT_COL.match(c)}) return out, { "from_pcode": pc, "from_slot": s, "k_ord": 1, "rows": len(bt), "slot_cols": slot_cols, }