From cdbfbb7e3f184dd2d23fefbf24e52bb2cf0329c1 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 20 Sep 2026 23:10:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(master=5Fdata):=20=EA=B4=80=EA=B2=BD?= =?UTF-8?q?=EC=9D=B4=20=EB=93=A0=20=EC=9E=AC=EB=A3=8C=20=EC=B0=B8=EC=A1=B0?= =?UTF-8?q?=EB=A5=BC=20=EA=B3=A0=EB=A5=B4=EA=B8=B0=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=C2=B7=20=EC=A1=B0=EA=B1=B4=20=EA=B0=92?= =?UTF-8?q?=EC=9D=98=20=EC=9E=85=EB=A0=A5=20=EC=9E=90=EB=A6=AC=20=EC=B1=84?= =?UTF-8?q?=EC=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 엔진 — 고르기 조건 값의 `{입력}` 을 계산 때 채움(관경) · 검사에서는 그 칸을 뺌(`static`) - 흄관 · 파형강관 두 줄을 고르기 조건으로 바꿈 - VR관(자재품목 단위 「본」 · 호표 「m」 환산 필요) · 고무링 · 커플링밴드 셋은 옛 참조 유지 — 조건만으로 줄이 안 좁혀짐 - 시험 하나 더함 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj --- resources/master_data/scripts/master_formula.py | 8 +++++++- resources/master_data/scripts/master_material.py | 10 +++++++++- .../master_data/로직_산림품셈_12장_철근콘크리트.json | 12 ++++++++++-- resources/tester/test_masterdata_check.py | 9 +++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/resources/master_data/scripts/master_formula.py b/resources/master_data/scripts/master_formula.py index 0d2a9649..ae49df15 100644 --- a/resources/master_data/scripts/master_formula.py +++ b/resources/master_data/scripts/master_formula.py @@ -479,7 +479,12 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict: ) else: # 고르기 조건 묶음은 그대로 넘김 — `element` 가 대표 줄·첫 줄로 풂 - ref = item["요소"] if isinstance(item["요소"], dict) else fill(item["요소"], env) + # 조건 값의 `{입력}`(관경 등)도 채움 — 규격이 입력 따라 바뀌는 관 줄 + ref = ( + {k: fill(v, env) if isinstance(v, str) else v for k, v in item["요소"].items()} + if isinstance(item["요소"], dict) + else fill(item["요소"], env) + ) try: price, source = element(master, ref, env, item.get("단위")) except FormulaError: @@ -704,6 +709,7 @@ from master_material import ( # noqa: E402, F401 candidates, check_pick, check_unit, + static, cond_text, element, pick, diff --git a/resources/master_data/scripts/master_material.py b/resources/master_data/scripts/master_material.py index d66d3f59..96736bcf 100644 --- a/resources/master_data/scripts/master_material.py +++ b/resources/master_data/scripts/master_material.py @@ -106,6 +106,11 @@ def _candidates(master: mf.Master, ref: str, row: dict, env: dict) -> list[str]: # ── 재료 고르기 ──────────────────────────────────────────────────────── +def static(cond: dict) -> dict: + """검사용 조건 — 값에 `{입력}` 이 든 칸은 뺌(계산 때 채워짐).""" + return {k: v for k, v in cond.items() if not (isinstance(v, str) and "{" in v)} + + def cond_text(cond: dict) -> str: """고르기 조건 한 글자 열쇠 — 「구분|상세구분|규격|대표」 · 화면 단가 미리보기가 줄을 찾는 이름.""" return "|".join(str(cond.get(k) or "") for k in PICK_KEYS) @@ -147,6 +152,7 @@ def pick(master: mf.Master, cond: dict, env: dict, unit: str | None = None) -> s def check_unit(master: mf.Master, where: str, cond: dict, unit: str | None) -> list[str]: """단위 경고 — 호표 줄·품셈재료 줄 단위와 맞는 후보가 없거나 대표 줄 단위가 다름. 틀린 데이터가 아니라 좁혀야 할 조건이라 `check_pick` 과 따로 봄(`check_master 단위`).""" + cond = static(cond) rows = master.index.get(MARKET, {}) rep = str(cond.get("대표") or "") if not unit or set(cond) - set(PICK_KEYS) or not cond.get("구분"): @@ -163,9 +169,11 @@ def check_unit(master: mf.Master, where: str, cond: dict, unit: str | None) -> l def check_pick(master: mf.Master, where: str, cond: dict, unit: str | None = None) -> list[str]: - """고르기 조건 검사 — 칸 이름 · 구분·상세구분이 자재품목에 있는지 · 대표 줄 · 후보 0.""" + """고르기 조건 검사 — 칸 이름 · 구분·상세구분이 자재품목에 있는지 · 대표 줄 · 후보 0. + 값에 `{입력}` 이 든 칸(관경 등)은 계산 때 채워지므로 검사에서 뺌.""" if set(cond) - set(PICK_KEYS) or not cond.get("구분"): return [f"{where} · 재료 고르기 조건 모양 「{cond}」 — {' · '.join(PICK_KEYS)}"] + cond = static(cond) out, rows = [], master.index.get(MARKET, {}) for slot in ("구분", "상세구분"): if cond.get(slot) and not any(r.get(slot) == cond[slot] for r in rows.values()): diff --git a/resources/master_data/로직_산림품셈_12장_철근콘크리트.json b/resources/master_data/로직_산림품셈_12장_철근콘크리트.json index 5e65b110..4630a5dc 100644 --- a/resources/master_data/로직_산림품셈_12장_철근콘크리트.json +++ b/resources/master_data/로직_산림품셈_12장_철근콘크리트.json @@ -1989,7 +1989,11 @@ }, { "종류": "재료", - "요소": "MP:흄관 ∅{관경}㎜", + "요소": { + "구분": "관·이음관", + "상세구분": "흄관", + "규격": "{관경}" + }, "이름": "흄관", "단위": "m", "수량": "찾기(QF000371, 구분='흄관', 관경=관경).적용", @@ -2133,7 +2137,11 @@ "호표": [ { "종류": "재료", - "요소": "MP:파형강관 ∅{관경}㎜", + "요소": { + "구분": "관·이음관", + "상세구분": "파형강관", + "규격": "{관경}" + }, "이름": "파형강관", "단위": "m", "수량": "찾기(QF000373, 구분='파형강관', 관경=관경).적용", diff --git a/resources/tester/test_masterdata_check.py b/resources/tester/test_masterdata_check.py index 9d67572e..baebd194 100644 --- a/resources/tester/test_masterdata_check.py +++ b/resources/tester/test_masterdata_check.py @@ -952,3 +952,12 @@ def test_단위_경고는_로직_검사와_따로(): assert cm.check_logics(files, whole) == [] warned = cm.check_units(files, whole) assert warned and all("단위" in x for x in warned) + + +def test_고르기_조건의_입력_자리_채움(whole): + """관경 같은 입력이 규격에 든 고르기 조건 — 계산 때 채워지고, 검사에서는 그 칸을 뺌.""" + cond = {"구분": "관·이음관", "상세구분": "파형강관", "규격": "{관경}"} + assert mm.static(cond) == {"구분": "관·이음관", "상세구분": "파형강관"} + assert mf.check_pick(whole, "시험", cond) == [] + 채움 = {k: mf.fill(v, {"관경": Decimal(800)}) for k, v in cond.items()} + assert mm.candidates(whole, 채움, None, "m")