feat(axis): 품셈 판 pum_edition 칸 — 자원·공종 쪽, 판이 다르면 값을 안 씀

공종 마스터·자원 축 줄·B08 인계 줄마다 공종 코드가 본 판을 실음(명세 17장 규칙 3).
판이 다른 인계 줄은 내역서에서 「확인 대기」로 빠지고, 코드에 박힌 절 번호(합산 단계·표 형태 판정·가드 목록)는
마스터 판과 다르면 마스터 빌드·단가 조립을 멈춤(규칙 4). 판 기준은 common_util_pum_edition 한 곳.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
2026-09-13 22:41:47 +09:00
co-authored by Claude Opus 5
parent d1958d067b
commit 66d57bb630
11 changed files with 145 additions and 5 deletions
@@ -583,6 +583,10 @@ def basis_quantity_is_grouped(quantity: float | None) -> bool:
def build() -> dict[str, Any]:
data = json.loads(SOURCE.read_text(encoding="utf-8"))
# 사람 판정 선언(표 형태·부모 모양)은 절 번호로 적혀 판에 묶임 — 판이 다르면 멈춤(명세 17장).
from common_util.common_util_pum_edition import require_code_edition
require_code_edition(data["effective_date"], "공종 마스터 빌드 — 표 형태·부모 모양 판정")
tables = data["variables"]["pum"]["tables"]
# 원문을 함께 연다 — 밑수가 표 밖(본문)에 있기 때문이다. 못 열면 밑수 없이 간다.
source_lines: list[str] = []
@@ -711,6 +715,8 @@ def build() -> dict[str, Any]:
"schema_version": SCHEMA_VERSION,
"dataset_id": "work_item_master_forest",
"effective_date": data["effective_date"],
# 공종 코드(`FP-*` 절 번호)가 묶인 품셈 판 — 자원 축·인계 줄이 이 값을 싣는다.
"pum_edition": data["effective_date"],
"generated_at": datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds"),
"dataset_version": src_meta,
"policy": {
@@ -231,6 +231,10 @@ def build_handoff(
(unit_quantity_table or {}).get("collected_stone_deduction_m3") or 0.0
)
result["placing_notes"] = placing_notes
# ⚠ 품셈 판(명세 17장) — 줄마다 코드가 본 판을 싣는다. 판이 다르면 B09 가 값을 안 씀.
result["pum_edition"] = table.pum_edition or None
for row in work_items:
row.setdefault("pum_edition", table.pum_edition or None)
return result
@@ -192,6 +192,8 @@ class WorkItemMapping:
"""수량 줄 → 공종 마스터 코드. 못 찾으면 `None` 을 돌려주고 부른 쪽이 목록에 남긴다."""
effective_date: str = ""
#: 매핑의 공종 코드가 본 품셈 판(명세 17장) — 인계 줄마다 이 값이 `pum_edition` 으로 실린다.
pum_edition: str = ""
earthwork: list[dict[str, Any]] = field(default_factory=list)
haul: list[dict[str, Any]] = field(default_factory=list)
structure: list[dict[str, Any]] = field(default_factory=list)
@@ -284,6 +286,7 @@ def load_mapping(path: Path | None = None) -> WorkItemMapping:
variant_aliases = [row for row in load_aliases("variant") if row["pum_edition"] == edition]
return WorkItemMapping(
effective_date=str(payload.get("effective_date") or ""),
pum_edition=edition,
earthwork=list(payload.get("earthwork") or []),
haul=list(payload.get("haul") or []),
structure=list(payload.get("structure") or []),