/* ============================================================================= * M02_Labor_LogicMats_Model.ts * 로직 자재(103-1) 자료 · 규칙(화면 없음) — 서버 calc `줄[].재료`(짝 `M02_Labor_Doc.logic_mats`) · * 입력이 바뀌면 그 입력에 걸린 고름을 풂(`releaseTied`) · 사용자 자재와 같은 품목 막기(`insideKeys`). * 시험 `resources/tester/test_m02_labor_logic_mats.py`(Node 헬퍼). * ========================================================================== */ /** 잡힌 품목 — 엔진 답 `품목`(값칸 = 단가가 나온 값 열) */ export interface LogicMatItem { 키: string; 이름?: string | null; 규격?: string | null; 단위?: string | null; 값칸?: string | null; } /** 로직 자재 한 줄 — 맨 위 층 호표 재료 줄(금액은 로직 재료비 안 · 합에 안 더함) */ export interface LogicMat { 차례: number; 이름: string; 단위: string; 수량: number | null; 단가: number | null; 금액: number | null; 까닭?: string | null; 품목: LogicMatItem | null; /** M01 자재품목 값 열 다섯(빈 값 null) */ 가격?: Record; 고름: boolean; /** 품목 조건에 든 입력 이름 — 그 입력이 바뀌면 고름을 풂 */ 걸림: string[]; } /** 입력값이 `before` 에서 바뀐 이름에 걸린 고름을 지움(다 풀리면 `고름` 칸째) — `row` 를 고쳐 돌려줌 */ export function releaseTied< T extends { 입력값: Record; 고름?: Record }, >(row: T, before: Record, mats: readonly LogicMat[]): T { if (!row.고름) return row; const text = (v: unknown): string => (v === null || v === undefined ? "" : String(v)); const keys = new Set([...Object.keys(before), ...Object.keys(row.입력값)]); const changed = [...keys].filter((k) => text(before[k]) !== text(row.입력값[k])); for (const mat of mats) if (mat.걸림.some((n) => changed.includes(n))) delete row.고름[String(mat.차례)]; if (!Object.keys(row.고름).length) delete row.고름; return row; } /** calc 답의 로직 자재 품목 키 전부 — 사용자 자재 추가 막기 */ export function insideKeys(lines: readonly { 재료?: LogicMat[] }[] | undefined): Set { return new Set( (lines ?? []).flatMap((l) => (l.재료 ?? []).flatMap((m) => (m.품목 ? [m.품목.키] : []))), ); }