- 끝수 한 곳 `master_cut.py`(B09 `rounddown` 재사용) — 엔진 답에 입힘: 줄 비목마다 0.1 원 버림 · 하위 로직 줄 = 하위 끝수 계 × 수량 · 덧줄 = 버린 줄 합으로 다시 셈 · 비목 합 1 원 버림 · 중간 칸도 버린 값 - 쓰는 곳: M01 시험 계산(`check_master.calc`) · 읽는 식(`master_text.lines`) · 옛 조합 미리보기(`master_combo`) · M02 `run_line`(단가산출 · 일위대가 · 펼침) · 별도계상 뺀 기준 · 켜쌓기율 곱 뒤 · 일위대가 반영 · 반영합 · 비율 줄 · 사전조합 안 줄 - 일위대가 반영 TS 짝(`Labor_Calc.ts`) 같은 끝수 · 거울 헬퍼가 상대 경로 .ts 가져옴 - B09 = `run_line(raw=True)` 엔진 날 값(제 단가로 다시 버림 · % 덧줄 율 비 그대로) — 검증_sub3 B09 247 칸 전후 같음 - `master_text` 꼴 셋 = `common_util_money` 로 갈음(줄 금액 `fmt_line_money` · 식 안 금액 = 버린 0.1 원) - 화면: 상세 줄 금액 · 시험 계산 줄 · 중간 돈 = `formatLineMoney` · 일위대가 반영 칸 0.1 원 · 상세 바닥 · 박스 · 인력재료경비 비목 소계 = 줄 합 1 원 버림 - 테스트1 네 보기 · 서고 목록 = 124,419(옛 124,420 / 124,419.85) · 재료 줄 11,694.6 + 23,425.6 + 1,404.8 = 소계 36,525 - 새 지킴 시험 test_751_3_money_cut - 옛 날 금액(끝수 없음)을 대조하던 시험 24 파일 고침 — 까닭 = 금액 규칙이 줄 0.1 원 · 소계 1 원 버림으로 바뀜(고정 값 → 새 값 · 줄 합 = 계 → 줄 합 아래 비목마다 1 원 안 · 곱 비 = 끝수 폭 안) · 실무 xlsx 영월 엑셀 같음 22 → 23 · 이어 붙이는 TS 시험(704_3 · 717_2b)에 `rounddown` 같은 식 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
4.1 KiB
Python
84 lines
4.1 KiB
Python
"""M02 단가산출(SK) 보기 넷 = 셈 답 하나(PLAN 750-1 · 요구 588-1) — 화면 셈 길 답 덧칸.
|
||
|
||
- `글` = 읽는 식(M01 `/text` 와 같은 꼴 · `master_text.lines` + `master_sheet_line.annotate`) ·
|
||
SK 고친 줄(`_Over`) · 같은 입력(자동값 + 입력값) · 같은 고름(`choosing`) 위에서 지음.
|
||
줄마다 `cid` = SK 줄 cid(`annotate` 와 같은 차례 — 호표 줄 × `_shares` 비목 · 뒤 덧줄).
|
||
금액 · 소계 · 계 = SK `줄` · `기준` 그대로(`finish` — 별도계상 뺌 · 켜쌓기율 · 단위 환산 ÷배 ·
|
||
못 셈 줄 까닭) · 식 글(`글` · `이름글`)은 바탕 단위 셈 그대로 + 머리 `나눔`(배 ≠ 1).
|
||
- `중간` = 시험 계산 중간값(수 · 글만) — 시험 계산 = {lines: `줄`, sums: `기준`, middle: `중간`}.
|
||
계산 자리 = 서버 단독(CLAUDE.md 5장 ③) · 화면은 그리기만.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from decimal import Decimal
|
||
from typing import Any
|
||
|
||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||
|
||
mf = labor.m01.cm.mf
|
||
import master_sheet_line as msl # noqa: E402 — scripts 길은 `m01` 이 둠
|
||
import master_text as mtx # noqa: E402
|
||
|
||
_ERRORS = (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError)
|
||
|
||
|
||
def text_of(over: Any, key: str, values: dict, picks: Any, ids: list, eng: list) -> dict:
|
||
"""`{글, 중간}` — `eng` = `run_line` 줄(별도계상 빼기 전 · 차례 맞춤용) · 못 지음 = 까닭."""
|
||
row = over.logic(key)
|
||
auto = {k: v for k, v in labor.m01.mau.values(over, row).items() if v is not None}
|
||
given = {**auto, **labor.m01._dec(values or {})}
|
||
try:
|
||
with mf.mp.choosing(picks or None):
|
||
text = msl.annotate(over, key, given, mtx.lines(over, key, given))
|
||
mid = labor.mcut.engine(over, key, mf.run(over, key, given)).get("중간") or {} # 751-3
|
||
except _ERRORS as e:
|
||
return {"글": {"ok": False, "reason": str(e) or type(e).__name__}, "중간": {}}
|
||
if text.get("ok") and "groups" in text and "결과" not in row:
|
||
_tag(row, ids, eng, text)
|
||
keep = (Decimal, int, float, str, bool)
|
||
return {"글": text, "중간": {k: v for k, v in mid.items() if isinstance(v, keep)}}
|
||
|
||
|
||
def _tag(row: dict, ids: list, eng: list, text: dict) -> None:
|
||
"""글 줄마다 `cid` — `annotate` 와 같은 차례(호표 줄 × 몫 비목 → 덧줄)."""
|
||
slots = {g["비목"]: iter(g["줄"]) for g in text["groups"]}
|
||
items, adds = row.get("호표") or [], row.get("덧줄") or []
|
||
for n, line in enumerate(eng):
|
||
if n < len(items):
|
||
parts = [p for p, _ in mtx._shares(line)] if line.get("비목") else []
|
||
else:
|
||
parts = [adds[n - len(items)]["비목"]] if n - len(items) < len(adds) else []
|
||
for part in parts:
|
||
one = next(slots.get(part, iter(())), None)
|
||
if one is not None and n < len(ids):
|
||
one["cid"] = ids[n]
|
||
|
||
|
||
def finish(ans: dict) -> dict:
|
||
"""화면 답(`per_unit` 뒤) — 글 금액 · 소계 · 계 = SK 줄 · 기준 · SK 에 없는 줄(별도계상) 뺌."""
|
||
text = ans.get("글") or {}
|
||
base = ans.get("기준")
|
||
if not text.get("ok") or "groups" not in text or not base:
|
||
return ans
|
||
sk = {x["cid"]: x for x in ans.get("줄") or [] if x.get("cid")}
|
||
groups = []
|
||
for g in text["groups"]:
|
||
kept = []
|
||
for one in g["줄"]:
|
||
line = sk.get(one.get("cid"))
|
||
if line is None and one.get("cid"):
|
||
continue # SK 에서 빠진 줄(별도계상)
|
||
if line is not None:
|
||
parts = line.get("비목") or {}
|
||
money = parts.get(g["비목"]) if len(parts) > 1 else line.get("금액")
|
||
one = {**one, "금액": money}
|
||
if line.get("까닭"):
|
||
one["까닭"] = line["까닭"]
|
||
kept.append(one)
|
||
if kept:
|
||
groups.append({**g, "줄": kept, "소계": base.get(g["비목"])})
|
||
times = ans.get("화면배")
|
||
head = {"나눔": times} if times not in (None, 1) else {}
|
||
return {**ans, "글": {**text, "groups": groups, "계": base.get("계"), **head}}
|