feat(M01): 재료 기본 검색어 찾기 · 고름 받기 · 단위 환산 · 단위 경고 (PLAN 8-2)

- 새 master_pick.py — 검색어 찾기(지역 낱말은 차례만) · 고름(호표 줄 차례 → 자재 키) · 같은 성격 단위 환산 · 단위경고 칸 · 기계 목록 밖 원문 분류번호 받기
- Router 몸에 고름 칸 · check_master 검색어 후보 0 · 단위 경고
- 검색어 없는 줄은 옛 길 그대로 — 1,354 자동값 금액 변화 0
- 시험 test_m01_pick 8개

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-24 20:24:43 +09:00
co-authored by Claude Sonnet 5
parent f29dc14c9a
commit af842c0337
5 changed files with 304 additions and 17 deletions
@@ -267,6 +267,7 @@ class Master:
if number in slot:
self.twice.add((key[:2], number))
slot.setdefault(number, row)
mp.EQ.set(self.numbers.get("EQ", {})) # 기계 입력이 목록 밖 원문 분류번호도 받게
def get(self, ref: str) -> dict:
tid, _, number = ref.partition(":")
@@ -502,7 +503,7 @@ def _inputs(row: dict, given: dict) -> dict:
pass
if isinstance(value, (int, float)):
value = Decimal(str(value))
if "고르기" in spec and value not in spec["고르기"]:
if "고르기" in spec and value not in spec["고르기"] and not mp.machine(spec, value):
raise FormulaError(f"입력 「{name}」={value} 고르기 밖")
if "범위" in spec:
low, high = spec["범위"]
@@ -547,8 +548,8 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
return {"결과": cut(row, value, "결과"), "중간": _shown(env)}
sums = dict.fromkeys(COST_ITEMS, Decimal(0))
lines = []
for item in row.get("호표", []):
source = why = None
for n, item in enumerate(row.get("호표", [])):
source = why = warn = None
try:
qty = evaluate(parse(item["수량"]), env, master, depth)
except EmptyInput as e: # 비어 있는 입력을 쓰는 줄만 비움
@@ -579,18 +580,10 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
lines.append(_blank_line(item, str(e)))
continue
# 후보 0 · 값 빈 품목은 그 줄만 단가·금액을 비우고 까닭을 닮 — 로직은 계속 · 합에서 뺌
# (없는 키는 틀린 로직 — 지금처럼 멈춤)
# (없는 키는 틀린 로직 — 지금처럼 멈춤) · 검색어 · 고름 · 단위 환산은 `master_pick`
if isinstance(ref, str) and qty != 0:
master.get(ref)
try:
price, source = element(master, ref, env, item.get("단위"))
why = (
None
if price is not None
else f"「{master.label(ref)}」 값 없음 — 관리자가 채울 값"
)
except FormulaError as e:
price, why = None, str(e)
price, source, why, warn = mp.priced(master, item, ref, env, n if depth == 0 else None)
if price is None and qty == 0:
price, why = Decimal(0), None # 안 쓰는 재료 줄 — 값이 비어도 금액 0
split = {item["비목"]: None if price is None else qty * price}
@@ -606,6 +599,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
"비목": split,
**({"출처": source} if source else {}),
**({"까닭": why} if why else {}),
**({"단위경고": warn} if warn else {}),
}
)
# 덧줄이 호표 줄 금액 하나를 빼고 더할 수 있게 — `줄.'호표 줄 이름'`(빈 줄은 뺌)
@@ -853,3 +847,4 @@ from master_material import ( # noqa: E402, F401
element,
pick,
)
import master_pick as mp # noqa: E402 — 검색어 · 고름 · 단위