/**
* STMATE w 8.0 — 단가목록표 화면(wM_JabPum) 끼워 넣기 (scr_ui_dan.js)
* 근거: ui_form_catalog.txt wM_JabPum(cBoxCode 10종 · cBox_IFORM 명칭,규격/산근내용) · wM_Boxa(RG_MComp) · wM_Edt_Hal(wcBx_Up)
* 후보 5칸 = COSTN AMT_11~51(페이지 PG_11~51) · 채택 = PG_1 · 가격지 이름 = 초기값 JMOK1~5/JMK10~50
* 기초단가(M·L·S) 고침 = COSTN 자기 비목 칸 + 계산 원천 자원(manualPrice) → 연쇄 재계산
*/
(function (global) {
"use strict";
const P = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
screens: {},
actions: {},
menus: [],
});
const U = global.STMATE_UI_DAN;
const esc = (s) =>
String(s == null ? "" : s).replace(
/[&<>"]/g,
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
);
const $ = (id) => document.getElementById(id);
const prefs = () => (global.STMATE_UI_APP_MENU && global.STMATE_UI_APP_MENU.prefs) || {};
function st(ctx) {
const u = ctx.uiState;
u.dan = u.dan || {
kind: u.danFilter && u.danFilter !== "ALL" ? u.danFilter : "ALL",
kw: "",
mode: "name",
sort: "",
code: "",
};
return u.dan;
}
// 자원(계산 원천) 맞춤 — 기초단가 한 건
function syncRes(ctx, code, price, how) {
const res = (ctx.state.resources || []).find((r) => r.code === code && !r.isSynthetic);
if (res) Object.assign(res, { adoptMode: how || "manual", manualPrice: price });
}
function render(ctx, api) {
if (!ctx.dbfData)
return `
`;
const s = st(ctx),
rows = ctx.dbfData.costn_rows || [];
const ini = ctx.dbfData.start_ini || {};
const slots = U.slotNames(ini);
const sort = s.sort || prefs().dflIndex || "C";
const list = U.filterRows(rows, {
kind: s.kind,
kw: s.kw,
mode: s.mode,
sort,
memos: ctx.dbfData.costn_memos || {},
});
const cnt = U.counts(rows);
const n2 = prefs().useName2;
const nm = (r, k) => (n2 && String(r[`${k}2`] || "").trim() ? r[`${k}2`] : r[k]) || "";
const slotName = (n) => {
const x = slots.list.find((q) => q.slot === n);
return x ? x.short || x.name : "";
};
const { fmt } = api;
const body = list
.map((r, i) => {
const c = U.codeOf(r),
base = U.isBase(c),
cand = U.candidates(r),
pg = U.pages(r),
ad = U.adoptedSlot(r);
const priceCell = (k) =>
base && k[0] === c[0]
? ``
: `${U.num(r[k]) ? fmt(r[k]) : ""}`;
return `
| ${i + 1} | ${c} |
${esc(nm(r, "NAME"))} | ${esc(nm(r, "SIZE"))} | ${esc(r.UNIT)} |
${priceCell("MAMT1")} | ${priceCell("LAMT1")} | ${priceCell("SAMT1")} |
${fmt(U.appliedPrice(r))} |
${slots.list
.map(({ slot: n }) => {
const v = cand[n - 1];
const on = ad === n ? "background:#fde68a;font-weight:700;" : "";
const click =
base && v > 0
? ` class="num dan-adopt" data-code="${c}" data-slot="${n}" style="cursor:pointer;${on}" title="이 단가 채택"`
: ` class="num" style="${on}"`;
return `${v ? fmt(v) : ""}${pg[n - 1] ? ` p.${esc(pg[n - 1])}` : ""} | `;
})
.join("")}
${ad ? esc(slotName(ad)) : base && U.isUserEdited(r) ? "직접" : ""} |
${esc(r.BQCODE || "")} | ${esc(r.EQCODE || "")} |
${/^[BDX]/.test(c) ? `` : ""} |
`;
})
.join("");
return ``;
}
function boxa(ctx, api) {
const slots = U.slotNames(ctx.dbfData.start_ini);
const s = st(ctx);
api.openModal(
"단가목록표 가격지 비교 (wM_Boxa)",
``,
);
$("bx-ok").onclick = () => {
const how = $("bx-how").value,
scope = $("bx-scope").value;
const rows =
scope === "one"
? ctx.dbfData.costn_rows.filter((r) => U.codeOf(r) === s.code)
: ctx.dbfData.costn_rows;
const ch = U.adoptAll(rows, how, {
kinds: scope === "one" ? "MLS" : scope,
skipZero: $("bx-skip0").checked,
keepUser: scope !== "one" && $("bx-keep").checked,
});
ch.forEach((c) => syncRes(ctx, c.code, c.to, `slot${c.slot}`));
ctx.uiState.danMsg = `${ch.length}건 채택 바꿈`;
api.closeModal();
api.cascadeAndRender();
};
}
function edtHal(ctx, api) {
const s = st(ctx);
api.openModal(
"기준단가 수정 (wM_Edt_Hal)",
``,
);
$("eh-ok").onclick = () => {
const kind = $("eh-kind").value;
const ch = U.edtHal(ctx.dbfData.costn_rows, {
kinds: kind === "one" ? "MLS" : kind,
codes: kind === "one" ? [s.code] : null,
pct: $("eh-pct").value,
digits: Number($("eh-dot").value),
mode: $("eh-up").value,
});
ch.forEach((c) => syncRes(ctx, c.code, c.to, "manual"));
ctx.uiState.danMsg = `기준단가 수정 ${ch.length}건`;
api.closeModal();
api.cascadeAndRender();
};
}
function findUse(ctx, api) {
const s = st(ctx),
f = U.findUse(s.code, ctx.dbfData);
const li = (a) => (a.length ? a.join("") : "(없음)");
api.openModal(
`사용처 찾기 — ${s.code}`,
``,
);
}
function bind(ctx, api) {
if (!ctx.dbfData) return;
const s = st(ctx),
rr = () => api.renderWorkspace();
$("dan-kind").onchange = (e) => ((s.kind = e.target.value), rr());
$("dan-mode").onchange = (e) => ((s.mode = e.target.value), rr());
$("dan-kw").onchange = (e) => ((s.kw = e.target.value), rr());
$("dan-sort").onchange = (e) => ((s.sort = e.target.value), rr());
$("dan-boxa").onclick = () => boxa(ctx, api);
$("dan-edthal").onclick = () => edtHal(ctx, api);
$("dan-use").onclick = () => findUse(ctx, api);
const grid = $("dan-grid");
if (s.scroll) grid.scrollTop = s.scroll;
grid.onscroll = () => (s.scroll = grid.scrollTop);
document
.querySelectorAll("#dan-form .dan-pick")
.forEach((td) => (td.onclick = () => ((s.code = td.getAttribute("data-code")), rr())));
document.querySelectorAll("#dan-form .dan-price").forEach(
(inp) =>
(inp.onchange = () => {
const c = inp.getAttribute("data-code");
const r = ctx.dbfData.costn_rows.find((x) => U.codeOf(x) === c);
const v = U.num(inp.value);
U.writeBase(r, v);
r.PG_1 = "0";
syncRes(ctx, c, v, "manual");
ctx.uiState.danMsg = `${c} 적용단가 ${v} 직접 수정`;
api.cascadeAndRender();
}),
);
document.querySelectorAll("#dan-form .dan-adopt").forEach(
(td) =>
(td.onclick = () => {
const c = td.getAttribute("data-code");
const r = ctx.dbfData.costn_rows.find((x) => U.codeOf(x) === c);
const ch = U.adopt(r, td.getAttribute("data-slot"), { skipZero: true });
if (!ch) return;
syncRes(ctx, c, ch.to, `slot${ch.slot}`);
ctx.uiState.danMsg = `${c} ${ch.slot} 단가 채택`;
api.cascadeAndRender();
}),
);
document
.querySelectorAll("#dan-form .dan-ilwi")
.forEach(
(b) =>
(b.onclick = () => (
(ctx.uiState.selectedIlwiCode = b.getAttribute("data-code")),
(ctx.uiState.activeScreen = "wM_Edit_iLWi"),
rr()
)),
);
}
P.screens.wM_JabPum = { render, bind };
})(window);