feat(814-1 1·2): 올리기 파일 하나 · 확장자 파일 = 단가산출표 판 + 산출근거
- 같은 공사 엑셀 · 원파일 붙이기 걷음(keep · upload_native · same 지움) — 엑셀이든 .estx · .STC 든 하나만 - 원파일 = 새 HP · 산출근거 머리(코드 · 이름 · 규격 · 단위 · 저장 금액) 단가산출표 격자 · 일위대가 안 만듦(HeadMainDat 안 씀) - 화면 = 일위대가 없으면 단가산출 목록 먼저 · 올림 안내 글 하나로 - 시험 810-2: 붙음 · EST 호표 → SC 기대 지움(요구 742-1 · 743-2 로 걷은 길) → 파일 하나 · 단가산출표 판 기대 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,9 +7,8 @@
|
||||
STmate = 원문 그대로 `M02_San_Emit.stmate`(원파일 원문 = 엑셀 숨은 G 열 · 단가 = COSTN ·
|
||||
끝전 = 옵션 SAN_TYP 1 비목버림 / 0 합버림_큰나머지 · 810-1 저장값 대조로 확인).
|
||||
블록 번 = 엑셀 길과 같음(EST = 산근 표 차례 1부터 = 「단산N」 · STmate = 코드 숫자).
|
||||
같은 공사 = 엑셀 산근 블록이 모두 원파일 블록과 금액 · 이름이 같음(`same`) — 둘 다 있으면 원파일.
|
||||
일위대가(EST HeadMainDat) → 실무자료 호표 꼴(`hopyo`) · STmate 일위대가 구성은 못 읽음(BDQTY 난독화) →
|
||||
호표 없음(엑셀을 같이 올리면 엑셀 호표 + 원파일 산근).
|
||||
올리기 = 파일 하나(814-1 · 요구 742-1 — 같은 공사 엑셀 붙이기 걷음) · 원파일 = 단가산출표 판(`grid` ·
|
||||
산출근거 머리) + 산출근거 · 일위대가 안 만듦(요구 743-2 · `hopyo` = 단산만).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -154,89 +153,52 @@ def inner(saved: dict, logic: dict) -> list[tuple[dict, int | None, str | None]]
|
||||
return out
|
||||
|
||||
|
||||
# ── 같은 공사 · 일위대가 ───────────────────────────────────────────────
|
||||
def same(saved: dict, sheets: dict) -> bool:
|
||||
"""엑셀 산근 블록이 모두 원파일 같은 번 블록과 계 금액(1 원 안)이 같음 = 같은 공사."""
|
||||
from M02_MasterTemplete import M02_San_Book as book
|
||||
|
||||
have = numbers(saved)
|
||||
kind = book.kind_of(sheets)
|
||||
if not kind or (kind == "STmate") != (saved["꼴"] == "STmate"):
|
||||
return False
|
||||
if kind == "STmate":
|
||||
excel = {int(b["코드"][1:]): b["머리"].get("C") for b in book.stmate_blocks(sheets)}
|
||||
else:
|
||||
excel = {int(n): t.get("T") for n, t in book.est_prices(sheets)[1].items()}
|
||||
if not excel:
|
||||
return False
|
||||
for n, total in excel.items():
|
||||
blk = have.get(int(n))
|
||||
if blk is None or abs(float(total or 0) - float((blk.get("저장") or {}).get("T") or 0)) > 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
GALLAE = {6: "노임", 5: "자재", 105: "기계", 104: "단가산출", 103: "호표", 21: "기타", 101: "기타"}
|
||||
# ── 단가산출표 판(814-1 · 요구 742-2 · 743-2) ───────────────────────────
|
||||
LIST = "단가산출표"
|
||||
_LIST_HEAD = ("코드", "이름", "규격", "단위", "재료비", "노무비", "경비", "합계")
|
||||
_LIST_COST = ("M", "L", "S", "T")
|
||||
|
||||
|
||||
def hopyo(saved: dict) -> dict[str, Any]:
|
||||
"""EST 일위대가(HeadMainDat) → 실무자료 읽기 꼴(`M02_Hopyo_Excel.read_book` 짝 · 호표 · 단산) ·
|
||||
STmate = 호표 없음(구성 못 읽음)."""
|
||||
blocks = numbers(saved)
|
||||
names = {str(b.get("코드")): n for n, b in blocks.items()}
|
||||
heads = {str(h.get("코드")): n for n, h in enumerate(saved.get("일위") or [], 1)}
|
||||
"""원파일 → 실무자료 읽기 꼴 — 단산(산출근거 머리)만 · 일위대가 안 만듦(요구 743-2 · HeadMainDat 안 씀)."""
|
||||
out: dict[str, Any] = {"꼴": "원파일", "시트": saved["꼴"], "호표": [], "검산": {"어긋남": []}}
|
||||
out["단산"] = [
|
||||
{"번": n, "이름": b.get("이름"), "단위": b.get("단위"), "금액": _cost(b.get("저장"))}
|
||||
for n, b in blocks.items()
|
||||
for n, b in numbers(saved).items()
|
||||
]
|
||||
if saved["꼴"] != "EST":
|
||||
return out
|
||||
for n, h in enumerate(saved.get("일위") or [], 1):
|
||||
lines = []
|
||||
for k, ln in enumerate(h.get("줄") or []):
|
||||
link = ln.get("연결")
|
||||
code = str((ln.get("자원") or {}).get("코드") or "")
|
||||
ref = None
|
||||
if link == 104 and code in names:
|
||||
ref = {"종류": "단산", "번": names[code]}
|
||||
elif link == 103 and code in heads:
|
||||
ref = {"종류": "일위", "번": heads[code]}
|
||||
kind = "%" if ln.get("연결") in (52, 58, 62) else GALLAE.get(link or 0, "기타")
|
||||
if kind == "자재" and str(ln.get("단위") or "") == "인":
|
||||
kind = "노임"
|
||||
if not any((ln.get("단가") or {}).values()) and kind not in ("%",):
|
||||
kind = "별도계상" if not ref else kind
|
||||
lines.append(
|
||||
{
|
||||
"차례": k,
|
||||
"이름": ln.get("품명") or "",
|
||||
"규격": ln.get("규격") or "",
|
||||
"수량": _dec(ln.get("수량")),
|
||||
"단위": ln.get("단위") or "",
|
||||
"단가": _cost(ln.get("단가")),
|
||||
"금액": _cost(ln.get("저장")),
|
||||
"비고": "",
|
||||
"참조": ref,
|
||||
"갈래": kind,
|
||||
"행": ln.get("행"),
|
||||
"코드": code or None,
|
||||
}
|
||||
)
|
||||
out["호표"].append(
|
||||
{
|
||||
"번": n,
|
||||
"이름": h.get("이름"),
|
||||
"규격": h.get("규격") or "",
|
||||
"단위": h.get("단위") or "",
|
||||
"금액": _cost(h.get("저장")),
|
||||
"행": [n, n],
|
||||
"줄": lines,
|
||||
}
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def grid(saved: dict) -> dict[str, Any]:
|
||||
"""원파일 → 단가산출표 한 시트 격자(`M02_Hopyo_Grid.read` 꼴) — 산출근거 머리(코드 · 이름 · 규격 ·
|
||||
단위 · 저장 금액) 한 줄씩 · 행 = 1 머리 + 블록 차례."""
|
||||
cells: dict[str, dict] = {}
|
||||
cols = "ABCDEFGH"
|
||||
for c, head in zip(cols, _LIST_HEAD):
|
||||
cells[f"{c}1"] = {"값": head}
|
||||
rows = []
|
||||
for at, (n, b) in enumerate(numbers(saved).items(), 2):
|
||||
cost = b.get("저장") or {}
|
||||
vals = [b.get("코드"), b.get("이름"), b.get("규격"), b.get("단위")]
|
||||
vals += [_dec(cost.get(k)) for k in _LIST_COST]
|
||||
for c, v in zip(cols, vals):
|
||||
if v not in (None, ""):
|
||||
cells[f"{c}{at}"] = {"값": v if isinstance(v, (int, float)) else str(v)}
|
||||
rows.append({"번": n, "이름": b.get("이름"), "행": [at, at]})
|
||||
sheet = {"id": "s1", "이름": LIST, "칸": cells, "틀고정": {"행": 1}}
|
||||
sheet["열"] = {c: {"폭": w} for c, w in zip(cols, (10, 40, 18, 6, 12, 12, 12, 12))}
|
||||
book = {"종류": "통합문서", "판": 1, "열": "", "서식": [{}], "시트": [sheet], "활성": "s1"}
|
||||
return {
|
||||
"책": book,
|
||||
"호표": [],
|
||||
"단산": rows,
|
||||
"보기": {LIST: "s1"},
|
||||
"원본": {LIST: LIST},
|
||||
"목록": {"호표": [], "단산": [{"번": r["번"], "행": r["행"][0]} for r in rows]},
|
||||
"줄": [],
|
||||
}
|
||||
|
||||
|
||||
def _dec(v: Any) -> int | float | None:
|
||||
"""수 → 엑셀 읽기와 같은 꼴(정수 = int · 아니면 float · 없음 = None)."""
|
||||
if v is None:
|
||||
|
||||
Reference in New Issue
Block a user