"""품셈 판(`pum_edition`) — `FP-*` 참조가 **어느 판을 보고 적혔나** (명세 17장 규칙 3·4). `FP-*` 는 절 번호라 **판 안에서만 유일**하다. 판이 바뀌면 같은 번호가 다른 공종일 수 있다. 공종 마스터 `effective_date` = 그 판 자원 축 줄 · 인계 줄 `pum_edition` 칸 — 그 줄이 본 판 코드에 박힌 절 번호 `CODE_PUM_EDITION` 한 곳 — 합산 단계·표 형태 판정·표 읽기 선언·가드 목록 ⛔ 판이 다르면 **값을 쓰지 않는다** — 줄은 「확인 대기」로 세우고, 코드에 박힌 선언은 조립을 멈춘다. 조용히 이웃 절에 붙으면 품이 통째로 달라진다(규칙 4). 판을 넘어 잇는 것은 대응표 몫이다. ⚠ 라이브러리 항목의 `pum_edition` 은 랩탑 서브가 붙였다(구조물도 양식). """ from __future__ import annotations #: 코드 안에 박힌 `FP-*` 가 본 판 — 산림사업 표준품셈(산림청고시 제2025-82호, 2026-01-01 시행). #: ⚠ 판을 올리는 날 이 값만 바꾸지 말 것 — 박힌 선언을 대응표로 옮긴 **뒤에** 바꿀 것. CODE_PUM_EDITION = "2026-01-01" class PumEditionError(RuntimeError): """코드에 박힌 절 번호의 판과 공종 마스터의 판이 다를 때. 값을 쓰지 않고 멈춘다.""" def require_code_edition(master_edition: str, where: str) -> None: """코드에 박힌 선언을 쓰기 전에 부른다 — 판이 다르면 멈춘다.""" if str(master_edition) != CODE_PUM_EDITION: raise PumEditionError( f"{where}: 코드에 박힌 절 번호는 {CODE_PUM_EDITION} 판을 보고 적혔는데 공종 마스터는 " f"{master_edition or '(판 없음)'} 판입니다 — 판 대응표로 옮기기 전에는 쓰지 않습니다 " "(명세 17장 규칙 4)" ) def edition_waiting_reason(row_edition: str | None, master_edition: str) -> str: """판이 다른 줄의 「확인 대기」 사유. 어느 한쪽이 판을 안 적었거나 같으면 빈 글.""" if not row_edition or not master_edition or str(row_edition) == str(master_edition): return "" return ( f"품셈 판 확인 대기 — 이 줄의 공종 코드는 {row_edition} 판," f" 공종 마스터는 {master_edition} 판 · 절 번호가 같아도" " 같은 공종이라는 보장이 없어 값을 안 씀(명세 17장)" )