fix(815-1 K1·K5): 공사 직접 번호(M0000000…) 코드 잇기 뺌 · 견적은 우리 값 있는 자재일 때만 바꿈

- EST 공사 파일 자재 239 중 235 = 공사 직접 번호 — 파일마다 다른 품목(거창 치즐(0.7㎥) M0000000001 ↔ 우리 표 Air Hammer) · 이름 길로
- 견적 줄이 값 칸 다섯이 빈 우리 자재(치즐 0.7 MT000011)로 이어지면 0 원 — 견적 값 그대로 + 표지
- 시험 test_815_1 — 공사 직접 번호 · 값 빈 견적 · 거창 전 블록 금액

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-11 15:00:17 +09:00
co-authored by Claude Opus 5.5
parent 030a843f07
commit 8edd8ad6ee
3 changed files with 47 additions and 19 deletions
+12 -2
View File
@@ -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]:
+3 -2
View File
@@ -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}"
+32 -15
View File
@@ -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"}