Files
Aislo/tools/est-test/native-jdl-compare.js
T
2026-11-12 13:33:50 +09:00

125 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 원본 「JDLExcel과 단가비교」(ActionJDLCompare, 숨은 단축키 Ctrl+Alt+J → TfrmJDLExcelCompare) — 일위대가·단가산출·기계경비 호표마다
// Excel 의 코드 열에서 그 코드를 품은 셀(Pos(코드, 셀) > 0)을 1행~(호표 수+50)행 안에서 찾아 같은 행 합계 열 값과 TC 를 비교한다.
// 차이(|TC − Excel|)가 있으면 표시, 못 찾으면 「없음」. 자료는 바꾸지 않는다(보기 전용). Excel 은 .xlsx·.xls 모두.
const EstJdlCompare = (() => {
const TABLES = [
["HeadTitleDat", "일위대가"],
["CostTitleDat", "단가산출"],
["MachTitleDat", "기계경비"],
];
function compare(rows, table, codeCol, totalCol) {
const limit = rows.length + 50;
return rows
.filter((r) => String(r.Code ?? "").trim())
.map((r) => {
const code = String(r.Code).trim();
let found = null;
for (let i = 0; i < Math.min(limit, table.length); i++) {
const cell = table[i]?.[codeCol];
if (cell != null && String(cell).includes(code)) {
found = Number(String(table[i][totalCol] ?? "").replace(/,/g, "")) || 0;
break;
}
}
const tc = Number(r.TC) || 0;
return {
Code: code,
Name: r.Name ?? "",
Size: r.Size ?? "",
Unite: r.Unite ?? "",
TC: tc,
OTC: found,
GN: found == null ? null : Math.abs(tc - found),
Checked: found == null || Math.abs(tc - found) > 1e-9,
};
});
}
// .xls(BIFF) 셀 → 행 배열
function xlsTables(sheets) {
const out = {};
for (const s of sheets) {
const table = [];
for (const [k, c] of Object.entries(s.cells)) {
const [r, col] = k.split(",").map(Number);
(table[r] ||= [])[col] = c.value;
}
out[s.name] = table;
}
return out;
}
function form(state) {
const c = (state.jdlCompare ||= {
tab: 0,
sheet: 1,
code: "A",
total: "H",
tables: null,
result: [],
}),
{ esc, number } = EstApp,
diff = c.result.filter((r) => r.Checked).length;
EstApp.showDialog(
"JDL엑셀 단가 비교",
`<div class="business-tools">${TABLES.map(([, l], i) => `<button data-action="jdl-compare-tab" data-i="${i}"${i === c.tab ? ' style="font-weight:bold"' : ""}>${l}</button>`).join("")}</div><div class="form-fields"><label>시트번호<input name="jc-sheet" type="number" min="1" value="${c.sheet}"></label><label>코드칼럼<input name="jc-code" value="${esc(c.code)}" size="3"></label><label>합계셀칼럼<input name="jc-total" value="${esc(c.total)}" size="3"></label></div><button data-action="jdl-compare-open">엑셀파일열기</button> <button data-action="jdl-compare-run">검 색</button> ${c.tables ? `시트 ${Object.keys(c.tables).length}개` : "Excel 을 여세요"} · 다른 금액·없음 ${diff}건<div style="max-height:380px;overflow:auto"><table class="sheet"><thead><tr><th>코드</th><th>품명</th><th>규격</th><th>단위</th><th>EST 금액</th><th>Excel 금액</th><th>차이</th></tr></thead><tbody>${c.result.map((r) => `<tr${r.Checked ? ' style="background:#fde2e2"' : ""}><td>${esc(r.Code)}</td><td style="text-align:left">${esc(r.Name)}</td><td>${esc(r.Size)}</td><td>${esc(r.Unite)}</td><td>${number(r.TC)}</td><td>${r.OTC == null ? "없음" : number(r.OTC)}</td><td>${r.GN == null ? "" : number(r.GN)}</td></tr>`).join("")}</tbody></table></div>`,
);
}
function read(c) {
const v = (n) => document.querySelector(`#dialog-content [name="${n}"]`)?.value;
c.sheet = Math.max(1, Number(v("jc-sheet")) || 1);
c.code = String(v("jc-code") || "A").toUpperCase();
c.total = String(v("jc-total") || "H").toUpperCase();
}
async function run(name, state, node) {
if (name === "jdl-compare") return form(state);
const c = state.jdlCompare;
read(c);
if (name === "jdl-compare-tab") {
c.tab = Number(node.dataset.i);
c.result = [];
} else if (name === "jdl-compare-open") {
const input = document.createElement("input");
input.type = "file";
input.accept = ".xlsx,.xls";
input.addEventListener("change", async () => {
try {
const file = input.files[0];
if (!file) return;
const body = await file.arrayBuffer();
c.tables = /\.xls$/i.test(file.name)
? xlsTables(await EstApp.api("/api/read-xls", { method: "POST", body }))
: EstExcelMap.sheetTables(
await EstApp.api("/api/import-xlsx", { method: "POST", body }),
);
} catch (error) {
state.notice = error.message;
}
form(state);
});
return input.click();
} else if (name === "jdl-compare-run") {
if (!c.tables) throw Error("엑셀파일을 먼저 여세요.");
const table = Object.values(c.tables)[c.sheet - 1];
if (!table) throw Error(`시트 ${c.sheet} 이 없습니다.`);
const col = (x) => EstExcelMap.columnIndex(x);
c.result = compare(
state.project.tables[TABLES[c.tab][0]] || [],
table,
col(c.code),
col(c.total),
);
}
form(state);
}
const names = new Set(["jdl-compare", "jdl-compare-tab", "jdl-compare-open", "jdl-compare-run"]);
function handle(name, state, node) {
if (!names.has(name)) return false;
run(name, state, node).catch((error) => {
state.notice = error.message;
EstApp.render();
});
return true;
}
return { compare, xlsTables, handle };
})();