Files
Aislo/tools/est-test/native-annual-copy.js
T
eomsangdonandClaude Opus 5.5 188b50fa2f feat(est-test): 연차 데이타 복사 옵션(덮어쓰기·열 삽입, 복사내용 고르기)
- 고른 내역 행을 차수 하나의 놓을 행부터 복사
- 수량·단가금액·명칭규격 고르기, 설계변경 상단/하단, 행 금액 다시 셈
- 내역서 도구줄과 연차 화면에 단추

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 19:16:19 +09:00

114 lines
5.7 KiB
JavaScript

// 원본 연차공사 「데이타 복사 옵션」(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) =>
`<label><input type="checkbox" data-ac="${k}" ${o[k] ? "checked" : ""}> ${label}</label>`;
EstApp.showDialog(
"데이타 복사 옵션",
`<div class="form-fields"><label>복사할 내역 행(예 1-3,5) <input data-ac="rows" value="${esc(range)}"></label><label>놓을 행 번호 <input type="number" min="1" data-ac="target" value="${(picked[0] ?? 0) + 1}"></label>` +
`<label>복사방법 <select data-ac="insert"><option value="0">덮어쓰기</option><option value="1" ${o.insert ? "selected" : ""}>열 삽입후 내용복사</option></select></label>` +
`<fieldset><legend>복사내용</legend>${check("gn", "수량")}${check("cost", "단가, 금액")}${check("name", "명칭, 규격")}</fieldset>` +
(change
? `<label>설계변경 <select data-ac="top"><option value="1">상단데이타 복사</option><option value="0">하단데이타 복사</option></select></label>`
: "") +
`<fieldset><legend>복사위치</legend>${project.annual.map((y, i) => `<label><input type="radio" name="ac-year" value="${i}"> ${esc(y.name)}</label>`).join("")}</fieldset></div>` +
`<button data-action="annual-copy-apply">적 용</button>`,
);
}
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 };
})();