- 공용 화면 A00_Common/formula_doc_*(M01 · 뒤에 M02 = 길 앞머리만 바꿈) — 옛 수식 나무 화면은 M01 에서 걷음(M02 는 그대로) - 보이는 수식 그 자리 고침(공용 editEq · 전체 고르기 = 한 줄 글) · 분수 · 첨자 · 만약 = 탄 갈래만 · 반올림 자리 숨김 · 값 식 단위 - 값 식 숫자 누름 = 그 자리 값 · 기호 값 누름 = 정의 줄 · 정의 영역 = 기호 : 이름 — 연결(키 · 값 · 단위 없음 · 탄 갈래로 닿는 기호만) - 새 기호 = 정의 필요 → 숫자 · 선택 · 입력 · 요소 · 표 · 단가산출 · 식 연결 · 기호 · 첨자 · 이름 고침 · 정의 지움 - 금액 = 경쟁사 꼴 이름 : 단가 × 수량 = 금액 · 묶음 하나 = 계 · 둘 이상 = 가. 이름 소계 + 합계 · 단가산출 연결 펼침 = 그 문서 식 · 값 식 - 셈 = 브라우저 계산기 즉시 · 연결 값 = 서버 기록 캐시(common_util_formula_doc_cache.ts ↔ master_doc_record.py) · 없는 연결만 뒤에서 미리 셈 - 서버 길 GET /api/m01/logic/doc · POST preview · save(서버가 정본으로 다시 셈 · 수식 문서 파일에 씀) · links · GET table (M01_MasterData_Doc.py) - 연결 대상 안 고른 연결(표 · 요소 · 단가산출) = 연결 끊김 표지(파이썬 · TS 짝 가드 — 서버 500 걷음) - 시험 test_802_3_doc_screen 11(읽기 · 빈 문서 · 저장 = 서버 다시 셈 · 관리자 아니면 막힘 · 지움 견딤 · 표지 갈래 · 후보) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
3.8 KiB
TypeScript
104 lines
3.8 KiB
TypeScript
/* =============================================================================
|
|
* common_util_formula_doc_cache.ts
|
|
* 수식 문서 연결 캐시(PLAN 802-3) — 서버 기록(요소 값 · 표 · 단가산출 문서) → `DocLinks`
|
|
* 화면이 같은 계산기(`calcDoc`)로 네트워크 없이 셈 · 캐시에 없는 연결 = 빈 값(「불러오는 중」) + `missing` 표시
|
|
* → 화면이 뒤에서 미리 셈(서버 기록)을 받아 `merge` 뒤 다시 셈
|
|
* 파이썬 짝 = resources/master_data/scripts/master_doc_record.py(기록 꼴) · 화면 · DOM · fetch 없음
|
|
* ========================================================================== */
|
|
|
|
import type { FormulaDoc } from "./common_util_formula_doc";
|
|
import { Broken, Gap, plainText, type DocLinks } from "./common_util_formula_doc_calc";
|
|
import { tableValue, type DocTable } from "./common_util_formula_doc_table";
|
|
|
|
type Payload = Record<string, unknown>;
|
|
type Got = { 값?: unknown; 빈?: string; 끊김?: string };
|
|
|
|
export interface DocRecord {
|
|
요소?: (Got & { p: Payload })[];
|
|
표?: Record<string, (DocTable & { 끊김?: string }) | null>;
|
|
문서?: Record<string, FormulaDoc | null>;
|
|
이름?: Record<string, string>;
|
|
}
|
|
|
|
export const LOADING = "불러오는 중";
|
|
|
|
function canon(v: unknown): unknown {
|
|
if (typeof v === "number") return plainText(v);
|
|
if (Array.isArray(v)) return v.map(canon);
|
|
if (v && typeof v === "object") {
|
|
const out: Record<string, unknown> = {};
|
|
for (const k of Object.keys(v).sort()) out[k] = canon((v as Payload)[k]);
|
|
return out;
|
|
}
|
|
return v;
|
|
}
|
|
const keyOf = (p: Payload): string => JSON.stringify(canon(p));
|
|
|
|
export interface DocCache {
|
|
links: DocLinks;
|
|
/** 서버 기록을 더함 */
|
|
merge: (rec: DocRecord | undefined) => void;
|
|
/** 키 → 이름(연결 글) */
|
|
names: Map<string, string>;
|
|
/** 마지막 셈에서 캐시에 없던 연결이 있었나 — 읽으면 지움 */
|
|
takeMissing: () => boolean;
|
|
/** 표 하나 넣음(고르기에서 받은 것) */
|
|
putTable: (ref: string, table: DocTable) => void;
|
|
}
|
|
|
|
export function createDocCache(): DocCache {
|
|
const elements = new Map<string, Got>();
|
|
const tables = new Map<string, (DocTable & { 끊김?: string }) | null>();
|
|
const docs = new Map<string, FormulaDoc | null>();
|
|
const names = new Map<string, string>();
|
|
let missing = false;
|
|
const miss = (): never => {
|
|
missing = true;
|
|
throw new Gap(LOADING);
|
|
};
|
|
const links: DocLinks = {
|
|
element(p) {
|
|
if (!p.키 && p.재료 === undefined) throw new Broken("요소 고르지 않음"); // 짝 master_doc_link
|
|
const got = elements.get(keyOf(p));
|
|
if (!got) return miss();
|
|
if (got.빈 !== undefined) throw new Gap(got.빈);
|
|
if (got.끊김 !== undefined) throw new Broken(got.끊김);
|
|
return got.값;
|
|
},
|
|
table(p) {
|
|
if (!p.표 || !p.칸) throw new Broken("표 · 칸 고르지 않음");
|
|
const ref = String(p.표);
|
|
if (!tables.has(ref)) return miss();
|
|
const t = tables.get(ref);
|
|
if (!t || t.끊김 !== undefined) throw new Broken(t?.끊김 ?? `「${ref}」 표 아님`);
|
|
return tableValue(t, p);
|
|
},
|
|
doc(key) {
|
|
if (!key) return null;
|
|
if (!docs.has(key)) return miss();
|
|
return docs.get(key) ?? null;
|
|
},
|
|
};
|
|
return {
|
|
links,
|
|
names,
|
|
merge(rec) {
|
|
for (const one of rec?.요소 ?? []) {
|
|
const { p, ...got } = one;
|
|
elements.set(keyOf(p), got);
|
|
}
|
|
for (const [k, t] of Object.entries(rec?.표 ?? {})) tables.set(k, t);
|
|
for (const [k, d] of Object.entries(rec?.문서 ?? {})) docs.set(k, d);
|
|
for (const [k, n] of Object.entries(rec?.이름 ?? {})) names.set(k, n);
|
|
},
|
|
takeMissing() {
|
|
const was = missing;
|
|
missing = false;
|
|
return was;
|
|
},
|
|
putTable(ref, table) {
|
|
tables.set(ref, table);
|
|
},
|
|
};
|
|
}
|