/* ============================================================================= * M02_MasterTemplete_Labor_RowSk.ts * 일위대가 줄 모달 안 단가산출 상세 = 717-2b 단가산출(SK) 상세 「줄 모드」(PLAN 723-3 · 정함 4 · 5). * · 줄 → 이름 없는 SK 문서(`lineDoc` · 고름 · 자재표시 = 바탕 줄 정체 cid) → `mountSangeunDetail` 그대로 얹음 * (보기 넷 · 품 · 단가 덮개 · 줄 빼기 · 더하기 · 시험 입력 칩) * · `take()` = [적용] 때 그 줄에 남길 것 — 패치 `{덮개, 뺀줄, 더한줄}`(M02 변경 항목 · M01 안 바뀜 · 내역 17-2) + * 옵션(시험 입력 → 그 줄 `입력값` · 정함 5) * · [단가산출로도 저장](기본 꺼짐) = `saveAsSk` — 개인 층 새 SK(`from-line`) * 공용 부품 갈아 끼움은 724(sub2) — 지금은 717-2b 상세를 그대로 씀. * ========================================================================== */ import { el } from "@ui/ui_template_elements"; import { createText } from "@ui/ui_template_text"; import { fetchAuto } from "../M01_MasterData/M01_MasterData_UI_Logic_Api"; import type { LaborRow } from "./M02_MasterTemplete_Labor_Api"; import { calcSangeun, sangeunFromLine, type SkScope } from "./M02_MasterTemplete_Sangeun_Cover"; import { mountSangeunDetail } from "./M02_MasterTemplete_Sangeun_Detail"; import { cleanPatch, lineDoc, needsIds, type LinePatch, type SkCalc, } from "./M02_Sangeun_Cover_Model"; export interface RowSkHandle { /** 고친 것(패치 · 옵션) — 없으면 null */ take: () => { patch: LinePatch | null; values: Record } | null; destroy: () => void; } const same = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b); /** 줄 모드 SK 상세를 `host` 에 — 바탕 줄 정체가 필요하면 먼저 셈 */ export function mountRowSk( host: HTMLElement, o: { row: LaborRow; scope: SkScope; title: string; unit: string; readOnly: boolean }, ): RowSkHandle { let handle: ReturnType | null = null; let alive = true; /** 자동값(상세가 빈 입력에 채움) — 손대지 않은 자동값은 줄에 안 남김(빈 값 = 자동값 그대로) */ let auto: Record = {}; const before = cleanPatch(o.row.패치); const values0 = Object.fromEntries( Object.entries(o.row.입력값 ?? {}).map(([k, v]) => [k, String(v)]), ); host.replaceChildren(createText("…", "muted")); void (async () => { let ids: string[] | null = null; try { if (needsIds(o.row)) ids = (await calcSangeun(o.scope, lineDoc(o.row, null, null))).정체 ?? null; } catch { ids = null; // 정체 못 받음 = 고름 · 자재표시 없이(셈 까닭은 상세가 보임) } if (!alive) return; const doc = { ...lineDoc(o.row, ids, before), 이름: o.title, 단위: o.unit || "식" }; const box = el("div", { attrs: { "data-row-sk": "" } }); host.replaceChildren(box); handle = mountSangeunDetail(box, doc, { layer: o.scope.layer, owner: o.scope.owner, readOnly: o.readOnly, onChanged: () => undefined, }); if (o.row.로직 && !o.row.참조) auto = await fetchAuto(o.row.로직).catch(() => ({})); })(); return { take: () => { if (!handle) return null; const d = handle.getDoc(); const patch = cleanPatch({ 덮개: d.덮개 as LinePatch["덮개"], 뺀줄: d.뺀줄, 더한줄: d.더한줄, }); const values = o.row.참조 ? values0 : Object.fromEntries( Object.entries(handle.getValues()).filter( ([k, v]) => String(v).trim() !== "" && (k in values0 || auto[k] == null || String(auto[k]) !== String(v)), ), ); return same(patch, before) && same(values, values0) ? null : { patch, values }; }, destroy: () => { alive = false; handle?.destroy(); }, }; } /** [단가산출로도 저장] — 그 줄(고친 패치 포함)을 개인 층 새 SK 로 · 새 이름 돌려줌 */ export async function saveAsSk( row: LaborRow, patch: LinePatch | null, name: string, unit: string, ): Promise { const line: Record = { ...row }; delete line.패치; delete line.수량; if (patch) line.패치 = patch; const got = await sangeunFromLine({ layer: "personal", owner: null }, line, name, unit || "식"); return got.이름; }