Files
Aislo/tools/stmate-test/conv_jaje.py
T

103 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""재료비 수량집계표를 엑셀파일로 만들기(wM_JajeXLS · 단방향 내보내기) — 「주요건설자재 현황」 시트.
근거(지식DB REA 분석/37 §3): STmate.Cpm TWM_JAJEXLS DFM 0x698df0(입력 8칸 · 「엑셀파일 만들기」·「종료」) ·
ActMkJaje 0x5658bc(CON_QTY 의 B_CODE 첫 글자 M 줄 → PRT) · ST_P_XLS Prn_Col_M_EXL 0x4eb4d0 · 머리 0x4ea93c
· 2~5줄 공사 정보 · 7줄 머리글(Start8x.Ini [PRINT] CAP_* · 없으면 기본 글) · 열 너비 31·31·12·6·12·14·12
· 줄: 명칭(+줄바꿈+NAME2) · 규격(+줄바꿈+SIZE2) · 수량 = 소수 2자리 사사오입 · 단위 · 단가 · 금액 = ROUND(수량×단가, 0) · 비고
· 65,000 줄 넘으면 새 시트
조건부: 1줄 제목 자리 · 「단위/수량」 자리 바꿈 설정(+0x529c) · 셀 병합 범위 · 파일 이름(원본 기본값 미확인)
"""
from __future__ import annotations
import io
from decimal import ROUND_HALF_UP, Decimal
TITLE = "주요건설자재 현황"
SHEET = "주요건설자재현황"
CAPS = [
("CAP_NAME", "명 칭"),
("CAP_SIZE", "규 격"),
("CAP_QTY", "수 량"),
("CAP_SUNIT", "단위"),
("CAP_AMT", "단 가"),
("CAP_AMOUNT", "금 액"),
("CAP_BIGO", "비 고"),
]
WIDTHS = [31, 31, 12, 6, 12, 14, 12]
MAX_ROWS = 65000
def half(v: float, d: int = 0) -> float:
q = Decimal(1).scaleb(-d)
return float(Decimal(str(v)).quantize(q, rounding=ROUND_HALF_UP))
def caps_from_ini(sections: dict) -> list[str]:
prt = {k.upper(): v for k, v in (sections.get("PRINT") or {}).items()}
return [prt.get(k) or d for k, d in CAPS]
def _join(a: str, b: str) -> str:
a, b = (a or "").rstrip(), (b or "").rstrip()
return f"{a}\n{b}" if b else a
def build_jaje_xlsx(head: dict, rows: list[dict], caps: list[str] | None = None) -> bytes:
"""head: PNAME · PNUM · BUDGET · 수요기관 · 시공회사 · 공사현장 · 공사기간 · 계약일자 /
rows: NAME · NAME2 · SIZE · SIZE2 · UNIT · QTY · PRICE(차수 합계 단가)."""
import openpyxl
from openpyxl.styles import Alignment, Font
wb = openpyxl.Workbook()
wb.remove(wb.active)
caps = caps or [d for _k, d in CAPS]
chunks = [rows[i : i + MAX_ROWS] for i in range(0, len(rows), MAX_ROWS)] or [[]]
for n, part in enumerate(chunks):
ws = wb.create_sheet(SHEET if n == 0 else f"{SHEET}{n + 1}")
for j, w in enumerate(WIDTHS):
ws.column_dimensions[chr(65 + j)].width = w
ws["A1"] = TITLE
ws["A1"].font = Font(bold=True, size=14)
ws.merge_cells("A1:G1")
ws["A1"].alignment = Alignment(horizontal="center")
ws["A2"] = f"공 사 명: {head.get('PNAME', '')}"
ws["G2"] = f"관리번호: {head.get('PNUM', '')}"
for r, (la, va, lb, vb) in enumerate(
[
("수 요 기 관", head.get("수요기관", ""), "시 공 회 사", head.get("시공회사", "")),
("예 산 액", head.get("BUDGET", ""), "공 사 현 장", head.get("공사현장", "")),
("공 사 기 간", head.get("공사기간", ""), "계 약 일 자", head.get("계약일자", "")),
],
start=3,
):
ws.cell(r, 1, la)
ws.cell(r, 3 if r == 4 else 2, va)
if r != 4:
ws.merge_cells(f"B{r}:C{r}")
ws.cell(r, 4, lb)
ws.merge_cells(f"D{r}:E{r}")
ws.cell(r, 6, vb)
ws.merge_cells(f"F{r}:G{r}")
for j, c in enumerate(caps, 1):
ws.cell(7, j, c).alignment = Alignment(horizontal="center")
for i, r in enumerate(part, start=8):
qty = half(float(r.get("QTY") or 0), 2)
price = float(r.get("PRICE") or 0)
ws.cell(i, 1, _join(r.get("NAME", ""), r.get("NAME2", ""))).alignment = Alignment(wrap_text=True)
ws.cell(i, 2, _join(r.get("SIZE", ""), r.get("SIZE2", ""))).alignment = Alignment(wrap_text=True)
ws.cell(i, 3, qty)
ws.cell(i, 4, r.get("UNIT", ""))
ws.cell(i, 5, price)
ws.cell(i, 6, half(qty * price, 0))
ws.cell(i, 7, "")
out = io.BytesIO()
wb.save(out)
return out.getvalue()
def amount(row: dict) -> float:
"""금액 = ROUND(수량(소수 2자리 사사오입) × 단가, 0)."""
return half(half(float(row.get("QTY") or 0), 2) * float(row.get("PRICE") or 0), 0)