Files
Aislo/common_util/common_util_formula_doc_cache.ts
T
eomsangdonandClaude Opus 5.5 54fb55487c feat(802-3): M01 단가산출 기초 수식 문서 화면 — 왼쪽 기호 식 · 오른쪽 값 식 · 빈 문서 시작 · 연결 고르기
- 공용 화면 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>
2026-11-07 19:23:57 +09:00

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);
},
};
}