From 2e9d457f9433b12634a037b75363a6fea54f7409 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 24 Sep 2026 22:04:30 +0900 Subject: [PATCH] =?UTF-8?q?feat(M01):=20/calc=20=EC=9E=AC=EB=A3=8C=20?= =?UTF-8?q?=EC=A4=84=EB=A7=88=EB=8B=A4=20=ED=92=88=EB=AA=A9=20=EC=B9=B8(?= =?UTF-8?q?=ED=82=A4=C2=B7=EC=9D=B4=EB=A6=84=C2=B7=EA=B7=9C=EA=B2=A9=C2=B7?= =?UTF-8?q?=EB=8B=A8=EC=9C=84=C2=B7=EA=B0=92=EC=B9=B8)=20=EB=8D=94?= =?UTF-8?q?=ED=95=A8=20(PLAN=208-5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj --- .../master_data/scripts/master_formula.py | 6 ++--- resources/master_data/scripts/master_pick.py | 22 ++++++++++++++++++- resources/tester/test_m01_pick.py | 16 ++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/resources/master_data/scripts/master_formula.py b/resources/master_data/scripts/master_formula.py index 8f6ac76e..83cc99f5 100644 --- a/resources/master_data/scripts/master_formula.py +++ b/resources/master_data/scripts/master_formula.py @@ -550,7 +550,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict: sums = dict.fromkeys(COST_ITEMS, Decimal(0)) lines = [] for n, item in enumerate(row.get("호표", [])): - source = why = warn = None + source = why = extra = None try: qty = evaluate(parse(item["수량"]), env, master, depth) except EmptyInput as e: # 비어 있는 입력을 쓰는 줄만 비움 @@ -584,7 +584,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict: # (없는 키는 틀린 로직 — 지금처럼 멈춤) · 검색어 · 고름 · 단위 환산은 `master_pick` if isinstance(ref, str) and qty != 0: master.get(ref) - price, source, why, warn = mp.priced(master, item, ref, env, n if depth == 0 else None) + price, source, why, extra = 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} @@ -600,7 +600,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 {}), + **(extra or {}), } ) # 덧줄이 호표 줄 금액 하나를 빼고 더할 수 있게 — `줄.'호표 줄 이름'`(빈 줄은 뺌) diff --git a/resources/master_data/scripts/master_pick.py b/resources/master_data/scripts/master_pick.py index 185943f9..445af9d9 100644 --- a/resources/master_data/scripts/master_pick.py +++ b/resources/master_data/scripts/master_pick.py @@ -129,7 +129,27 @@ def pick(master, cond: dict, env: dict, unit: str | None = None) -> str: def priced(master, item: dict, ref, env: dict, index: int | None): """재료 줄 단가 — (단가, 출처, 까닭, 단위경고). 고른 품목(`고름`)이 있으면 그것 · 검색어가 있으면 찾기 · - 없으면 옛 길. 자재 단가는 줄 단위로 환산 · 성격이 다르면 그대로 두고 경고.""" + 없으면 옛 길. 자재 단가는 줄 단위로 환산 · 성격이 다르면 그대로 두고 경고. + 넷째 = 줄에 덧붙일 칸 — `단위경고` · `품목`(잡힌 품목 키 · 이름 · 규격 · 단위 · 값 칸).""" + price, source, why, warn = _priced(master, item, ref, env, index) + extra = {"단위경고": warn} if warn else {} + key, _, slot = (source or "").partition(".") + try: + row = master.get(key) if key else None + except mm.mf.FormulaError: + row = None + if row: + extra["품목"] = { + "키": key, + "이름": row.get("이름"), + "규격": row.get("규격"), + "단위": row.get("단위"), + "값칸": slot, + } + return price, source, why, extra + + +def _priced(master, item: dict, ref, env: dict, index: int | None): unit = item.get("단위") picked = (CHOSEN.get() or {}).get(str(index)) if index is not None else None try: diff --git a/resources/tester/test_m01_pick.py b/resources/tester/test_m01_pick.py index f911987b..b891cd4e 100644 --- a/resources/tester/test_m01_pick.py +++ b/resources/tester/test_m01_pick.py @@ -127,3 +127,19 @@ def test_기계_입력은_EQ_키도_원문번호로_풀어_받음(client) -> Non assert by_key["sums"] == by_number["sums"] # EQ 키 = 원문번호와 같은 계산 bad = client.post("/api/m01/calc", json={"key": key, "inputs": {**base, "기계": "EQ999999"}}) assert bad.json()["ok"] is False + + +def test_재료_줄마다_품목_칸(client) -> None: + key = "GF000160" + body = {"key": key, "inputs": {k: str(v) for k, v in store.auto(key)["값"].items()}} + lines = client.post("/api/m01/calc", json=body).json()["lines"] + idx = next( + i for i, x in enumerate(lines) if x.get("품목", {}).get("이름", "").startswith("합판(내수)") + ) + first = lines[idx]["품목"] + assert first["이름"] == "합판(내수), 서울" and first["값칸"] + assert {"키", "이름", "규격", "단위", "값칸"} <= set(first) + lines = client.post("/api/m01/calc", json={**body, "고름": {str(idx): "MT000019"}}).json()[ + "lines" + ] + assert lines[idx]["품목"]["키"] == "MT000019" and "인천" in lines[idx]["품목"]["이름"]