feat(802-4a): M02 단가산출 · 일위대가 · SC · B09 사본 = 수식 문서 사본 — 문서 셈 · 수식 길 · 옮기기 도구 · 대조
- 문서 칸 `수식`(옛 `사본` 은 802-5 까지 옆에) = 값 사본(로직 · 자원 단가 줄) + 부름 닫힌 묶음 + SK 문서 · 끊음 · 따로 — `M02_Doc_Copy`(새)
· 부름 문서 = 값 줄이 지금 M01 줄과 같으면 M01 수식 문서 · 다르면 만든 때 줄 변환
- SK 금액 = 문서 셈(`M02_Doc_Sk.on_run`) — 옛 칸(덮개 · 더한줄 …)은 파생(엔진에 넣던 고친 줄 → 문서 · 덮개 없는 바탕 = M01 문서 사본) · 수식 길 고침 = 따로(옛 칸 고침 저장 409 · 재생성 = 파생으로)
- 일위대가 · SC 로직 줄 = 문서 셈(`M02_Labor_Copy.line`) · 줄 옵션 = 문서 규칙(켜쌓기율 변수 r · 고른 자재 = 요소 키 · 별도계상 = 합밖 + 뒤 「재료비의 %」 밑엔 그 줄 금액 더함)
- [단가 갱신] = 자원 단가 줄만(문서 그대로) · [재생성] = 지금 M01 문서로(고친 문서 · 끊음 버림 알림) · 빈 SK = 빈 문서 「—」
- 수식 길 GET/POST …/sangeun/{키}/doc · /preview · /save(층 쓰기 · 서버 정본 셈) — `M02_Doc_Sk_Path` · `M02_Doc_Router`(새 · main 등록)
- B09 구조물 · 서고 사본 = `수식.json` 함께 굳힘(`M02_Doc_B09` · 원본판 안 듦) · B09 셈 그대로(문서 셈 전환 = 802-6)
- 옮기기 `M02_Doc_Migrate`(보기만 · --apply --backup · --restore) · 목록 `docs/raw/요구_1104/_목록_802-4a_M02대조.xlsx` — 층 SK 15 · 일위대가 21 · SC 4 · B09 서고 사본 3 같음 · 빈 SK 2 = 옛 0 → 「—」 · 가져온 식 1 = 802-4b
- 시험 test_802_4a(12) · test_204_2 서고 사본 파일 목록에 수식.json · test_795_1 `_fake` 가 M01 수식 문서도 같이 바꿈(M01 정본 = 수식 문서 — 옛 줄만 바꾼 흉내는 문서 사본에 안 닿음) · 옛 SK 흉내 = 사본 · 수식 둘 다 뗌
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,11 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Doc_Copy as dc
|
||||
from M02_MasterTemplete import M02_Doc_Sk as dsk
|
||||
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
||||
|
||||
KEY, JOB, PRICE, REGEN = skc.KEY, skc.JOB, skc.PRICE, skc.REGEN
|
||||
@@ -49,7 +52,7 @@ def stamp(doc: dict) -> dict | None:
|
||||
return skc.snap(list(dict.fromkeys(keys)), list(dict.fromkeys(links)))
|
||||
|
||||
|
||||
def _stored(doc: dict) -> dict | None:
|
||||
def _file(doc: dict) -> dict:
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
|
||||
key = str(doc.get("키") or doc.get("열") or "")
|
||||
@@ -57,31 +60,57 @@ def _stored(doc: dict) -> dict | None:
|
||||
try:
|
||||
got = json.loads(store._path(kind, key).read_bytes())
|
||||
except (OSError, ValueError, store.StoreError):
|
||||
return None
|
||||
return {}
|
||||
return got if isinstance(got, dict) else {}
|
||||
|
||||
|
||||
def _stored(doc: dict) -> dict | None:
|
||||
got = _file(doc)
|
||||
return got[KEY] if skc.has(got) else None
|
||||
|
||||
|
||||
def _keys(doc: dict) -> list[str]:
|
||||
"""수식 사본 부름 뿌리 — 로직 줄 키(줄 덮개 · 참조 · 고정 줄은 옛 길)."""
|
||||
lines = [x for x in doc.get("담은로직") or [] if isinstance(x, dict)]
|
||||
return list(dict.fromkeys(str(x["로직"]) for x in lines if x.get("로직")))
|
||||
|
||||
|
||||
def formula(doc: dict, raw: Any, job: Any, file: dict) -> dict | None:
|
||||
"""수식 사본(802-4a) — 값 = 로직 사본 줄 · 부름 = 있던 것(받은 것 › 저장본 · 옛 사본과 같은 차례) ·
|
||||
재생성 = 새로."""
|
||||
copy = doc.get(KEY)
|
||||
if not copy:
|
||||
return None
|
||||
have = raw.get(dc.KEY) if dc.has(raw) else (file.get(dc.KEY) if dc.has(file) else None)
|
||||
seed = None if job == REGEN else (have or {}).get("부름")
|
||||
return dsk.norm(dc.stamp(copy, _keys(doc), seed) or {})
|
||||
|
||||
|
||||
def settle(doc: Any, save: bool = False) -> Any:
|
||||
"""셈 · 저장 첫머리 — 서버가 정한 사본으로 갈음(할일 · 저장 때 찍음 · 새 줄 · 할일 칸 뗌)."""
|
||||
if not isinstance(doc, dict) or _project():
|
||||
return doc
|
||||
job = doc.get(JOB) # 모르는 할일 = 없음
|
||||
out = {k: v for k, v in doc.items() if k not in (KEY, JOB)}
|
||||
copy = skc.taken(doc.get(KEY)) or _stored(doc)
|
||||
out = {k: v for k, v in doc.items() if k not in (KEY, JOB, dc.KEY)}
|
||||
file = _file(doc)
|
||||
copy = skc.taken(doc.get(KEY)) or (file[KEY] if skc.has(file) else None)
|
||||
if job == REGEN or (copy is None and save):
|
||||
copy = stamp(out)
|
||||
elif job == PRICE and copy:
|
||||
copy = skc.refresh(copy, skc.whole_())[0]
|
||||
if save and copy:
|
||||
copy = skc.grow(copy, stamp(out))
|
||||
return {**out, KEY: copy} if copy else out
|
||||
if not copy:
|
||||
return out
|
||||
got = {**out, KEY: copy}
|
||||
return {**got, dc.KEY: formula(got, doc, job, file)}
|
||||
|
||||
|
||||
def keep(raw: Any, out: dict) -> dict:
|
||||
"""정리한 문서에 사본 · 할일 칸을 실어 보냄(꼴 검사는 `settle` 몫)."""
|
||||
if not isinstance(raw, dict):
|
||||
return out
|
||||
got = {k: raw[k] for k in (KEY, JOB) if raw.get(k) is not None}
|
||||
got = {k: raw[k] for k in (KEY, JOB, dc.KEY) if raw.get(k) is not None}
|
||||
return {**out, **got}
|
||||
|
||||
|
||||
@@ -93,6 +122,42 @@ def live(doc: dict) -> Any:
|
||||
return whole if _project() else skc.over(settle(doc), whole)
|
||||
|
||||
|
||||
_LAST: list = [(None, None)] # (문서, 수식 사본) — 한 셈 안 줄마다 settle 안 되풀이
|
||||
|
||||
|
||||
def _formula_of(doc: dict) -> dict | None:
|
||||
have, got = _LAST[0]
|
||||
if have is not doc:
|
||||
got = (settle(doc) or {}).get(dc.KEY)
|
||||
_LAST[0] = (doc, got)
|
||||
return got
|
||||
|
||||
|
||||
def line(whole: Any, doc: dict, one: dict) -> dict[str, Any]:
|
||||
"""로직 줄 기준 — 수식 사본 있는 층 셈 = 문서 셈(줄 옵션 = 문서 규칙 · `dc.line_doc`) ·
|
||||
상세 줄 · 재료 = 옛 엔진(화면 그대로 · 802-4b) · 프로젝트 층 · B09(단가 책) · 사본 없음 = 옛 길."""
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
|
||||
code, rate, picks = one["로직"], one.get("켜쌓기율"), one.get("고름")
|
||||
old = labor.drop_apart(
|
||||
labor.run_line(whole, code, one.get("입력값"), rate, picks), one.get("자재표시")
|
||||
)
|
||||
copy = None if _project() or dsk.OLD.get() else _formula_of(doc)
|
||||
if not copy or getattr(whole, "_tag", None) != (copy.get("판"), copy.get("단가판")):
|
||||
return old
|
||||
base = copy["부름"].get(code)
|
||||
if base is None:
|
||||
return {**old, "기준": None, "까닭": f"수식 문서 없음 「{code}」"}
|
||||
apart = dc.apart_of(one.get("자재표시"), old.get("재료"))
|
||||
given = dc.given_of(whole, code, one.get("입력값"))
|
||||
ans = dc.run(dc.line_doc(base, rate, picks, apart), copy, given, whole)
|
||||
if "결과" in ans:
|
||||
return old
|
||||
money = dc.money(ans)
|
||||
got = {k: Decimal(str(v)) for k, v in money.items()} if money else None
|
||||
return {**old, "기준": got, "까닭": None if got else dc.why(ans)}
|
||||
|
||||
|
||||
def combo(expand: Any, doc: dict, trail: tuple[str, ...]) -> dict[str, Any]:
|
||||
"""사전조합 1 단위 값 — 그 SC 사본 색인으로(층 셈만 · 프로젝트 = 굳힌 단가 그대로)."""
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
|
||||
Reference in New Issue
Block a user