/** * STMATE w 8.0 — 코드 관리 창 끼워 넣기 (scr_ui_code.js) · 코드변경 · 항목교체 · 코드삭제 · +α · 신규등록 * 근거: DFM 원문 — uM_ChgCode(현재코드 · 바뀔코드 · * 사용 · 코드변환실행 · 코드일괄변환 · 현재이후 자동입력 · 바뀔코드 초기화) * · uM_ReplCode(대상코드 · 항목교체실행 · 동일항목(명칭, 규격... 단가) 대상코드 자동입력) · uM_DelCode(선 택 · 선택코드 * 삭제실행 · 코드일괄삭제(중기비제외/포함) · 사용안된 "천원" "인" 일위대가 전체선택 · 동일항목 자동선택) * · uM_Chg_Plus(종류별 현재최종 + α) · wM_Boxa Act_CopyNew(신규등록) · Act_DEL_CODE · Act_CH_CODE · Act_RP_CODE * 계산 = ui_code.js · 저장 = app.js postSave(/api/ui/save · api_ui_code.py) */ (function (global) { "use strict"; const P = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || { screens: {}, actions: {}, menus: [], }); const UC = global.STMATE_UI_CODE; const esc = (s) => String(s == null ? "" : s).replace( /[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c], ); const $ = (id) => document.getElementById(id); const KINDS = ["L", "M", "S", "B", "X", "D", "W"]; const env = (ctx) => ({ d: ctx.dbfData, state: ctx.state, memo: ctx.uiState.editedSmtMap || (ctx.uiState.editedSmtMap = {}), }); const need = (ctx) => (ctx.dbfData ? true : (alert("열린 프로젝트가 없음"), false)); const done = (ctx, api, msg) => { ctx.uiState.danMsg = msg; api.closeModal(); api.cascadeAndRender(); }; const fail = (res) => alert((res.errors || [res.error || "실패"]).slice(0, 12).join("\n")); const TITLE = { change: "코드변경 [uM_ChgCode] — 현재코드 → 바뀔코드", replace: "항목교체(내역포함) [uM_ReplCode] — 현재코드 → 대상코드", delete: "코드삭제 [uM_DelCode]", }; function st(ctx, mode) { const u = ctx.uiState; if (!u.codeMgr || u.codeMgr.mode !== mode) u.codeMgr = { mode, kind: "L", map: {}, sel: {}, kw: "" }; return u.codeMgr; } function priceOf(r, k) { const f = { L: "LAMT1", M: "MAMT1", S: "SAMT1" }[k] || "TAMT1"; return Number(r[f]) || 0; } function mgrHtml(ctx, api) { const s = ctx.uiState.codeMgr, e = env(ctx), used = UC.usedSet(e); const rows = (ctx.dbfData.costn_rows || []).filter((r) => { const c = UC.codeOf(r); if (c[0] !== s.kind) return false; const kw = String(s.kw || "") .trim() .toLowerCase(); return ( !kw || `${c} ${r.NAME || ""} ${r.SIZE || ""} ${r.BQCODE || ""}`.toLowerCase().includes(kw) ); }); const cnt = (k) => (ctx.dbfData.costn_rows || []).filter((r) => UC.codeOf(r)[0] === k).length; const tabs = KINDS.map( (k) => ``, ).join(""); const col = s.mode === "delete" ? "선 택" : s.mode === "replace" ? "대상코드" : "바뀔코드"; const body = rows .map((r) => { const c = UC.codeOf(r); const cell = s.mode === "delete" ? `` : ` `; return `${c}${cell}${esc(r.NAME)}${esc(r.SIZE)}${esc(r.UNIT)}${esc(r.BQCODE || "")}${esc(r.EQCODE || "")}${api.fmt(priceOf(r, s.kind))}${used.has(c) ? "*" : ""}`; }) .join(""); const btn = (id, t) => ``; const acts = { change: [ btn("cm-run-sel", "코드변환실행(선택)"), btn("cm-run-all", "코드일괄변환"), btn("cm-auto", "현재이후 자동입력"), btn("cm-clear", "바뀔코드 초기화"), btn("cm-plus", "현재코드 + α"), ], replace: [ btn("cm-run-all", "항목교체실행"), btn("cm-same", "동일항목 대상코드 자동입력"), btn("cm-clear", "대상코드 초기화"), ], delete: [ btn("cm-del", "선택코드 삭제실행"), btn("cm-del-x", "코드일괄삭제 (중기비제외)"), btn("cm-del-t", "코드일괄삭제 (중기비포함)"), btn("cm-1000", '사용안된 "천원" "인" 일위대가 선택'), btn("cm-same", "동일항목 자동선택"), btn("cm-clear", "선택코드 초기화"), ], }[s.mode]; return `
${tabs}
${acts.join("")}
${body}
현재코드${col}명칭규격단위분류번호시설코드단가*

* = 내역서·구성표·산출근거 식에 쓰임 · 쓰이는 코드는 지우지 않음 · 바꾼 뒤 [계산 완료] 저장 때 작업본에 씀

`; } function openMgr(ctx, api, mode) { if (!need(ctx)) return; const s = st(ctx, mode); api.openModal(TITLE[mode], mgrHtml(ctx, api)); const re = () => openMgr(ctx, api, mode); document .querySelectorAll(".cm-kind") .forEach((b) => (b.onclick = () => ((s.kind = b.getAttribute("data-k")), re()))); $("cm-kw").onchange = (e) => ((s.kw = e.target.value), re()); document .querySelectorAll(".cm-map") .forEach( (i) => (i.onchange = () => (s.map[i.getAttribute("data-c")] = i.value.trim().toUpperCase())), ); document .querySelectorAll(".cm-sel") .forEach((i) => (i.onchange = () => (s.sel[i.getAttribute("data-c")] = i.checked))); const on = (id, fn) => $(id) && ($(id).onclick = fn); const pick = (onlySel) => Object.fromEntries(Object.entries(s.map).filter(([c, v]) => v && (!onlySel || s.sel[c]))); const run = (onlySel) => { const m = pick(onlySel); const res = mode === "replace" ? UC.replaceCodes(env(ctx), m) : UC.changeCodes(env(ctx), m); if (!res.ok) return fail(res); s.map = {}; s.sel = {}; done( ctx, api, `${mode === "replace" ? "항목교체" : "코드변경"} ${res.count}건 · 내역 ${res.refs.boq} · 구성 ${res.refs.comp} · 식 ${res.refs.memo}곳`, ); }; on("cm-run-sel", () => run(true)); on("cm-run-all", () => run(false)); on("cm-clear", () => { const k = s.kind; Object.keys(s.map).forEach((c) => c[0] === k && delete s.map[c]); Object.keys(s.sel).forEach((c) => c[0] === k && delete s.sel[c]); re(); }); on("cm-auto", () => { const list = (ctx.dbfData.costn_rows || []).map(UC.codeOf).filter((c) => c[0] === s.kind); const from = list.find((c) => s.sel[c]) || list[0]; const start = prompt(`${from} 부터 차례로 넣을 첫 바뀔코드`, from); if (!start || !UC.CODE_RE.test(start.trim().toUpperCase())) return; let n = UC.codeNum(start.trim().toUpperCase()); list.slice(list.indexOf(from)).forEach((c) => (s.map[c] = UC.mkCode(s.kind, n++))); re(); }); on("cm-plus", () => openPlus(ctx, api)); on("cm-same", () => { const m = UC.autoReplaceMap(ctx.dbfData.costn_rows, s.kind); if (mode === "delete") Object.keys(m).forEach((c) => (s.sel[c] = true)); else Object.assign(s.map, m); if (!Object.keys(m).length) alert("동일항목 없음"); re(); }); on( "cm-1000", () => (UC.unusedBUnit(env(ctx)).forEach((c) => (s.sel[c] = true)), (s.kind = "B"), re()), ); const del = (codes, what) => { if (!codes.length) return alert(`${what} 없음`); if ( !confirm( `${what} ${codes.length}건을 지움\n${codes.slice(0, 20).join(", ")}${codes.length > 20 ? " …" : ""}`, ) ) return; const res = UC.deleteCodes(env(ctx), codes); if (!res.ok) return fail(res); s.sel = {}; done( ctx, api, `코드삭제 ${res.count}건${res.kept.length ? ` · 쓰이는 코드 ${res.kept.length}건은 남김` : ""}`, ); }; on("cm-del", () => del( Object.keys(s.sel).filter((c) => s.sel[c]), "선택코드", ), ); on("cm-del-x", () => del(UC.unusedCodes(env(ctx), false), "안 쓰이는 코드(중기비제외)")); on("cm-del-t", () => del(UC.unusedCodes(env(ctx), true), "안 쓰이는 코드(중기비포함)")); } // 코드번호 변경(현재코드 + α) function openPlus(ctx, api) { if (!need(ctx)) return; const last = UC.lastCodes(ctx.dbfData.costn_rows); const kinds = ["L", "M", "S", "B", "X", "D", "W"]; api.openModal( "코드번호 변경 ( 현재코드 + α ) [uM_Chg_Plus]", `
${kinds .map( (k) => `
`, ) .join( "", )}
`, ); $("cp-no").onclick = api.closeModal; $("cp-ok").onclick = () => { const alpha = Object.fromEntries(kinds.map((k) => [k, Number($(`cp-${k}`).value) || 0])); const res = UC.changeCodes(env(ctx), UC.plusMap(ctx.dbfData.costn_rows, alpha)); if (!res.ok) return fail(res); done(ctx, api, `코드번호 + α ${res.count}건`); }; } // ── 단가목록표 한 코드 동작(신규등록 · 현재코드 삭제 · 코드번호 변경 · 항목교체) ── function copyNew(ctx, api, code) { if (!need(ctx)) return; const kind = code ? code[0] : (prompt("새 코드 종류 글자(L M S B X D W)", "M") || "").trim().toUpperCase(); if (!kind) return; const def = UC.nextCode(ctx.dbfData.costn_rows, kind); const nc = (prompt(code ? `${code} 를 복사한 새 코드` : "새 코드", def) || "") .trim() .toUpperCase(); if (!nc) return; const fields = code ? {} : { NAME: prompt("명칭", "") || "", SIZE: prompt("규격", "") || "", UNIT: prompt("단위", "") || "", }; const res = UC.addCode(env(ctx), { from: code || null, kind, code: nc, fields }); if (!res.ok) return fail(res); const s = ctx.uiState.dan || (ctx.uiState.dan = {}); Object.assign(s, { code: res.code, kind: "ALL", kw: res.code }); ctx.uiState.danMsg = `${res.code} 신규등록${code ? ` (${code} 복사)` : ""}`; api.cascadeAndRender(); } function delOne(ctx, api, code) { if (!code || !confirm(`${code} 삭제?`)) return; const res = UC.deleteCodes(env(ctx), [code]); if (!res.ok) return fail(res); ctx.uiState.dan.code = ""; ctx.uiState.danMsg = `${code} 삭제`; api.cascadeAndRender(); } function chgOne(ctx, api, code, replace) { if (!code) return; const to = ( prompt(replace ? `${code} 를 쓰는 자리를 바꿀 대상코드` : `${code} → 바뀔코드`, "") || "" ) .trim() .toUpperCase(); if (!to) return; const res = replace ? UC.replaceCodes(env(ctx), { [code]: to }) : UC.changeCodes(env(ctx), { [code]: to }); if (!res.ok) return fail(res); ctx.uiState.dan.code = replace ? code : to; ctx.uiState.danMsg = `${code} → ${to} ${replace ? "항목교체" : "코드변경"} · 내역 ${res.refs.boq} · 구성 ${res.refs.comp} · 식 ${res.refs.memo}곳`; api.cascadeAndRender(); } Object.assign(P.actions, { Act_Chg_Code_Sel: (ctx, api) => openMgr(ctx, api, "change"), Act_Chg_Code_All: (ctx, api) => openMgr(ctx, api, "change"), Act_Chg_Plus: (ctx, api) => openPlus(ctx, api), Act_Del_Code_Sel: (ctx, api) => openMgr(ctx, api, "delete"), Act_Del_Code_All_Minus: (ctx, api) => ( openMgr(ctx, api, "delete"), $("cm-del-x") && $("cm-del-x").click() ), Act_Del_Code_All_Plus: (ctx, api) => ( openMgr(ctx, api, "delete"), $("cm-del-t") && $("cm-del-t").click() ), Act_Chg_Item: (ctx, api) => openMgr(ctx, api, "replace"), }); global.STMATE_UI_CODE_SCR = { openMgr, openPlus, copyNew, delOne, chgOne, env }; })(window);