From 8edd8ad6ee18ea15a5603036e23bc00e1d3c7f74 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 11 Nov 2026 15:00:17 +0900 Subject: [PATCH] =?UTF-8?q?fix(815-1=20K1=C2=B7K5):=20=EA=B3=B5=EC=82=AC?= =?UTF-8?q?=20=EC=A7=81=EC=A0=91=20=EB=B2=88=ED=98=B8(M0000000=E2=80=A6)?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=9E=87=EA=B8=B0=20=EB=BA=8C=20=C2=B7?= =?UTF-8?q?=20=EA=B2=AC=EC=A0=81=EC=9D=80=20=EC=9A=B0=EB=A6=AC=20=EA=B0=92?= =?UTF-8?q?=20=EC=9E=88=EB=8A=94=20=EC=9E=90=EC=9E=AC=EC=9D=BC=20=EB=95=8C?= =?UTF-8?q?=EB=A7=8C=20=EB=B0=94=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EST 공사 파일 자재 239 중 235 = 공사 직접 번호 — 파일마다 다른 품목(거창 치즐(0.7㎥) M0000000001 ↔ 우리 표 Air Hammer) · 이름 길로 - 견적 줄이 값 칸 다섯이 빈 우리 자재(치즐 0.7 MT000011)로 이어지면 0 원 — 견적 값 그대로 + 표지 - 시험 test_815_1 — 공사 직접 번호 · 값 빈 견적 · 거창 전 블록 금액 Co-Authored-By: Claude Opus 5.5 --- M02_MasterTemplete/M02_Elem_Alias.py | 14 ++++++- M02_MasterTemplete/M02_San_Link.py | 5 ++- resources/tester/test_815_1_import_link.py | 47 +++++++++++++++------- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/M02_MasterTemplete/M02_Elem_Alias.py b/M02_MasterTemplete/M02_Elem_Alias.py index 523dade54..26b7c2e37 100644 --- a/M02_MasterTemplete/M02_Elem_Alias.py +++ b/M02_MasterTemplete/M02_Elem_Alias.py @@ -4,7 +4,8 @@ - `keys(표, 이름, 규격)` = 별칭으로 이어지는 키들(풀이 = `master_alias` · 표 LB 인력 · EQ 기계 · MT 재료 · MO 유가) — 부르는 곳: 단가산출 불러오기(`M02_San_Link.m01`) · 1차 줄 판정(`M02_Hopyo_Row.m01_picks`) · 기계 찾기(`M02_Machine_Find.alias`) · 일위대가 호표 줄(`M02_Hopyo_Units`) -- `est_code(코드)` = EST 원파일 자재 자원 코드(M…) ↔ 우리 자재 줄 `출처` 칸 「자재품목 M…」 같은 줄 +- `est_code(코드)` = EST 원파일 자재 자원 코드(M…) ↔ 우리 자재 줄 `출처` 칸 「자재품목 M…」 같은 줄 · + 공사 직접 번호(M0000000…)는 빼고 이름 길 - `exact(이름, 규격)` = 지역 꼬리 뗀 이름 · 규격이 `master_alias.norm` 으로 같은 자재 줄(가까운 규격 아님) - `one(줄, 키들)` = 같은 품목(지역 꼬리 뗀 이름 · 규격 같음)의 지역 줄 여럿 → 줄 지역 › 서울 › 같은 단위 › 키 차례 하나 · 다른 품목이 섞이면 None(후보 여럿) @@ -21,8 +22,10 @@ from M01_MasterData import M01_MasterData_Store as m01 import master_alias as am # noqa: E402 — M01 store 가 scripts 경로를 넣음 MARKET = "MT" +PRICES = ("물가자료", "유통물가", "물가정보", "거래가격", "관급") # 자재 값 칸 다섯 BASE_REGION = "서울" _CODE = re.compile(r"\bM\d{10}\b") +LOCAL = "M0000000" # 공사 직접 번호(파일마다 다른 품목 · 거창 치즐 = 우리 표 Air Hammer) = 코드로 안 이음 _REGION = re.compile(r"\s*,\s*([가-힣]{2,4})\s*$") # 자재 이름 지역 꼬리 「, 서울」 @@ -68,10 +71,17 @@ def keys(table: str, name: Any, spec: Any = "") -> list[str]: return am.match(b["별칭"].get(table, {}), lambda k: (got.get(k) or {}).get("규격"), name, spec) +def priced(key: Any) -> bool: + """우리 자재 줄에 값 칸 다섯 중 하나라도 있음.""" + return any(row(MARKET, key).get(c) is not None for c in PRICES) + + def est_code(code: Any) -> list[str]: """EST 자재 코드(M + 수 열) → 우리 자재 키들(출처 칸 같은 코드).""" text = str(code or "").strip() - return list(book()["코드"].get(text, [])) if _CODE.fullmatch(text) else [] + if not _CODE.fullmatch(text) or text.startswith(LOCAL): + return [] + return list(book()["코드"].get(text, [])) def exact(name: Any, spec: Any) -> list[str]: diff --git a/M02_MasterTemplete/M02_San_Link.py b/M02_MasterTemplete/M02_San_Link.py index 3723088d6..43731d4d8 100644 --- a/M02_MasterTemplete/M02_San_Link.py +++ b/M02_MasterTemplete/M02_San_Link.py @@ -70,13 +70,14 @@ def m01(kind: str, src: dict) -> tuple[str | None, str | None]: def quote(src: dict) -> tuple[str | None, str | None]: """견적 · 운임 · 수수료 꼴 줄(갈래 밖 — EST 견적 · STmate 고정 코드 · 815-1 K5) → 우리 자재에 이어지면 - (코드 · 별칭 · 정확 일치 · 단가 줄일 때) 그 키 + 갈래 재료(우리 값 · 엑셀 값은 검산) · 아니면 그대로 + 표지.""" + (코드 · 별칭 · 정확 일치 · 단가 줄 · 우리 값 있을 때) 그 키 + 갈래 재료(우리 값 · 엑셀 값은 검산) · + 아니면 그대로 + 표지.""" from M02_MasterTemplete import M02_Elem_Alias as ea src["견적"] = True line = {k: src.get(k) or "" for k in ("이름", "규격", "단위")} key, _how = ea.material(line, src.get("코드")) - if key and src.get("비목"): + if key and src.get("비목") and ea.priced(key): # 우리 값 없는 자재 = 견적 값 그대로 src["갈래"] = "재료" return key, None return None, f"{NOTE}{QUOTE}" diff --git a/resources/tester/test_815_1_import_link.py b/resources/tester/test_815_1_import_link.py index 5140a2ed5..9f5677f95 100644 --- a/resources/tester/test_815_1_import_link.py +++ b/resources/tester/test_815_1_import_link.py @@ -91,6 +91,10 @@ def test_est_code_first() -> None: gone = {**src, "코드": "M0000000123", "이름": "치즐(0.7m3)"} # 공사가 넣은 코드 = 이름 길 assert link.m01("재료", gone) == ("MT000011", None) assert ea.est_code("자재품목 M0005250069") == [] and ea.est_code(None) == [] + # 공사 직접 번호 = 파일마다 다른 품목(거창 치즐 ↔ 우리 표 Air Hammer) → 코드로 안 이음 · 이름 길 + assert ea.est_code("M0000000001") == [] + local = {**src, "코드": "M0000000001", "이름": "치즐(0.7㎥)"} + assert link.m01("재료", local) == ("MT000011", None) # ── K2 별칭 → 요소 ─────────────────────────────────────────────────────────── @@ -149,11 +153,18 @@ def test_exact_norm() -> None: # ── K5 견적 ─────────────────────────────────────────────────────────────── def test_quote_line() -> None: - ours = {"꼴": "STmate", "갈래": "고정", "코드": "Q1", "이름": "치즐(0.7m3)", "규격": "", "단위": "EA", "비목": "M", "단가": {"M": 9}} # fmt: skip - assert link.m01("고정", ours) == ("MT000011", None) - assert ( - ours["갈래"] == "재료" and ours["견적"] is True - ) # 우리 자재 = 재료 줄(우리 값 · 엑셀 = 검산) + ours = {"꼴": "STmate", "갈래": "고정", "코드": "Q1", "이름": "치즐(0.6m3)", "규격": "", "단위": "EA", "비목": "M", "단가": {"M": 9}} # fmt: skip + assert link.m01("고정", ours) == ( + "MT000010", + None, + ) # 우리 값 있는 자재 = 재료 줄(우리 값 · 엑셀 = 검산) + assert ours["갈래"] == "재료" and ours["견적"] is True + empty = { + **ours, + "갈래": "고정", + "이름": "치즐(0.7m3)", + } # 우리 줄 값 칸 빔(MT000011) = 견적 값 그대로 + assert link.m01("고정", empty)[0] is None and empty["갈래"] == "고정" keep = {"꼴": "EST", "갈래": "고정", "종류": "견적", "코드": "W0000001", "이름": "도선료", "규격": "8.0ton", "비목": "S"} # fmt: skip key, mark = link.m01("고정", keep) assert key is None and mark == f"{link.NOTE}{link.QUOTE}" and keep["갈래"] == "고정" @@ -176,27 +187,33 @@ def test_quote_money_and_tag() -> None: assert len(notes) == 2 -def test_quote_linked_money_same() -> None: +def test_quote_kept_money_same() -> None: saved = t.native("STC", "소광22") - out = fn.emit(saved, 96) # 구조물헐기 — 치즐(0.7m3) 견적 = 우리 자재 MT000011 + out = fn.emit( + saved, 96 + ) # 구조물헐기 — 치즐(0.7m3) 견적 · 우리 치즐 0.7 값 칸 빔 = 견적 값 그대로 + 표지 hit = [h for h in out["로직"]["호표"] if (h.get("자원") or {}).get("견적")] - assert [(h["종류"], h["이음"]) for h in hit] == [("재료", "MT000011")] + assert [(h["종류"], h["이음"]) for h in hit] == [("로직", None)] + assert any(str(m).endswith(link.QUOTE) for m in out["원본"]["표지"]) _sk, _doc, got = t.doc_and_money(out) want = fn.numbers(saved)[96]["저장"] assert all(abs(float(got[k]) - float(want.get(c) or 0)) < 0.5 for c, k in t.COST) -def test_est_code_block_money() -> None: +def test_est_local_code_block_money() -> None: + """거창 = 자재가 모두 공사 직접 번호(M0000000…) → 이름 길 · 치즐(0.7㎥) = 우리 치즐 0.7(Air Hammer 아님) · + 금액 = 원파일 저장 그대로(전 블록).""" saved = t.native("EST", "거창") - linked = 0 - for no in list(fn.numbers(saved))[:20]: + chisel = set() + for no in fn.numbers(saved): out = fn.emit(saved, no) for h in out["로직"]["호표"]: r = h.get("자원") or {} - if r.get("종류") == "자재" and (hit := ea.est_code(r.get("코드"))): - assert h["이음"] in hit - linked += 1 + if r.get("종류") == "자재": + assert str(r.get("코드")).startswith(ea.LOCAL) or r.get("코드") == "M0004113991" + if r.get("코드") == "M0000000001": + chisel.add(h["이음"]) _sk, _doc, got = t.doc_and_money(out) want = fn.numbers(saved)[no]["저장"] assert all(abs(float(got[k]) - float(want.get(c) or 0)) < 0.5 for c, k in t.COST), no - assert linked >= 2 # 거창 앞 스무 블록 자재 줄 셋 중 코드 같음 둘 + assert chisel == {"MT000011"}