250 lines
9.8 KiB
JavaScript
250 lines
9.8 KiB
JavaScript
/**
|
|
* STMATE w 8.0 — 변환·가져오기 끼워 넣기 (scr_conv.js) · 1번(파일) 메뉴
|
|
* 조달청 XML 가져오기(Conv_XML_ST8) · XML 변환 설정(uM_XML_Info · uM_ConstType) · 엑셀 → 내역(wM_From_XLS) ·
|
|
* 엑셀 역등록(CONV_XLS_2_ST) · 기준 BOQ·자재 기준자료·코드 변경표 엑셀 왕복 · 분리형 변경 STC · JB_* 제비율표
|
|
* 서버: api_conv.py — 쓰기는 work_dbf/ 아래만 · 파일은 화면에서 골라 base64 로 보냄
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
|
|
const PLUG = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
|
|
screens: {},
|
|
actions: {},
|
|
menus: [],
|
|
});
|
|
const app = () => global.STMATE_APP;
|
|
|
|
async function req(url, body, raw) {
|
|
const res = await fetch(url, {
|
|
method: body === undefined ? "GET" : "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
if (!res.ok) throw new Error(await res.text());
|
|
return raw ? res.blob() : res.json();
|
|
}
|
|
|
|
const curPath = (ctx) => {
|
|
if (!ctx.currentSourcePath) throw new Error("먼저 프로젝트를 불러오세요.");
|
|
return ctx.currentSourcePath;
|
|
};
|
|
const pname = (ctx) => (ctx.state.projectMeta.pname || "공사").slice(0, 28);
|
|
|
|
// 파일 고르기 → {b64, name}
|
|
function pickFile(accept) {
|
|
return new Promise((resolve, reject) => {
|
|
const inp = document.createElement("input");
|
|
inp.type = "file";
|
|
inp.accept = accept;
|
|
inp.onchange = () => {
|
|
const f = inp.files && inp.files[0];
|
|
if (!f) return reject(new Error("파일을 고르지 않음"));
|
|
const rd = new FileReader();
|
|
rd.onload = () => resolve({ b64: String(rd.result), name: f.name });
|
|
rd.onerror = () => reject(rd.error);
|
|
rd.readAsDataURL(f);
|
|
};
|
|
inp.click();
|
|
});
|
|
}
|
|
|
|
function download(blob, name) {
|
|
const a = document.createElement("a");
|
|
a.href = URL.createObjectURL(blob);
|
|
a.download = name;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
setTimeout(() => (URL.revokeObjectURL(a.href), a.remove()), 1000);
|
|
}
|
|
|
|
async function openSaved(path) {
|
|
const a = app();
|
|
const src = await req("/api/sources");
|
|
if (src && src.projects) a.ctx.sourcesData = src;
|
|
const sel = document.getElementById("top-project-select");
|
|
if (sel && src && src.projects)
|
|
sel.innerHTML = src.projects
|
|
.map((p) => `<option value="${p.path}">${p.name}</option>`)
|
|
.join("");
|
|
await a.loadProjectFromServer(path, "wPrjList");
|
|
}
|
|
|
|
const fmtRep = (r) =>
|
|
Object.entries(r || {})
|
|
.filter(([, v]) => typeof v !== "object")
|
|
.map(([k, v]) => `${k}: ${v}`)
|
|
.join("\n");
|
|
|
|
// 화면 XML — report_pps 내보내기(지금 계산 결과)
|
|
function currentXml(ctx) {
|
|
const P = global.STMATE_REPORT_PPS;
|
|
if (!P) throw new Error("조달청 XML 모듈(report_pps.js) 없음");
|
|
return P.buildPpsXml(ctx.dbfData, ctx.state, ctx.computed, 1);
|
|
}
|
|
|
|
const ACTS = {
|
|
async xmlImport(ctx) {
|
|
const f = await pickFile(".xml");
|
|
const r = await req("/api/conv/xml-import", {
|
|
template_path: curPath(ctx),
|
|
xml: { b64: f.b64 },
|
|
});
|
|
alert(
|
|
`[조달청 XML 가져오기] ${r.saved_dir}\n${fmtRep(r.report.info)}\n내역 ${r.report.boq_count}행 · 단가 ${r.report.costn_count}건`,
|
|
);
|
|
await openSaved(r.saved_dir);
|
|
},
|
|
async xmlSettings(ctx) {
|
|
const opt = await req("/api/conv/xml-options");
|
|
const types = opt.prj_types || [];
|
|
const pick = prompt(
|
|
`[XML 변환 설정] 공사유형 번호:\n${types.map((t, i) => `${i + 1}. ${t}`).join("\n")}`,
|
|
"1",
|
|
);
|
|
const prjType = types[parseInt(pick, 10) - 1];
|
|
if (!prjType) return;
|
|
const settings = { prj_type: prjType };
|
|
if (prjType === "건축공사") {
|
|
const help = (opt.building || []).map((b) => b.name).join(", ");
|
|
const txt = prompt(
|
|
`건축공사 사업내용 — 칸=값 을 ; 로 이어 적음(빈 칸 = 0·첫 값)\n${help}`,
|
|
"연면적=0",
|
|
);
|
|
settings.building = Object.fromEntries(
|
|
String(txt || "")
|
|
.split(";")
|
|
.map((s) => s.split("=").map((x) => x.trim()))
|
|
.filter((kv) => kv[0]),
|
|
);
|
|
}
|
|
const r = await req("/api/conv/xml-settings", { xml_text: currentXml(ctx), settings });
|
|
download(new Blob([r.xml], { type: "application/xml" }), `${pname(ctx)}_조달청.xml`);
|
|
alert(
|
|
r.validation.ok ? "XSD 검증 통과" : `XSD 위반: ${(r.validation.errors || [])[0] || ""}`,
|
|
);
|
|
},
|
|
async xlsBoq(ctx) {
|
|
const f = await pickFile(".xlsx");
|
|
const r = await req("/api/conv/xls-boq", {
|
|
source_path: curPath(ctx),
|
|
xlsx: { b64: f.b64 },
|
|
table: (ctx.dbfData && ctx.dbfData.boq_table_name) || "",
|
|
});
|
|
const unk = r.report.unknown_codes || [];
|
|
alert(
|
|
`[엑셀 → 내역] ${r.report.sheet} ${r.report.row_count}행 → ${r.report.table}${unk.length ? `\n단가에 없는 코드 ${unk.length}: ${unk.slice(0, 8).join(", ")}` : ""}`,
|
|
);
|
|
await openSaved(r.saved_dir);
|
|
},
|
|
async xlsRegister(ctx) {
|
|
const f = await pickFile(".xlsx");
|
|
const r = await req("/api/conv/xls-register", {
|
|
template_path: curPath(ctx),
|
|
xlsx: { b64: f.b64 },
|
|
});
|
|
alert(`[엑셀 역등록] ${r.report.pname}\n${fmtRep(r.report)}`);
|
|
await openSaved(r.saved_dir);
|
|
},
|
|
async boqXlsx(ctx) {
|
|
const tb = (ctx.dbfData && ctx.dbfData.boq_table_name) || "";
|
|
download(
|
|
await req("/api/conv/boq-xlsx", { source_path: curPath(ctx), table: tb }, true),
|
|
`${pname(ctx)}_기준BOQ.xlsx`,
|
|
);
|
|
},
|
|
async jajeXlsx(ctx) {
|
|
download(
|
|
await req("/api/conv/jaje-xlsx", { source_path: curPath(ctx) }, true),
|
|
`${pname(ctx)}_자재기준자료.xlsx`,
|
|
);
|
|
},
|
|
async jajeImport(ctx) {
|
|
const f = await pickFile(".xlsx");
|
|
const r = await req("/api/conv/jaje-import", {
|
|
source_path: curPath(ctx),
|
|
xlsx: { b64: f.b64 },
|
|
});
|
|
const rows = ctx.dbfData.costn_rows || (ctx.dbfData.costn_rows = []);
|
|
for (const u of r.costn_updates) {
|
|
const row = rows.find((x) => String(x.CODE).trim() === u.CODE);
|
|
if (row) Object.assign(row, u);
|
|
else rows.push(Object.assign({}, u));
|
|
}
|
|
app().cascadeAndRender && app().cascadeAndRender();
|
|
alert(`[자재 기준자료 불러오기] 바뀐 자재 ${r.count}건 — [저장]으로 반영`);
|
|
},
|
|
async codeXlsx(ctx) {
|
|
download(
|
|
await req("/api/conv/code-xlsx", { source_path: curPath(ctx) }, true),
|
|
`${pname(ctx)}_코드변경표.xlsx`,
|
|
);
|
|
},
|
|
async codeChange(ctx) {
|
|
const how = prompt("코드 변경 — 1: 코드 변경표 엑셀 고르기 · 2: 직접 입력(옛=새;옛=새)", "1");
|
|
const body = { source_path: curPath(ctx) };
|
|
if (how === "1") body.xlsx = { b64: (await pickFile(".xlsx")).b64 };
|
|
else if (how === "2") {
|
|
const t = prompt("옛코드=새코드 ; 로 이음", "");
|
|
if (!t) return;
|
|
body.mapping = Object.fromEntries(
|
|
t.split(";").map((s) => s.split("=").map((x) => x.trim())),
|
|
);
|
|
} else return;
|
|
const r = await req("/api/conv/code-change", body);
|
|
alert(`[코드 변경] ${Object.keys(r.mapping).length}개 코드 · ${r.change_count}곳 바꿈`);
|
|
await openSaved(r.saved_dir);
|
|
},
|
|
async splitChange(ctx) {
|
|
if (!confirm("현재안(통합형이면 BOQ12)을 차수1로 하는 다음 변경 차수 STC 를 만들까요?"))
|
|
return;
|
|
const r = await req("/api/conv/split-change", { source_path: curPath(ctx) });
|
|
alert(
|
|
`[분리형 변경 STC] 공사 ${r.report.from_pcode} 차수 ${r.report.from_slot} → 새 파일 차수1 · ${r.saved_stc}`,
|
|
);
|
|
await openSaved(r.saved_dir);
|
|
},
|
|
async jb(ctx) {
|
|
const L = await req("/api/conv/jb-list");
|
|
const m = ctx.currentSourcePath
|
|
? await req("/api/conv/jb-match", { source_path: ctx.currentSourcePath })
|
|
: {};
|
|
const by = {};
|
|
for (const f of L.files)
|
|
by[`${f.kind} ${f.edition}`] = (by[`${f.kind} ${f.edition}`] || 0) + 1;
|
|
alert(
|
|
`[기준 제비율표 JB_*] ${L.files.length}개 (${L.dir})\n${Object.entries(by)
|
|
.map(([k, v]) => `${k}: ${v}`)
|
|
.join("\n")}\n\n[이 공사 RATE 표 ↔ 기준]\n` +
|
|
Object.entries(m)
|
|
.map(([t, v]) => `${t}: ${v.jb} ${v.exact ? "(같음)" : `(다른 칸 ${v.diff_cells})`}`)
|
|
.join("\n"),
|
|
);
|
|
},
|
|
};
|
|
|
|
async function run(act) {
|
|
try {
|
|
await ACTS[act](app().ctx);
|
|
} catch (err) {
|
|
alert("변환 실패: " + err.message);
|
|
}
|
|
}
|
|
|
|
[
|
|
["Act_ConvXmlImport", "조달청 XML 가져오기 (Conv_XML_ST8)", "xmlImport"],
|
|
["Act_ConvXmlSettings", "조달청 XML 변환 설정·내보내기 (uM_XML_Info)", "xmlSettings"],
|
|
["Act_ConvXlsBoq", "엑셀 → 내역 불러오기 (wM_From_XLS)", "xlsBoq"],
|
|
["Act_ConvXlsRegister", "엑셀 역등록 — 새 공사 (CONV_XLS_2_ST)", "xlsRegister"],
|
|
["Act_ConvBoqXlsx", "기준 BOQ 엑셀로 내보내기 (MK_BSCBOQ_EXL)", "boqXlsx"],
|
|
["Act_ConvJajeXlsx", "자재 기준자료 엑셀로 내보내기 (wM_JajeXLS)", "jajeXlsx"],
|
|
["Act_ConvJajeImport", "자재 기준자료 엑셀 불러오기 (wM_JajeXLS)", "jajeImport"],
|
|
["Act_ConvCodeXlsx", "코드 변경표 엑셀로 내보내기 (ST_U_ChgCode)", "codeXlsx"],
|
|
["Act_ConvCodeChange", "코드 일괄 변경 (ST_U_ChgCode)", "codeChange"],
|
|
["Act_ConvSplitChange", "분리형 변경 STC 만들기 — 차수 인계", "splitChange"],
|
|
["Act_ConvJb", "기준 제비율표 JB_* 보기", "jb"],
|
|
].forEach(([act, label, key]) => {
|
|
PLUG.actions[act] = () => run(key);
|
|
PLUG.menus.push({ menu: "m1", label, act });
|
|
});
|
|
})(window);
|