- 밑 공용 함수 rate_base ↔ rateBase · 검사 · 짝잃음 · B09 rows:n@labor(파이썬 · TS) · 굳힘 · 화면 열쇠 옮김이 비목 꼬리 그대로 - 여덟 파일 율 줄 152 중 기준 찾은 147 재현(인력 82 · 재료 35 · 절 소계 16 · 노무비 11 · 장비 2 · 자재+율 1) - 시험 test_778_4_rate_line 29 · 전후 대조 148/148 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
4.3 KiB
TypeScript
99 lines
4.3 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete/logic/M02_Sangeun_Undo.ts
|
|
* 단가산출(SK) 되돌리기 기억(PLAN 744-1 서버 · 748-0 화면) — 더한 기초 · 더한 항목 빼기 → 문서 `뺀더한줄`
|
|
* `[{차례, 줄}]`(되살림 = 그 차례에 다시 끼움) · 더한 항목 수량 → `처음수량`(넣을 때 값) 되돌림.
|
|
* 더한줄 지움 · 끼움 때 뒤 더한줄 열쇠 `add:#j[/…]`(덮개 · 고름 · 자재표시 · 율 줄 기준 목록 778-4)를
|
|
* 한 칸씩 당김 · 밂(지운 줄을 가리킨 기준 열쇠는 버림 — 빈 목록 = 서버 짝잃음 알림).
|
|
* 금액은 늘 서버가 셈 — 여기는 문서 칸 옮김만 · 화면 · fetch · sessionStorage 없음(`CLAUDE.md` 4장).
|
|
* ========================================================================== */
|
|
|
|
import type { SkAdded, SkDoc } from "./M02_Sangeun_Cover_Model";
|
|
|
|
/* ── 순수 셈 ── */
|
|
const KEYED = ["덮개", "고름", "자재표시"] as const;
|
|
const KEY_RE = /^add:#(\d+)(.*)$/;
|
|
|
|
/** 문서 열쇠 맵의 `add:#j…` 를 옮김 — `drop` = 지운 j(그 열쇠는 버림) · 뒤 j 는 한 칸 당김 / `push` = 끼운 j 부터 한 칸 밂 */
|
|
function moveKeys(doc: SkDoc, mode: { drop: number } | { push: number }): void {
|
|
for (const name of KEYED) {
|
|
const map = doc[name] as Record<string, unknown> | undefined;
|
|
if (!map) continue;
|
|
const out: Record<string, unknown> = {};
|
|
for (const [key, value] of Object.entries(map)) {
|
|
const hit = KEY_RE.exec(key);
|
|
if (!hit) {
|
|
out[key] = value;
|
|
continue;
|
|
}
|
|
const j = Number(hit[1]);
|
|
if ("drop" in mode) {
|
|
if (j === mode.drop) continue;
|
|
out[j > mode.drop ? `add:#${j - 1}${hit[2]}` : key] = value;
|
|
} else out[j >= mode.push ? `add:#${j + 1}${hit[2]}` : key] = value;
|
|
}
|
|
(doc as Record<string, unknown>)[name] = out;
|
|
}
|
|
for (const one of doc.더한줄 ?? []) {
|
|
const rate = (one.고정 as { 비율?: { 기준?: unknown } } | undefined)?.비율;
|
|
if (!rate || !Array.isArray(rate.기준)) continue;
|
|
rate.기준 = (rate.기준 as string[]).flatMap((key) => {
|
|
const hit = KEY_RE.exec(key);
|
|
if (!hit) return [key];
|
|
const j = Number(hit[1]);
|
|
if ("drop" in mode)
|
|
return j === mode.drop ? [] : [j > mode.drop ? `add:#${j - 1}${hit[2]}` : key];
|
|
return [j >= mode.push ? `add:#${j + 1}${hit[2]}` : key];
|
|
});
|
|
}
|
|
}
|
|
|
|
/** 더한줄 k 를 빼고 기억에 둠 — 이름은 되살림 띠 글(서버가 저장 때 다시 적음) */
|
|
export function forgetAdd(doc: SkDoc, k: number, name: string): void {
|
|
const one = doc.더한줄?.[k];
|
|
if (!one) return;
|
|
doc.뺀더한줄 = [
|
|
...(doc.뺀더한줄 ?? []),
|
|
{ 차례: k, 줄: JSON.parse(JSON.stringify({ ...one, ...(name ? { 이름: name } : {}) })) },
|
|
];
|
|
doc.더한줄 = (doc.더한줄 ?? []).filter((_, i) => i !== k);
|
|
moveKeys(doc, { drop: k });
|
|
}
|
|
|
|
/** 기억 n 번째를 그 차례에 다시 끼움(차례가 줄 수보다 크면 맨 끝) */
|
|
export function reviveAdd(doc: SkDoc, n: number): void {
|
|
const gone = doc.뺀더한줄?.[n];
|
|
if (!gone) return;
|
|
const list = [...(doc.더한줄 ?? [])];
|
|
const at = Math.min(Math.max(gone.차례, 0), list.length);
|
|
list.splice(at, 0, gone.줄);
|
|
doc.더한줄 = list;
|
|
moveKeys(doc, { push: at });
|
|
const rest = (doc.뺀더한줄 ?? []).filter((_, i) => i !== n);
|
|
if (rest.length) doc.뺀더한줄 = rest;
|
|
else delete doc.뺀더한줄;
|
|
}
|
|
|
|
/** 되살림 띠 글 — 줄 이름 · 없으면 로직 · 요소 · 참조 키 */
|
|
export function goneName(one: SkAdded): string {
|
|
const ref = (one.참조 as { 키?: string } | undefined)?.키;
|
|
return String(one.이름 || one.로직 || one.요소 || ref || "");
|
|
}
|
|
|
|
/** 더한줄 수량 고침 — 처음 값(옛 문서엔 없음)을 먼저 적어 둠 */
|
|
export function setQty(one: SkAdded, qty: number): void {
|
|
one.처음수량 ??= one.수량;
|
|
one.수량 = qty;
|
|
}
|
|
|
|
/** 더한줄 수량 처음 값으로 — 처음 값 모르면 그대로 */
|
|
export function resetQty(one: SkAdded): void {
|
|
if (one.처음수량 != null) one.수량 = one.처음수량;
|
|
}
|
|
|
|
/** 넣을 때 수량 적어 둠 */
|
|
export const withFirst = <T extends { 수량: number }>(line: T): T & { 처음수량: number } => ({
|
|
...line,
|
|
처음수량: line.수량,
|
|
});
|
|
/* ── 순수 셈 끝 ── */
|