feat(master_data): 자체 재료 67개 · 막혔던 재료 줄 · 크레인·펌프차·믹서 조종원직종

- 재료_자체.json 새로 — 공시에 없는 재료 이름·규격·단위 · 값 null(관리자가 채움) · 비고에 원문 절
- 9·10·11·12·13장 로직에 자체 재료 줄 얹음 · 12-22 · 12-29 · 12-31 · 12-34-4 로직 새로(12장 56개)
- 기계 요소 조종원직종 — 크레인(타이어·무한궤도) · 콘크리트펌프차 = 건설기계운전사 · 믹서 0.55㎥ 미만 = 일반기계운전사(8-1-3 5)
- 12-1-2 믹서 · 12-11 · 12-14 크레인 · 12-17 펌프차 기계경비 줄
- 계산기 — 값 없는 요소는 「관리자가 채울 값」으로 멈춤 · 수량 0 줄은 금액 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
This commit is contained in:
2026-09-19 17:16:49 +09:00
co-authored by Claude Opus 5
parent 18b160c7dd
commit f563a97290
12 changed files with 1898 additions and 207 deletions
@@ -292,6 +292,8 @@ def evaluate(node, env: dict, master: Master, depth: int = 0):
return env[node[1]]
if kind == "ref":
value = master.get(fill(node[1], env)).get("값")
if value is None:
raise FormulaError(f"「{node[1]}」 값 없음 — 관리자가 채울 값")
if isinstance(value, list):
raise FormulaError(f"「{node[1]}」 값이 범위 — 입력으로 받아야 함")
return value
@@ -392,6 +394,10 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
)
else:
price = master.get(fill(item["요소"], env))["값"]
if price is None and qty == 0:
price = Decimal(0) # 안 쓰는 재료 줄 — 값이 비어도 금액 0
if price is None:
raise FormulaError(f"「{fill(item['요소'], env)}」 값 없음 — 관리자가 채울 값")
split = {item["비목"]: qty * price}
for k, v in split.items():
sums[k] += v