feat(795-4): B09 사본 규칙 — 만든 때 그대로 · [단가 갱신] 값만 · [재생성] 따로 · 시점 섞임 고침

- 구조물 · 서고 사본 단가 묶음 = M02 일위대가 · SC · SK 로직 사본 줄 먼저(`collect(over)`) · M01 에서 지워진 키 = `사본_남은줄.json`
- 단가 책 새 줄([저장] · 첫 굳힘) = 연결 사본 `단가.json` 자원 줄(구조물 지정 때 값 · `Linked.frozen_rows`) · 없는 자원만 지금 M01 + 책 `now_keys` · 입력 `now` · 알림 한 줄
- POST …/estimation/prices 기본 = 단가 갱신(값만 · 사본 재적용 없음) · job 재생성 = 옛 [업데이트] · 미리 보기 단가 갱신 = 바뀐 자원 · 총괄 · 대가 합 전 → 후
- B09 화면 단추 [업데이트] → [단가 갱신] · 미리 보기 창 [재생성] → 재생성 미리 보기 → [적용] · 안내 글 둘 이름 맞춤
- 옮기기 도구(보기만) — 프로젝트 층 못셈 14 = 「못셈(옛 SP 참조)」 갈래 · B09 구조물 · 서고 사본 대조(그대로 · 재생성 금액)
- 새 시험 test_795_4_b09_copy(8) · 낡은 시험 고침: test_203_1 · test_296 = 단추 이름 「업데이트」 · 호버 글 · 몸 `{ user }` 를 박아 둔 것 → 795-4 이름 · job 덧칸으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-07 11:54:10 +09:00
co-authored by Claude Opus 5.5
parent 16d7eede50
commit 3e1dc294b2
14 changed files with 560 additions and 99 deletions
+47 -1
View File
@@ -1,4 +1,6 @@
"""B09 [업데이트] 한 곳(PLAN 703-3) — 단가 책 다시 굳힘 + 원본이 바뀐 구조물 사본 [재적용]을 함께.
"""B09 [단가 갱신] · [재생성](PLAN 795-4 · 옛 [업데이트] 703-3) — 사본은 만든 때 그대로 ·
[단가 갱신](기본) = 단가 책만 지금 M01 값 · 미리 보기 `price_preview` = 바뀐 자원 · 총괄 전 → 후 ·
[재생성](사용자가 따로) = 아래 옛 [업데이트] — 단가 책 다시 굳힘 + 원본이 바뀐 구조물 사본 [재적용].
미리 보기(`preview`) = 바뀌는 단가 줄(새 줄 + 바뀐 줄 · 지금 수요) · 다시 가져올 구조물 사본
(연결된 것 중 원본이 바뀐 것만 · 원본 없음 = 그대로) · 지킬 고친 칸(덮개 칸 수 · 702-4 —
@@ -12,6 +14,7 @@ M02 단가산출(SK · 719-2) — 구조물 · 서고 사본의 굳힌 SK 를
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
@@ -23,6 +26,49 @@ from M02_MasterTemplete import M02_Item_Sangeun as item_sk
from M02_MasterTemplete.M02_Item_Snapshot import MANIFEST_NAME, linked_snapshots, structures_dir
from M02_MasterTemplete.M02_Item_Source import LABOR_FILE, build_source, read_json
REGEN = "재생성" # POST …/estimation/prices · preview 몸 `job`(795-4)
def _value(row: dict[str, Any] | None) -> Any:
return None if row is None else row.get("price", row.get("prices"))
def price_preview(
old: dict[str, Any] | None, new: dict[str, Any], before: dict, after: dict, cells: int
) -> dict[str, Any]:
"""[단가 갱신] 미리 보기(795-4) — {할일, 바뀐자원 [{키, 이름, 전, 후}], 계수, 환율, 전, 후
(총괄 grand · 빈 줄 있으면 None), 대가 {전, 후}(일위대가 블록 합), 고친칸, 노임판} ·
사본(로직)은 안 바뀜."""
diff = prices.book_diff(old, new)
was = json.loads(json.dumps((old or {}).get("rows") or {}, ensure_ascii=False))
now = json.loads(json.dumps(new["rows"], ensure_ascii=False))
rows = [
{"키": key, "이름": row.get("name") or "", "전": _value(was.get(key)), "후": _value(row)}
for key, row in now.items()
if was.get(key) != row
]
def grand(sheets: dict) -> Any:
return (((sheets.get("total") or {}).get("subtotals")) or {}).get("grand")
def daega(sheets: dict) -> float:
return sum(float(b.get("sum") or 0) for b in sheets.get("daega_blocks") or [])
return {
"할일": "단가갱신",
"바뀐자원": rows,
"계수": diff["factor"],
"환율": diff["fx"],
"전": grand(before),
"후": grand(after),
"대가": {"전": daega(before), "후": daega(after)},
"고친칸": cells,
"노임판": {
"전": edition_text((old or {}).get("labor_edition")),
"후": edition_text(new.get("labor_edition")),
},
}
def changed_copies(root: str | Path) -> list[dict[str, Any]]:
"""연결된 구조물 사본 중 원본(층 · 마스터)이 바뀐 것 `[{폴더, 이름}]`."""