- 원본 처리 근거대로 품명·규격·단위 정규화 맞춤으로 현재 공사 단가 표에 연결 - 9개 선택·Over Write·노임 맞바꿈·전체연결 끊기 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
141 lines
6.1 KiB
JavaScript
141 lines
6.1 KiB
JavaScript
// 원본 데이타 자동연결(ActionAutoLink Ctrl+Alt+L 0x10d54f4 → TfrmDataAutoLink 적용 0x10d3008, 행 처리 10d2a48, 맞춤 10e0e58).
|
|
// 내역서·일위대가·기계경비 화면에서 현재 공사의 노임·자재·견적·표준·기계경비·단가산출·일위대가 표로 품명(·규격·단위)을 맞춰 연결.
|
|
// 정규화: 빈칸·|·(·) 제거, 단위는 「당」·[·] 를 한 번씩 더 지움. 노임은 품명만(못 찾으면 규격으로 찾고 품명·규격을 맞바꿈),
|
|
// 규격·단위 비면 품명+규격, 단위 있으면 품명+규격+단위. 노임·기계는 단위 무시. 처음 맞는 행. Over Write 끄면 이미 연결된 행은 건너뜀.
|
|
// 쓰는 값: Code, TU(=원본 TC), 내역·일위대가는 NU·GU·JU·SU(=원본 NC·GC·JC·SC), LinkIndex, 원본 SCheck 3 이면 SCheck 3.
|
|
// 「전체연결 끊기」는 원본처럼 모든 행 LinkIndex 를 0 으로 한다(조건 없음).
|
|
const EstAutoLink = (() => {
|
|
const num = (value) => Number(value) || 0;
|
|
const sources = {
|
|
6: "LaborcDat",
|
|
5: "StuffcDat",
|
|
101: "EstiMateDat",
|
|
21: "SilJukDat",
|
|
105: "MachTitleDat",
|
|
104: "CostTitleDat",
|
|
103: "HeadTitleDat",
|
|
};
|
|
const order = [6, 5, 101, 21, 105, 104, 103];
|
|
const tags = { EstMainDat: 1, HeadMainDat: 2, MachMainDat: 4 };
|
|
const norm = (s) => String(s ?? "").replace(/[\s|()]/g, "");
|
|
const unit = (s) => norm(s).replace("당", "").replace("[", "").replace("]", "");
|
|
|
|
function key(row, tag) {
|
|
let name = row.Name ?? "",
|
|
size = row.Size ?? "";
|
|
const unite = row.Unite ?? "";
|
|
if (tag === 4) {
|
|
name = String(name).replace(/[\s|]/g, "");
|
|
size = String(size).replace(/[\s|]/g, "");
|
|
if ([5, 212].includes(num(row.LinkIndex)) && name.includes("주연료")) {
|
|
name = size;
|
|
size = "";
|
|
}
|
|
}
|
|
return { name: String(name).trim(), size: String(size).trim(), unite: String(unite).trim() };
|
|
}
|
|
|
|
function find(project, kind, k) {
|
|
const rows = project.tables[sources[kind]] || [],
|
|
useUnit = ![6, 105].includes(kind) && k.unite;
|
|
const mode = !k.size && !useUnit ? (kind === 6 ? "name" : "size") : useUnit ? "unit" : "size";
|
|
return rows.find((src) => {
|
|
if (!src.Code) return false;
|
|
if (kind !== 101 && src.LinkIndex != null && num(src.LinkIndex) !== kind) return false;
|
|
if (src.DataKind != null && num(src.DataKind) !== 1) return false;
|
|
if (norm(src.Name) !== norm(k.name)) return false;
|
|
if (mode === "name" || kind === 6) return true;
|
|
if (norm(src.Size) !== norm(k.size)) return false;
|
|
if (mode === "size" || kind === 105) return true;
|
|
return unit(src.Unite) === unit(k.unite);
|
|
});
|
|
}
|
|
|
|
function linkKind(project, view, kind, overwrite) {
|
|
const tag = tags[view],
|
|
rows = project.tables[view] || [];
|
|
let linked = 0;
|
|
for (const row of rows) {
|
|
if ((tag === 2 || tag === 4) && num(row.Index) > 0) continue;
|
|
if (tag === 1 && (num(row.Level) > 0 || [54, 55, 58].includes(num(row.LinkIndex)))) continue;
|
|
const k = key(row, tag);
|
|
if (!k.name && !k.size && !k.unite) continue;
|
|
if (!overwrite && num(row.LinkIndex)) continue;
|
|
let src = find(project, kind, kind === 6 ? { name: k.name, size: "", unite: "" } : k);
|
|
if (!src && kind === 6 && k.size) {
|
|
src = find(project, kind, { name: k.size, size: "", unite: "" });
|
|
if (src) [row.Name, row.Size] = [row.Size, row.Name];
|
|
}
|
|
if (!src) continue;
|
|
row.Code = src.Code;
|
|
row.TU = num(src.TC);
|
|
if (tag === 1 || tag === 2) {
|
|
row.NU = num(src.NC);
|
|
row.GU = num(src.GC);
|
|
row.JU = num(src.JC);
|
|
row.SU = num(src.SC);
|
|
}
|
|
row.LinkIndex = kind;
|
|
if (num(src.SCheck) === 3) row.SCheck = 3;
|
|
linked++;
|
|
}
|
|
return linked;
|
|
}
|
|
|
|
function run(project, view, choice, overwrite) {
|
|
if (!tags[view]) throw Error("내역서·일위대가·기계경비 화면에서 자동연결하세요.");
|
|
if (choice === "unlink") {
|
|
for (const row of project.tables[view] || []) row.LinkIndex = 0;
|
|
return (project.tables[view] || []).length;
|
|
}
|
|
const kinds =
|
|
choice === "all"
|
|
? order.filter((k) => k !== 105 || [1, 2].includes(tags[view]))
|
|
: [Number(choice)];
|
|
return kinds.reduce((n, kind) => n + linkKind(project, view, kind, overwrite), 0);
|
|
}
|
|
|
|
const choices = [
|
|
["unlink", "전체연결 끊기"],
|
|
["all", "전체자동 연결"],
|
|
["6", "노임단가 연결"],
|
|
["5", "자재단가 연결"],
|
|
["101", "견적단가 연결"],
|
|
["21", "표준단가 연결"],
|
|
["105", "기계경비 연결"],
|
|
["104", "단가산출 연결"],
|
|
["103", "일위대가 연결"],
|
|
];
|
|
function open(state) {
|
|
if (!tags[state.view]) throw Error("내역서·일위대가·기계경비 화면에서 자동연결하세요.");
|
|
EstApp.showDialog(
|
|
"데이타 자동연결",
|
|
`<div class="form-fields"><label>연결데이타 선택<select name="al-choice">${choices.map(([v, l]) => `<option value="${v}" ${v === "all" ? "selected" : ""}>${l}</option>`).join("")}</select></label><label><input type="checkbox" name="al-over">Over Write</label></div><button data-action="autolink-apply">적 용</button><p id="autolink-result" role="status"></p>`,
|
|
);
|
|
}
|
|
function apply(state) {
|
|
const choice = document.querySelector('#dialog-content [name="al-choice"]').value,
|
|
overwrite = document.querySelector('#dialog-content [name="al-over"]').checked;
|
|
const ask =
|
|
choice === "unlink"
|
|
? "데이타를 연결을 끊을까요?"
|
|
: choice === "all"
|
|
? "전체 데이타를 연결 시킬까요?"
|
|
: null;
|
|
if (ask && !confirm(ask)) return;
|
|
let count;
|
|
EstApp.change((project) => (count = run(project, state.view, choice, overwrite)));
|
|
EstApp.byId("autolink-result").textContent =
|
|
choice === "unlink" ? `${count}행의 연결을 끊었습니다.` : `${count}행을 연결했습니다.`;
|
|
}
|
|
|
|
function handle(name, state) {
|
|
if (name === "autolink") open(state);
|
|
else if (name === "autolink-apply") apply(state);
|
|
else return false;
|
|
return true;
|
|
}
|
|
|
|
return { run, find, key, handle };
|
|
})();
|