feat(master_data): 품셈재료 고르기 칸 · 로직 재료 줄 일괄 변환 · 후보 값 열 다섯

- `재료_품셈재료.json` 363 줄에 「고르기」 칸 — 자재품목 구분·상세구분[·규격] · 212 줄 채움(규격 조건 19) · 짝 없는 줄은 null + 비고에 까닭
- `scripts/build_품셈재료_고르기.py` — 자재품목을 가른 바로 그 품명 글자 규칙 재사용 · 손으로 바로잡은 25 줄은 OVERRIDE · 연결이 이미 있는 줄은 옛 방식 그대로
- `scripts/build_로직_재료_고르기.py` — 호표 요소 `MP…` 를 고르기 조건으로 바꿈(로직 32 파일 287 줄) · 고르기 빈 줄과 `{이름}` 낀 원문번호 줄은 그대로
- 일괄 시험 계산 전후 — 통과 1119 → 1203 · 새로 멈춤 0 · 앞뒤 다 통과한 1119 줄 금액 변동 0
- `check_master` — 품셈재료 「고르기」 칸 검사(조건 모양 · 없는 구분·상세구분 · 조건 밖 대표 · 후보 0)
- `GET /api/m01/materials` — 후보마다 값 열 다섯(`값들`) · `값` 은 시험 계산이 쓸 낮은 값 · 값 있는 줄을 맨 앞에
- `GET /api/m01/logic` 의 `prices` — 고르기 조건 줄도 실음 · 열쇠는 조건 글 `mf.cond_text` · 후보 수 포함
- `_틀.md` 5장에 고르기 칸 · 일괄 변환 스크립트

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-20 22:02:28 +09:00
co-authored by Claude Opus 5
parent 826495f199
commit 1bcc957e9a
42 changed files with 959 additions and 657 deletions
@@ -92,7 +92,7 @@ ROW_KEYS = {
"중간",
"끝수",
),
"품셈재료": ("", "원문번호", "이름", "규격", "단위", "요구절", "연결"),
"품셈재료": ("", "원문번호", "이름", "규격", "단위", "요구절", "연결", "고르기"),
}
# 한 테이블 파일 — 줄의 갈래 칸 · 머리의 갈래 묶음
MERGED = {"인력": "구분", "기계": "세부분류"}
@@ -312,12 +312,17 @@ def _check_linked(where: str, row: dict) -> list[str]:
def check_links(whole: mf.Master) -> list[str]:
"""끊긴 키 — 품셈재료 연결 · 인력 준용이 가리키는 줄 · 추정 줄 값 = 준용 직종 값."""
"""끊긴 키 — 품셈재료 연결 · 고르기 조건 · 인력 준용이 가리키는 줄 · 추정 줄 값 = 준용 직종 값."""
out = []
for key, row in whole.index.get("MP", {}).items():
link = row.get("연결")
if isinstance(link, str) and link not in whole.index.get(link[:2], {}):
out.append(f"{whole.label(key)} · 연결 「{link}」 이 자재품목·유가전력에 없음")
pick = row.get("고르기")
if isinstance(pick, dict):
out += mf.check_pick(whole, whole.label(key) + " 고르기", pick)
elif pick is not None:
out.append(f"{whole.label(key)} · 고르기 모양 「{pick}」 — 조건 묶음이나 null")
for key, row in whole.index.get("LB", {}).items():
for col in ("준용",):
ref = row.get(col)