feat(792-3): run_line · B09 산근 셈이 엔진 새 의미를 따름 — 네 길 같은 금액
- master_cut: 엔진 답 덧칸 `셈법`(새 칸 뗀 작은 로직) · `plain(got, gone)` 이 셈법 있으면 줄 자리 · 합밖 · 단계 · 블록 끝전 · 조정 그대로(켜쌓기율 · 별도계상 뒤 다시 버림이 큰나머지 · 조정을 덮던 흠) · `kept` - M02_Labor_Doc: run_line 답이 단계 · 셈법을 넘김 · drop_apart 가 plain 에 뺄 줄 차례를 줌(686 줄 · 늘지 않음) - 새 B09_Estimation_Sangeun_Sum: 산근 풀이 · 산근 글이 새 의미 로직만 엔진 규칙(표시 · 줄 자리 · 합밖 · 번지 · 단계 · 끝전 · 조정) · 옛 로직 = Q ROUND 2 · ROUNDDOWN 1 · 0 그대로 - B09 산근 글: 로직 줄(중기 세 줄) 금액을 줄 하나로 묶어 차례를 호표와 맞춤(합 같음) - 지킴 시험 test_792_3_engine_paths: 견본 넷(봉화 · 소광 · 영월 · 임도) + 단위 로직 = 엔진 · M02 SK · SC 한 줄 · B09 산근(글 계 포함) 같은 금액 · 다시 버림 셈법 지킴 - test_791_1: 견본 넷을 만들기 함수로 떼어 냄(같은 기대) - test_222_1 낡은 기대 셋 고침: 751-3 끝수 뒤 별도계상 뺀 기준 = 남은 줄 비목 합 원 버림(소수 없음) · 옛 기대는 「원 버린 기준 − 소수 재료 금액」이라 0.6 원 어긋남 - test_145_1: 작업량 Q 이름 규칙이 B09_Estimation_Sangeun_Sum 으로 옮김 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sg
|
||||
from B09_Estimation import B09_Estimation_Sangeun_Sum as ss
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor_doc
|
||||
from M02_MasterTemplete import M02_Labor_Inputs as labor_inputs
|
||||
@@ -58,8 +59,8 @@ def _qty_text(item: dict, env: dict, whole: Any, times: Decimal, rate: Decimal |
|
||||
return text
|
||||
|
||||
|
||||
def _line(name: str, price: Any, qty_text: str, q: float) -> tuple[str, float | None]:
|
||||
money = None if price is None else sg._money(float(price), q)
|
||||
def _line(name: str, price: Any, qty_text: str, q: float, rule=None) -> tuple[str, float | None]:
|
||||
money = None if price is None else ss._cut(float(price) * q, rule) # 줄 자리(792-3)
|
||||
return f"{name} : {_price(price)} * {qty_text} = {_money(money)}", money
|
||||
|
||||
|
||||
@@ -308,21 +309,24 @@ def _pct_specs(whole, row, env) -> list[str]:
|
||||
return [str(one.get("규격") or "") for one in got]
|
||||
|
||||
|
||||
def _full(amounts: list[dict]) -> list[dict]:
|
||||
return [{**dict.fromkeys(sg.PARTS, 0.0), **a} for a in amounts]
|
||||
|
||||
|
||||
def _extra_lines(whole, row, env, amounts: list[dict]) -> list[tuple[str, dict]]:
|
||||
"""% 줄(덧줄) — 밑수 = 그때까지 B09 비목 합(`Sangeun._extras` 와 같음) · 금액 ROUNDDOWN 1."""
|
||||
out: list[tuple[str, dict]] = []
|
||||
specs = _pct_specs(whole, row, env)
|
||||
for extra, spec in zip(row.get("덧줄", []), specs):
|
||||
part = sg.COST_PART.get(extra.get("비목"), "material")
|
||||
sums = {
|
||||
name: sg.total([a.get(p, 0.0) for a in amounts]) for name, p in sg.COST_PART.items()
|
||||
}
|
||||
sums = ss.sums(row, _full(amounts)) # 합밖 · 제외(792-3)
|
||||
head = f"{extra['이름']}{f' ({spec})' if spec else ''} : "
|
||||
if any(v is None for v in sums.values()):
|
||||
out.append((f"{head}밑수 빈칸", {part: None}))
|
||||
amounts.append({part: None})
|
||||
continue
|
||||
env.update({k: Decimal(str(v)) for k, v in sums.items()})
|
||||
ss.scope(row, _full(amounts), env) # 번지 · 단계(792-3)
|
||||
try:
|
||||
node = mf.parse(extra["식"])
|
||||
value = float(mf.evaluate(node, env, whole, 0))
|
||||
@@ -331,7 +335,7 @@ def _extra_lines(whole, row, env, amounts: list[dict]) -> list[tuple[str, dict]]
|
||||
out.append((f"{head}계산 못 함 — {e}", {part: None}))
|
||||
amounts.append({part: None})
|
||||
continue
|
||||
money = sg.rounddown(value, 1)
|
||||
money = ss.extra(row, extra, value, env) # 줄 자리(792-3)
|
||||
out.append((f"{head}{shown} = {_money(money)}", {part: money}))
|
||||
amounts.append({part: money})
|
||||
return out
|
||||
@@ -377,13 +381,15 @@ def render_sangeun_text(
|
||||
q = float(Decimal(str(raw)) / times * (scale or 1))
|
||||
kind = item.get("종류")
|
||||
if kind == "로직":
|
||||
merged: dict = {} # 줄 하나 = 금액 하나(중기 세 줄 · 792-3 합밖 · 번지 차례)
|
||||
for text, money in _logic_lines(whole, item, env, qty_text, q, price_of):
|
||||
out.append(text)
|
||||
amounts.append(money)
|
||||
merged.update(money)
|
||||
amounts.append(merged)
|
||||
continue
|
||||
if kind == "인력":
|
||||
key = mf.fill(item["요소"], env) if isinstance(item["요소"], str) else ""
|
||||
text, money = _line(name, price_of(key), qty_text, q)
|
||||
text, money = _line(name, price_of(key), qty_text, q, item.get("자리"))
|
||||
out.append(text)
|
||||
amounts.append({"labor": money})
|
||||
continue
|
||||
@@ -397,12 +403,12 @@ def render_sangeun_text(
|
||||
if q == 0 and price is None:
|
||||
price = 0.0 # 안 쓰는 재료 줄 — 금액 0(`Sangeun._line` 과 같음)
|
||||
label = f"{name}({spec})" if spec else name
|
||||
text, money = _line(label, price, qty_text, q)
|
||||
text, money = _line(label, price, qty_text, q, item.get("자리"))
|
||||
out.append(text)
|
||||
amounts.append({sg.COST_PART.get(item.get("비목"), "material"): money})
|
||||
for text, _money_part in _extra_lines(whole, row, env, amounts):
|
||||
out.append(text)
|
||||
sums = sg.block_totals([{**dict.fromkeys(sg.PARTS, 0.0), **a} for a in amounts])
|
||||
sums = ss.totals(row, _full(amounts)) or sg.block_totals(_full(amounts)) # 끝전 · 조정(792-3)
|
||||
parts = " + ".join(
|
||||
f"{PART_NAME[p]} {'빈칸' if sums[p] is None else f'{sums[p]:,.0f}'}" for p in sg.PARTS
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user