feat(stmate-test): 3차 화면·데이터 마무리 — 보호표시 · 공종번호 · 직종·노임 · 제비율표 편집 · 차수 칸 저장 · 계약 변환등록 = 울진 계약 STC · 장부 87.6%
- 시험 615 통과 · 브라우저 화면 13개·메뉴 90개 오류 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""설계 → 계약자료 변환등록(wM_Conv_Mode · wM_Mk_Cont) 끼워 넣기 API.
|
||||
|
||||
근거: 27_계약_실행_기성_단계.md §2(계약 변환 옵션 · 적용제외) · 26 §4(NAGCD 낙찰율 열 · 계약단가) ·
|
||||
16 §4.5(통합형 BOQ11 설계 + BOQ12 계약) · ui_form_catalog wM_Conv_Mode(당초/변경 수량·단가 · 레벨)
|
||||
실증(울진 2025): 소광 원설계 STC → 계약 STC = PRJLST 새 행(PCODE 다음 번호 · CCODE = 설계 PCODE · PCLASS 4)
|
||||
+ BOQ1m = 설계 내역 복사 위 계약 공종(새 코드 · 비목 단가 버림) + COSTN 새 코드 행(산근 글·구성표 그대로)
|
||||
흐름: 화면(contract_calc.registerRows)이 계약 행을 만들고 서버는
|
||||
stc_project.copy_register(그대로 복사) → stc_save.apply_save(새 표 행 · 새 COSTN · 산근 · 구성표 · PRJLST) 만 함.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
|
||||
from bdqty import decode_bdqty
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from stc_project import copy_register, write_output
|
||||
from stc_read import Source
|
||||
from stc_save import apply_save
|
||||
|
||||
router = APIRouter()
|
||||
CONTRACT_PCLASS = 4 # 계약 공사(울진 2025 계약 행 · REA K_ORD 표 PCLASS 4)
|
||||
|
||||
|
||||
def _root() -> pathlib.Path:
|
||||
import server # 실행 중 바뀔 수 있는 작업 폴더(시험은 임시 폴더로 바꿈)
|
||||
|
||||
return server.WORK_DBF_ROOT
|
||||
|
||||
|
||||
class ContractRegReq(BaseModel):
|
||||
source_path: str
|
||||
src_pcode: str = "11"
|
||||
pname: str = ""
|
||||
pnum: str = ""
|
||||
boq_rows: list[dict] = [] # 계약 공종 행 — {_recno, BDCODE1, 단가·금액 칸}
|
||||
costn_rows: list[dict] = [] # 새 코드 COSTN 행
|
||||
costn_memos: dict[str, str] = {} # 새 산근 코드 → 산출근거 글
|
||||
bdqty_rows: list[dict] = [] # 새 대가 코드 구성행(decode_bdqty 꼴)
|
||||
costw1: float | None = None # 계약 총공사비(화면 계약 원가계산서)
|
||||
target_folder_name: str = ""
|
||||
|
||||
|
||||
def contract_register(files: dict, req: ContractRegReq) -> tuple[dict, str, list]:
|
||||
"""(새 files, 새 PCODE, 바뀐 칸 목록) — 쓰기 없음(시험용)."""
|
||||
out, pcode, log0 = copy_register(files, req.src_pcode, "copy", req.pname)
|
||||
prj_upd = {"CCODE": req.src_pcode, "PCLASS": CONTRACT_PCLASS}
|
||||
if req.pnum:
|
||||
prj_upd["PNUM"] = req.pnum
|
||||
if req.costw1 is not None:
|
||||
prj_upd["COSTW1"] = req.costw1
|
||||
save = {
|
||||
"boq_table": f"BOQ{pcode}.DBF",
|
||||
"boq_updates": [dict(r) for r in req.boq_rows],
|
||||
"costn_updates": [dict(r) for r in req.costn_rows],
|
||||
"costn_memo_updates": dict(req.costn_memos),
|
||||
"prjlst_updates": prj_upd,
|
||||
}
|
||||
if req.bdqty_rows and "BDQTY.DBF" in out:
|
||||
save["bdqty_rows"] = decode_bdqty(out["BDQTY.DBF"]) + [dict(r) for r in req.bdqty_rows]
|
||||
out2, log = apply_save(out, save)
|
||||
return out2, pcode, log0 + log
|
||||
|
||||
|
||||
@router.post("/api/ui/contract-register")
|
||||
def api_contract_register(req: ContractRegReq):
|
||||
try:
|
||||
s = Source(req.source_path)
|
||||
except (FileNotFoundError, ValueError) as e:
|
||||
raise HTTPException(400, str(e))
|
||||
try:
|
||||
files, pcode, changes = contract_register(s.files, req)
|
||||
except (ValueError, KeyError) as e:
|
||||
raise HTTPException(400, str(e))
|
||||
target = req.target_folder_name or f"{s.path.stem}_계약{pcode}"
|
||||
names = dict(s.names, **{f"BOQ{pcode}.DBF": f"BOQ{pcode}.DBF"})
|
||||
res = write_output(_root(), target, files, names, s.entries)
|
||||
return {**res, "new_pcode": pcode, "change_count": len(changes)}
|
||||
Reference in New Issue
Block a user