- 내역 행이 쓰는 기초자료와 일위대가·단가산출·기계경비 호표를 하위까지 모아 다른 공사로 옮김 - 같은 코드는 붙여넣을 공사 자료 유지, 비어 있는 기계경비 적용기준만 함께 옮김 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
155 lines
6.1 KiB
JavaScript
155 lines
6.1 KiB
JavaScript
// 원본 다른 공사 불러오기·복사(TFRMOTHERLOAD)와 프로젝트 추가(mnuFileLoadProjectAs)의 하위 자료 보존.
|
|
// 내역 행이 쓰는 자재·노임·실적·견적 행과 일위대가·단가산출·기계경비 호표를 하위까지 모아 함께 옮긴다.
|
|
// 붙여넣을 공사에 같은 코드가 있으면 그 공사의 자료를 그대로 쓰고(덮어쓰지 않음) 개수만 알린다.
|
|
// 기계경비 적용기준은 붙여넣을 공사에 하나도 없을 때만 함께 옮긴다.
|
|
const EstTransfer = (() => {
|
|
const flat = { 5: "StuffcDat", 6: "LaborcDat", 21: "SilJukDat", 101: "EstiMateDat" };
|
|
const grouped = {
|
|
103: ["HeadTitleDat", "HeadMainDat"],
|
|
104: ["CostTitleDat", "CostMainDat"],
|
|
105: ["MachTitleDat", "MachMainDat"],
|
|
};
|
|
const num = (value) => Number(value) || 0;
|
|
|
|
function collect(source, rows) {
|
|
const deps = { flat: {}, groups: {} },
|
|
seen = new Set(),
|
|
queue = [...rows];
|
|
while (queue.length) {
|
|
const row = queue.shift(),
|
|
kind = num(row.LinkIndex),
|
|
code = row.Code;
|
|
if (!code || seen.has(`${kind}:${code}`)) continue;
|
|
if (flat[kind]) {
|
|
const found = (source.tables[flat[kind]] || []).find((r) => r.Code === code);
|
|
if (!found) continue;
|
|
seen.add(`${kind}:${code}`);
|
|
(deps.flat[flat[kind]] ||= []).push(structuredClone(found));
|
|
} else if (grouped[kind]) {
|
|
const [titleTable, mainTable] = grouped[kind],
|
|
group = EstNative.groups(source.tables[mainTable] || []).get(code);
|
|
if (!group) continue;
|
|
seen.add(`${kind}:${code}`);
|
|
const title = (source.tables[titleTable] || []).find((r) => r.Code === code);
|
|
(deps.groups[mainTable] ||= []).push({
|
|
code,
|
|
titleTable,
|
|
title: title ? structuredClone(title) : null,
|
|
rows: structuredClone(group.rows),
|
|
});
|
|
queue.push(...group.rows.slice(1));
|
|
}
|
|
}
|
|
// 기계경비 적용기준은 공사 전체 설정 — 붙여넣을 공사에 없을 때만 함께 옮긴다.
|
|
if (deps.groups.MachMainDat?.length && source.tables.MachGijunDat?.length)
|
|
deps.criteria = structuredClone(source.tables.MachGijunDat);
|
|
return deps;
|
|
}
|
|
|
|
function nextPkID(project, table, extra = []) {
|
|
return (
|
|
Math.max(
|
|
0,
|
|
...(project.tables[table] || []).map((r) => num(r.PkID)),
|
|
...extra.map((r) => num(r.PkID)),
|
|
) + 1
|
|
);
|
|
}
|
|
|
|
function merge(target, deps) {
|
|
const added = [],
|
|
kept = [];
|
|
for (const [table, list] of Object.entries(deps.flat)) {
|
|
const rows = (target.tables[table] ||= []);
|
|
for (const row of list) {
|
|
if (rows.some((r) => r.Code === row.Code)) {
|
|
kept.push(row.Code);
|
|
continue;
|
|
}
|
|
row.PkID = nextPkID(target, table);
|
|
row.Index = Math.max(0, ...rows.map((r) => num(r.Index))) + 1;
|
|
rows.push(row);
|
|
added.push(row.Code);
|
|
}
|
|
}
|
|
for (const [table, list] of Object.entries(deps.groups)) {
|
|
const rows = (target.tables[table] ||= []);
|
|
for (const group of list) {
|
|
if (EstNative.groups(rows).has(group.code)) {
|
|
kept.push(group.code);
|
|
continue;
|
|
}
|
|
let pk = nextPkID(target, table);
|
|
const index = Math.max(0, ...rows.map((r) => num(r.Index))) + 1;
|
|
group.rows.forEach((row, i) => {
|
|
row.PkID = pk++;
|
|
if (i === 0) row.Index = index;
|
|
});
|
|
rows.push(...group.rows);
|
|
if (group.title) {
|
|
const titles = (target.tables[group.titleTable] ||= []);
|
|
if (!titles.some((r) => r.Code === group.code)) {
|
|
group.title.PkID = nextPkID(target, group.titleTable);
|
|
group.title.Index = index;
|
|
titles.push(group.title);
|
|
}
|
|
}
|
|
added.push(group.code);
|
|
}
|
|
}
|
|
let criteria = false;
|
|
if (deps.criteria && !target.tables.MachGijunDat?.length) {
|
|
target.tables.MachGijunDat = deps.criteria;
|
|
criteria = true;
|
|
}
|
|
return { added, kept, criteria };
|
|
}
|
|
|
|
function paste(target, rows, at) {
|
|
const list = target.tables.EstMainDat;
|
|
let pk = nextPkID(target, "EstMainDat");
|
|
const copies = structuredClone(rows).map((row) => {
|
|
row.PkID = pk++;
|
|
row.NewItem = true;
|
|
delete row.PreviousCodes;
|
|
return row;
|
|
});
|
|
list.splice(at, 0, ...copies);
|
|
return copies.length;
|
|
}
|
|
|
|
// 화면
|
|
let clipboard = null;
|
|
function handle(name, state) {
|
|
if (name === "copy-with-links") {
|
|
if (state.view !== "EstMainDat") throw Error("내역서 화면에서 행을 고르세요.");
|
|
const rows = state.project.tables.EstMainDat,
|
|
picked = [...state.selection].sort((a, b) => a - b).map((i) => rows[i]);
|
|
if (!picked.length) throw Error("복사할 내역 행을 선택하세요.");
|
|
clipboard = {
|
|
rows: structuredClone(picked),
|
|
deps: collect(state.project, picked),
|
|
from: state.project.name,
|
|
};
|
|
const count =
|
|
Object.values(clipboard.deps.flat).flat().length +
|
|
Object.values(clipboard.deps.groups).flat().length;
|
|
state.notice = `내역 ${picked.length}행과 연결 자료 ${count}건을 복사했습니다. 다른 공사를 열고 붙여넣으세요.`;
|
|
} else if (name === "paste-with-links") {
|
|
if (!clipboard) throw Error("먼저 「연결 자료와 함께 복사」를 하세요.");
|
|
if (state.view !== "EstMainDat") throw Error("내역서 화면에서 붙여넣으세요.");
|
|
const rows = state.project.tables.EstMainDat,
|
|
at = ([...state.selection].sort((a, b) => a - b).at(-1) ?? rows.length - 1) + 1;
|
|
let result;
|
|
EstApp.change((project) => {
|
|
result = merge(project, structuredClone(clipboard.deps));
|
|
paste(project, clipboard.rows, at);
|
|
});
|
|
state.notice = `내역 ${clipboard.rows.length}행을 붙여넣었습니다. 연결 자료 ${result.added.length}건 추가${result.kept.length ? `, 같은 코드 ${result.kept.length}건은 이 공사 자료를 그대로 씀` : ""}${result.criteria ? ", 비어 있던 기계경비 적용기준도 함께 옮김" : ""}.`;
|
|
} else return false;
|
|
return true;
|
|
}
|
|
|
|
return { collect, merge, paste, handle };
|
|
})();
|