feat(master_data): 재료 마스터 모양 확정 반영 — 시중물가 시중·조달 두 묶음 · 재료_품셈재료 연결 · 낮은 값과 출처
- 재료_시중물가: 채택 · 견적가 칸 뺌 · 값 = 시중{다섯 칸 + 면수} · 조달{값·출처·기준}
- 관급 줄 가격정보 4,552 → 조달(출처 가격정보 · 기준 = 원천 면수) · 나라장터 온전 짝 54 → 조달 출처 「나라장터:<열쇠>」(값 안 베낌)
- 재료_나라장터자재 머리 「자료」 = 조달 자료
- 재료_자체 → 재료_품셈재료: 값·출처 칸 없음 · 요구절 · 연결(열쇠 5 · 후보 조건 3 · 못 이음 352)
- 엔진(master_material): 연결된 줄의 시중·조달 가운데 낮은 값 · 호표 줄 출처 · 후보 조건은 로직 입력 「자재지역」 — 줄 없으면 「지역 값 없음」 · 규격 여럿이면 「후보 여럿」
- 로직: 재료:자체 → 재료:품셈재료 516줄 · 자재지역 입력 24 로직 · check_master 품셈재료 틀·연결 검사 · M01 prices 출처
- 일괄 시험 계산(울진): 계산됨 1,109 · 값 없음 232 · 후보 여럿 8 · 미공표 2
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
@@ -311,7 +311,7 @@ def evaluate(node, env: dict, master: Master, depth: int = 0):
|
||||
raise FormulaError(f"모르는 이름 「{node[1]}」")
|
||||
return env[node[1]]
|
||||
if kind == "ref":
|
||||
value = master.get(fill(node[1], env)).get("값")
|
||||
value = element(master, fill(node[1], env), env)[0]
|
||||
if value is None:
|
||||
raise FormulaError(f"「{node[1]}」 값 없음 — 관리자가 채울 값")
|
||||
if isinstance(value, list):
|
||||
@@ -417,6 +417,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
|
||||
lines = []
|
||||
for item in row.get("호표", []):
|
||||
qty = evaluate(parse(item["수량"]), env, master, depth)
|
||||
source = None
|
||||
if item["종류"] == "로직":
|
||||
bundle = evaluate(parse(item["요소"]), env, master, depth)
|
||||
price = bundle["계"]
|
||||
@@ -426,15 +427,17 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
|
||||
else {item["비목"]: qty * price}
|
||||
)
|
||||
else:
|
||||
node = parse(item["요소"])
|
||||
if node[0] == "field": # 값 묶음 요소의 칸 — 「재료:시중물가:M….채택」
|
||||
price = master.get(fill(node[1][1], env))["값"][node[2]]
|
||||
else:
|
||||
price = master.get(fill(item["요소"], env))["값"]
|
||||
ref = fill(item["요소"], env)
|
||||
try:
|
||||
price, source = element(master, ref, env)
|
||||
except FormulaError:
|
||||
if qty != 0:
|
||||
raise
|
||||
price = None
|
||||
if price is None and qty == 0:
|
||||
price = Decimal(0) # 안 쓰는 재료 줄 — 값이 비어도 금액 0
|
||||
if price is None:
|
||||
raise FormulaError(f"「{fill(item['요소'], env)}」 값 없음 — 관리자가 채울 값")
|
||||
raise FormulaError(f"「{ref}」 값 없음 — 관리자가 채울 값")
|
||||
split = {item["비목"]: qty * price}
|
||||
for k, v in split.items():
|
||||
sums[k] += v
|
||||
@@ -446,6 +449,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
|
||||
"단가": price,
|
||||
"금액": qty * price,
|
||||
"비목": split,
|
||||
**({"출처": source} if source else {}),
|
||||
}
|
||||
)
|
||||
for extra in row.get("덧줄", []):
|
||||
@@ -511,9 +515,12 @@ def _check_node(row, node, known: set, master: Master, where: str, out: list, ca
|
||||
refs = _expand(row, part[1], master) or []
|
||||
for ref in refs:
|
||||
try:
|
||||
master.get(ref)
|
||||
link = master.get(ref).get("연결")
|
||||
except FormulaError as e:
|
||||
out.append(f"{where} · {e}")
|
||||
continue
|
||||
if isinstance(link, dict) and link.get("지역") == "입력" and REGION not in known:
|
||||
out.append(f"{where} · 「{ref}」 는 입력 「{REGION}」 필요")
|
||||
if kind == "field" and part[1][0] == "ref":
|
||||
for ref in _expand(row, part[1][1], master) or []:
|
||||
try:
|
||||
@@ -621,3 +628,7 @@ def find_loops(graph: dict[str, set]) -> list[list[str]]:
|
||||
if node not in state:
|
||||
visit(node, [node])
|
||||
return loops
|
||||
|
||||
|
||||
# 품셈재료 단가 — 서로 부르는 모듈이라 끝에서 읽음
|
||||
from master_material import LINKED, REGION, element # noqa: E402, F401
|
||||
|
||||
Reference in New Issue
Block a user