"""사본 = 셈이 실제로 읽은 줄만(PLAN 826-3 · 요구 806 · 808 · 조사 804 5 절) — 서버 단독. [저장] 관문(`M02_MasterTemplete_Store._server` — SK · 일위대가 · 사전조합 · 층 복사 · 열 복사)에서: `saved` 동안 사본 셈 색인(`skc.Copy.get`)이 내준 줄 키를 그 색인의 사본 줄 묶음별로 적음 (`skc.READS`) → 문서 사본(`사본` · `수식` 값 몫)을 이 문서 셈이 읽은 줄만 남김 · 읽었는데 사본에 없는 줄(옵션 · 기계 · 지역을 바꿔 새로 읽은 줄) = 지금 M01(+ 층 내 기계) 값으로 더함. 이 문서 셈 = 사본 줄 묶음이 이 문서 사본 안에 드는 색인(남의 SK · SC 참조 셈 = 제 사본 → 안 셈). 파일 꼴 그대로 — 옛 코드는 사본에 없는 줄을 지금 M01 로 읽음(지금 옵션에서 안 읽는 줄 = 금액 같음). 읽은 줄 없음(셈 안 돎) · 프로젝트 층 = 그대로 · 이미 저장된 파일은 다음 [저장] 때 줄어듦. """ from __future__ import annotations from typing import Any, Callable from M02_MasterTemplete import M02_Doc_Copy as dc from M02_MasterTemplete import M02_Item_Prices as prices from M02_MasterTemplete import M02_MyMachine as mym from M02_MasterTemplete import M02_Sangeun_Copy as skc def _keys(rows: list[dict]) -> set[str]: return {str(r.get("키")) for r in rows} def cut(copy: Any, reads: dict, more: Any = ()) -> Any: """값 사본(`skc` 꼴) → 이 사본 셈이 읽은 줄만 + 읽은 · 사본 없는 줄(지금 값) · 판은 바뀐 몫만 다시.""" if not skc.has({skc.KEY: copy}): return copy rows = [*copy["로직"], *(copy.get("단가") or [])] have = _keys(rows) keys = {k for box, got in reads.items() if box <= have for k in got} | set(more) if not keys: return copy # 셈이 이 사본을 안 읽음 — 그대로 whole = skc.whole_() add = [ prices.to_json(whole.index[k[:2]][k]) for k in sorted(keys - have) if k in whole.index.get(k[:2], {}) ] keep = [r for r in rows if str(r.get("키")) in keys] + add logic = [r for r in keep if str(r.get("키"))[:2] not in skc.RES] cost = [r for r in keep if str(r.get("키"))[:2] in skc.RES] out = {**copy, "로직": logic, "단가": cost} if logic != copy["로직"]: out["판"] = skc._fp(logic) if cost != (copy.get("단가") or []): out["단가판"] = skc._fp(cost) return out def cut_doc(doc: Any, reads: dict) -> Any: """문서의 로직 사본(`사본`) · 수식 사본 값 몫(`수식`)을 줄임 — 문서 안 내 기계 키는 늘 담음(822-1).""" if not isinstance(doc, dict): return doc more = mym.keys_in({k: v for k, v in doc.items() if k not in (skc.KEY, dc.KEY)}) out = dict(doc) if skc.has(doc): out[skc.KEY] = cut(doc[skc.KEY], reads, more) if dc.has(doc) and isinstance(doc[dc.KEY].get("로직"), list): got = doc[dc.KEY] out[dc.KEY] = {**got, **cut(dc.values_of(got), reads, more)} return out def _project() -> bool: from M02_MasterTemplete import M02_Labor_Ref as refs return refs.here()[0] == "project" def _record(kind: str, out: Any) -> None: """저장 길이 이 문서 셈을 안 돌린 몫만 한 번 셈(읽은 줄 적기) — 사전조합 · 수식 길로 고친 SK.""" if not isinstance(out, dict): return if kind == "precombo" and skc.has(out): from M02_MasterTemplete import M02_Labor_Doc as labor labor.calc(out, {}) elif kind == "sangeun" and dc.has(out) and out[dc.KEY].get("따로"): from M02_MasterTemplete import M02_Doc_Sk as dsk dsk.run(out, out[dc.KEY]) def saved(kind: str, make: Callable[[], Any]) -> Any: """[저장] 관문 — `make()`(정리 · 셈 · 사본 정함) 동안 읽은 줄을 적고 사본을 그 줄로 줄임.""" if _project(): return make() reads: dict = {} token = skc.READS.set(reads) try: out = make() _record(kind, out) finally: skc.READS.reset(token) return cut_doc(out, reads)