feat(830-10): M01 재료 찾기가 지역 칸을 봄 — 로직 1,520 시험 계산 전후 다름 0

- master_material.label = 이름 + 지역 칸 · 검색어 지역 낱말 · 자재지역 입력 · 고르는 차례(master_pick)가 이름 글 대신 지역 칸과 맞대짐
- 로직 1,520: 같음 1,517 · 전부터 멈춤 3(GC001199 ~ 1201) — 지역 칸만 고쳤을 때 다르던 11(시멘트 전국 · 포항 모래 · 포항 레미콘)이 원래 줄로
- 계산 줄 품목 칸에 지역 · 상하차 · M01 고르기 창(가격 연결 · 로직 재료 후보) 글에 지역 · 상하차(모름 「-」 뺌)
- M01 표 찾기(Store_Find) 낱말이 지역 칸에도 맞음 — 「레미콘 서울」 · 저장 뒤 찾기 기억 지움도 지역 칸 포함
- 새 줄 지역 기본 「-」 · test_m01_pick 합판 서울 기대를 지역 칸으로
- check_master 틀 · 본문 · 로직 · 조합 · 목차 0 · 단위 27 · 배 7(전과 같음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-14 20:42:29 +09:00
co-authored by Claude Opus 5.5
parent 86cf20d997
commit 98f3ab59cb
11 changed files with 49 additions and 22 deletions
@@ -44,6 +44,12 @@ def plain(s) -> str:
return re.sub(r"\s+", "", re.sub(r"[φΦ∅⌀]", "ø", s))
def label(row: dict) -> str:
"""맞대기 글 = 이름 + 지역 칸(요구 839 · 830-10) — 검색어 · 자재지역의 지역 낱말이 지역 칸과 맞대짐."""
where = row.get("지역")
return plain(row.get("이름")) + ("" if where in (None, "", "-") else plain(where))
def plain_spec(s) -> str:
return plain(s).replace("×", "x").replace("*", "x")
@@ -67,14 +73,14 @@ def _lowest(master: mf.Master, key: str) -> tuple[object, str | None]:
def _market_index(master: mf.Master) -> list[tuple[str, str, str, str, str]]:
"""(품명 뿌리, 이름, 규격, 단위, 키) — 「레미콘(관급)-경상북도(울진군)」 뿌리 = 레미콘."""
"""(품명 뿌리, 이름 + 지역, 규격, 단위, 키) — 「레미콘(관급)」 뿌리 = 레미콘 · 지역 = 지역 칸(`label`)."""
if not hasattr(master, "_market"):
master._market = []
for key, row in master.index.get("MT", {}).items():
name = plain(row.get("이름"))
root = re.sub(r"\(.*?\)", "", name.split(",")[0]).split("-")[0]
master._market.append(
(root, name, plain_spec(row.get("규격")), plain_unit(row.get("단위")), key)
(root, label(row), plain_spec(row.get("규격")), plain_unit(row.get("단위")), key)
)
return master._market
@@ -99,7 +105,7 @@ def _tariff(master: mf.Master, label_ref, cond: dict, env: dict) -> list[str]:
def _region(master: mf.Master, label_ref, cond: dict, env: dict, unit) -> list[str]:
"""후보 조건{이름 · 규격 · 지역: 입력} — 뿌리가 이름으로 끝나고 이름에 지역 글자 · 단위 같음."""
"""후보 조건{이름 · 규격 · 지역: 입력} — 뿌리가 이름으로 끝나고 이름 · 지역 칸에 지역 글자 · 단위 같음."""
if cond.get("지역") == "입력" and REGION not in env:
raise mf.FormulaError(f"「{master.label(label_ref)}」 입력 「{REGION}」 없음")
region = plain(env.get(REGION, "")) if cond.get("지역") == "입력" else ""
@@ -149,7 +155,7 @@ def candidates(
) -> list[str]:
"""고르기 조건 안 자재품목 키 — 구분 · 상세구분 · 이름 · 규격 낱말이 모두 든 줄 · 파일 차례대로.
`unit` 을 주면 그 단위(호표 줄 단위)와 같은 줄만 — 단위가 다르면 다른 물건.
입력 `자재지역` 이 있으면 이름에 그 지역이 든 줄만(그런 줄이 없으면 조건 안 전부).
입력 `자재지역` 이 있으면 이름 · 지역 칸에 그 지역이 든 줄만(그런 줄이 없으면 조건 안 전부).
조건에 `검색어` 가 있으면 기본 검색어 찾기(`master_pick`) ·
빈 검색어 = 자재표에 없는 품목(후보 없음)."""
if mp.empty_term(cond):
@@ -169,7 +175,7 @@ def candidates(
and (not need or plain_unit(row.get("단위")) == need)
]
region = plain((env or {}).get(REGION, ""))
near = [k for k in hits if region in plain(master.get(k).get("이름"))] if region else []
near = [k for k in hits if region in label(master.get(k))] if region else []
return near or hits
+3 -2
View File
@@ -103,7 +103,7 @@ def _search(master, cond: dict, env: dict | None = None, unit: str | None = None
def rank(key):
one = rows[key]
name = mm.plain(one.get("이름"))
name = mm.label(one) # 이름 + 지역 칸(830-10)
return (
not mm._offers(master, key),
bool(want) and mm.plain_unit(one.get("단위")) != want,
@@ -123,7 +123,7 @@ 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(".")
@@ -137,6 +137,7 @@ def priced(master, item: dict, ref, env: dict, index: int | None):
"이름": row.get("이름"),
"규격": row.get("규격"),
"단위": row.get("단위"),
**{c: row[c] for c in ("지역", "상하차") if c in row}, # 830-10
"값칸": slot,
}
return price, source, why, extra