feat(715-1b): 엔진 입력 덧칸 — 호표 줄 단가(손 단가) · 로직 줄 값묶음(노 · 재 · 경) · SK 고친 줄 셈 마무리

- master_pick — _priced 앞 「단가」 덧칸 = 손 단가(찾기 없음 · 출처 「손 단가」) · bundled(값묶음 셋 = 그것 · 일부 = 엔진 값 위 · 계 = 셋 합)
- master_formula — 로직 줄 묶음 부르는 줄 하나(mp.bundled) · 712-1 출력 묶음과 다른 이름
- M01 호표 5,266 줄에 덧칸 없음 · M01 1,469 로직 값 전후 같음
- 시험 test_715_sangeun_engine.py 다시 넣음(손 단가 · 3 % 다시 셈 · 값묶음 일부 · 참조 · 고정 더한줄)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 16:38:19 +09:00
co-authored by Claude Opus 5.5
parent 472c78cd77
commit c60b87c111
3 changed files with 105 additions and 1 deletions
@@ -587,7 +587,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
continue
if item["종류"] == "로직":
try:
bundle = evaluate(parse(item["요소"]), env, master, depth)
bundle = mp.bundled(item, lambda: evaluate(parse(item["요소"]), env, master, depth))
except EmptyInput as e:
lines.append(_blank_line(item, str(e)))
continue
@@ -9,6 +9,7 @@ from __future__ import annotations
from contextlib import contextmanager
from contextvars import ContextVar
from decimal import Decimal
import master_material as mm
from master_unit import UNITS, convert, kind_of, same_kind # noqa: F401 — 단위 한 벌(712-2)
@@ -121,6 +122,8 @@ def priced(master, item: dict, ref, env: dict, index: int | None):
def _priced(master, item: dict, ref, env: dict, index: int | None):
if item.get("단가") is not None: # 손 단가(715-1b · M02 단가산출 덮개 · 더한줄) — 찾기 없음
return Decimal(str(item["단가"])), HAND, None, None
unit = item.get("단위")
picked = (CHOSEN.get() or {}).get(str(index)) if index is not None else None
if not picked and empty_term(ref): # 자재표에 없는 품목 — 구분 안 첫 품목으로 잇지 않음
@@ -143,6 +146,21 @@ def _priced(master, item: dict, ref, env: dict, index: int | None):
return price, source, why, warn
HAND = "손 단가"
def bundled(item: dict, base):
"""로직 줄 노 · 재 · 경 묶음 — 덧칸 `값묶음`(715-1b · M02 단가산출)이 셋 다면 그것 ·
일부면 `base()`(엔진 값) 위에 · 계 = 셋 합 · 덧칸 없음 = `base()` 그대로."""
given = item.get("값묶음")
if not given:
return base()
costs = mf.COST_ITEMS
have = {k: Decimal(str(v)) for k, v in given.items() if k in costs}
got = have if len(have) == len(costs) else {**base(), **have}
return {**got, "계": sum(got[k] for k in costs)}
NO_ITEM = "자재표에 없는 품목 — 검색어 비움(결정 목록) · 품목을 골라 넣음"