// 원본 연차공사 「데이타 복사 옵션」(TfrmMultiDataCopyMethod btnApply 0xedaba4 → TDataCopyMulti eedc54 — spec_pb2 §1): // 내역서 행을 골라 차수 하나의 놓을 행부터 차례로 옮긴다. 복사방법 = 덮어쓰기 · 열 삽입후 내용복사(놓을 자리에 빈 행을 넣어 // 모든 차수가 한 줄 밀림). 복사내용 = 명칭·규격(Name·Size·Unite) · 수량(GN) · 단가·금액(TU·TC·NU·NC·GU·GC·JU·JC·SU·SC). // 설계변경 내역이면 상단(당초 = 「_」 짝 칸) / 하단(변경 = 지금 값). 고른 차수 줄이 없으면 지금 내역 줄을 바탕으로 하고, // 행마다 금액을 수량×단가로 다시 셈(f1a078). 끝 행을 넘는 나머지는 버림. 코드·연결 등 다른 칸은 그대로. const EstAnnualCopy = (() => { const num = (v) => Number(v) || 0; const NAME = ["Name", "Size", "Unite"], COST = ["TU", "TC", "NU", "NC", "GU", "GC", "JU", "JC", "SU", "SC"]; const KEY = "est-annual-copy"; function apply(project, { sources, target, year, insert, name, gn, cost, top }) { const main = project.tables.EstMainDat, chosen = project.annual?.[year]; if (!chosen) throw Error("입력할 차수를 선택하세요."); if (!(target >= 0 && target <= main.length)) throw Error("놓을 행이 없습니다."); const picked = sources.map((i) => structuredClone(main[i])).filter(Boolean); if (insert) { main.splice(target, 0, {}); for (const y of project.annual) if (Array.isArray(y.rows)) y.rows.splice(target, 0, null); } chosen.rows ||= []; let count = 0; picked.forEach((src, k) => { const t = target + k; if (t >= main.length) return; const read = (f) => (top && src[f + "_"] != null ? src[f + "_"] : src[f]); const row = chosen.rows[t] ? { ...chosen.rows[t] } : structuredClone(main[t]); if (name) for (const f of NAME) row[f] = read(f) ?? ""; if (gn) row.GN = num(read("GN")); if (cost) for (const f of COST) row[f] = num(read(f)); // 행 금액 계산: 비목 금액 = 수량 × 비목 단가, 합계 = 비목 합 for (const p of ["N", "G", "J", "S"]) row[p + "C"] = Math.trunc(num(row.GN) * num(row[p + "U"])); row.TC = ["NC", "GC", "JC", "SC"].reduce((s, f) => s + num(row[f]), 0); row.TU = ["NU", "GU", "JU", "SU"].reduce((s, f) => s + num(row[f]), 0); while (chosen.rows.length < t) chosen.rows.push(null); chosen.rows[t] = row; count++; }); return count; } // 번호 목록 "1-3,5" → 0부터 차례 function parse(text) { const out = []; for (const part of String(text) .split(/[,\s]+/) .filter(Boolean)) { const [a, b] = part.split("-").map(Number); if (!Number.isInteger(a)) throw Error(`행 번호 오류: ${part}`); for (let i = a; i <= (Number.isInteger(b) ? b : a); i++) out.push(i - 1); } return out; } function dialog(state) { const project = state.project, { esc } = EstApp; if (!project.annual?.length) throw Error("차수를 먼저 추가하세요."); const saved = JSON.parse(localStorage.getItem(KEY) || "{}"), o = { insert: false, gn: true, cost: true, name: true, ...saved }; const picked = state.view === "EstMainDat" ? [...state.selection].sort((a, b) => a - b) : []; const range = picked.length ? picked.map((i) => i + 1).join(",") : "1"; const change = project.options.Mode === "설계변경"; const check = (k, label) => ``; EstApp.showDialog( "데이타 복사 옵션", `
` + `` + `
복사내용${check("gn", "수량")}${check("cost", "단가, 금액")}${check("name", "명칭, 규격")}
` + (change ? `` : "") + `
복사위치${project.annual.map((y, i) => ``).join("")}
` + ``, ); } function run(state) { const box = document.getElementById("dialog-content"), get = (k) => box.querySelector(`[data-ac="${k}"]`); const year = box.querySelector('[name="ac-year"]:checked'); if (!year) throw Error("입력할 차수를 선택하세요."); const opts = { insert: get("insert").value === "1", gn: get("gn").checked, cost: get("cost").checked, name: get("name").checked, }; localStorage.setItem(KEY, JSON.stringify(opts)); let count = 0; EstApp.change( (project) => (count = apply(project, { ...opts, sources: parse(get("rows").value), target: Number(get("target").value) - 1, year: Number(year.value), top: get("top")?.value === "1", })), ); EstApp.byId("dialog").close(); state.notice = `${count}행을 복사했습니다.`; } function handle(name, state) { if (name === "annual-copy") dialog(state); else if (name === "annual-copy-apply") run(state); else return false; return true; } return { apply, parse, handle }; })();