feat(828-2): STmate 엑셀 일위대가표 숨은 칸 → 구성 줄 — 읽는 곳 하나 · 율 줄 · 닫기 · 일식 · 별도계상 · 판에 일위 재료

- M02_San_Xlsx_Ilwi(새) = 일위대가표 읽기 한 곳 — N 열 메타(COD · IPR · PRI · PRO · EQC · KWN · DVD) · O 열 차수 꼬리 기준 · 줄 꼴 = 828-1 M02_San_Ilwi 꼴 + 엑셀 칸(행 · 차수 · 비고 · 전체 · 율.코드 · 메타) · 숨은 칸 없음 = 옛 코드 찾기 · 단산 길 일위 빈 목록 + 표지
- 5 건 호표 136 · 줄 706 · STC 구성 줄과 코드 · 갈래 · 수량 · 율 · 밑줄 같음(다른 5 줄 = STC 별도계상 규칙이 비고 별산 표시를 놓침)
- 호표 길(M02_Hopyo_Stmate._hopyos) · 단산 길(M02_San_Xlsx_Stmate.read 일위) · 단서(M02_San_MatFind.from_book) 모두 공용 읽기 · 단서 · 산근 · 단가 전후 같음
- M02_Hopyo_Fixed._ratio = 숨은 칸 율 밑 · 대상 비목 먼저(글 짐작 대신) — 호표 길 전후 바뀜 = 영덕 제11호표 산악할증 0% 한 줄(금액만 → 비율 노무비 · 금액 0 그대로) · 단산 항목 1564 판정 그대로
- 재료 판(census) = 일위 구성 줄 자재도 셈(M02_San_MatFind.ilwi_found · 종 칸 일위 = 호표 코드 · 화면 「일위대가 N 호표」)
- 시험: 새 test_828_2_xlsx_ilwi_meta · test_827_2 단서 시트 보기에 「제N호표」 줄 넣음(실제 시트는 늘 있음 · 공용 읽기가 호표 머리부터 셈)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 20:39:15 +09:00
co-authored by Claude Opus 5.5
parent 53932e964f
commit 109789a746
9 changed files with 466 additions and 88 deletions
+12 -6
View File
@@ -59,13 +59,19 @@ def _parts(money: Any) -> dict[str, Decimal]:
def _ratio(line: dict[str, Any], others: list[dict[str, Any]]) -> dict[str, str] | None:
"""% 줄 → `{기준, 비목}` — 밑수 = 다른 줄 엑셀 금액[기준] 합(끝수 1 원 안) · 금액 비목 하나."""
hit = _BASE.search(f"{line.get('규격') or ''} {line.get('이름') or ''}")
"""% 줄 → `{기준, 비목}` — 밑수 = 다른 줄 엑셀 금액[기준] 합(끝수 1 원 안) · 금액 비목 하나 ·
STmate 숨은 칸 율(`율` {밑, 대상} · 828-2)이 있으면 글 「노무비의」 · 금액 칸 짐작 대신 그것."""
under = _d((line.get("단가") or {}).get("계"))
paid = [k for k, v in _parts(line.get("금액")).items() if v]
if not hit or under is None or len(paid) != 1:
return None
base = "노무비" if hit.group(1) == "인건비" else hit.group(1)
if rate := line.get("율"):
base, paid = rate.get("밑"), [rate.get("대상")]
if base not in COSTS or paid[0] not in COSTS or under is None:
return None # 전체(닫기 뒤) 율 · 빈 칸 = 금액만
else:
hit = _BASE.search(f"{line.get('규격') or ''} {line.get('이름') or ''}")
paid = [k for k, v in _parts(line.get("금액")).items() if v]
if not hit or under is None or len(paid) != 1:
return None
base = "노무비" if hit.group(1) == "인건비" else hit.group(1)
total = sum((_parts(x.get("금액"))[base] for x in others), Decimal(0))
return {"기준": base, "비목": paid[0]} if abs(total - under) <= GAP else None