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
@@ -393,9 +393,22 @@ def job_for_row(name, spec):
return None if q is None else ("1048" if q >= 12 else "1049")
if "공기압축기" in n and "이동식" in n:
return "1048" if q is not None and q >= 2.83 else None
if n in ("크레인(타이어)", "크레인(무한궤도)", "콘크리트펌프차"):
return "1048"
if n == "콘크리트믹서":
return None if q is None else ("1048" if q >= 0.55 else "1050")
return job_of(name)
# 원문 이름과 다르게 짝지은 기종 — 비고에 해석으로 남김
JOB_HINT = {
"크레인(타이어)": "해석: 크레인(타이어) = 원문 「기중기(차륜)」",
"크레인(무한궤도)": "해석: 크레인(무한궤도) = 원문 「기중기(무한궤도)」",
"콘크리트펌프차": "해석: 콘크리트펌프차 = 원문 「콘크리트 펌프(5㎥이상)」",
"콘크리트믹서": "해석: 0.55㎥ 미만 = 원문 「소형믹서」(일반기계운전사) · 이상 = 「콘크리트 믹서(0.55㎥ 이상)」",
}
# ---------------------------------------------------------------- 실행
@@ -477,6 +490,9 @@ def compose(rows):
unit[lab] = ""
row = {"열쇠": k, "이름": name, "규격": spec, "단위": unit, "": val, "출처": src}
memo = ["조종원직종 원문 이름: " + JOB_NAME[job]] if job else []
hint = JOB_HINT.get(re.sub(r"\s", "", name)) if job and not fixed else None
if hint:
memo.append(hint)
if v.get("규격단위"):
memo.append("규격 단위: " + v["규격단위"])
if memo:
@@ -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