- 서버 보관함 data/forms.json 에 원가계산서 양식 등록·삭제·목록 - 양식 열기·파일 내려받기/불러오기, 금액 제외하고 열 때 다시 계산 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
5.3 KiB
JavaScript
112 lines
5.3 KiB
JavaScript
// 원본 원가계산서 양식(TFRMWONGABASE popupSaveAs·popupFormats·popupLoadFrom)과 양식목록(TFRMFORMATFILELIST)
|
|
// — 현재 원가 표를 이름 붙여 서버 보관함(data/forms.json)에 등록하고, 목록에서 열기·삭제, 파일로 내려받기·불러오기.
|
|
const EstFormsLibrary = (() => {
|
|
// 원가 표 양식으로 남길 필드 — 금액(Cost)은 계산 결과라 남기지 않는다.
|
|
function strip(rows) {
|
|
return rows.map((row) => {
|
|
const { Cost, ...rest } = row;
|
|
return rest;
|
|
});
|
|
}
|
|
function validRows(rows) {
|
|
if (!Array.isArray(rows) || !rows.length) throw Error("원가계산서 양식에 행이 없습니다.");
|
|
if (rows.some((row) => !row || typeof row !== "object" || Array.isArray(row)))
|
|
throw Error("원가계산서 양식 행이 올바르지 않습니다.");
|
|
return rows;
|
|
}
|
|
|
|
async function list() {
|
|
return (await EstApp.api("/api/forms")).filter((f) => f.kind === "wonga");
|
|
}
|
|
async function show(state) {
|
|
const { esc } = EstApp,
|
|
forms = await list();
|
|
EstApp.showDialog(
|
|
"원가계산서 양식 목록",
|
|
`<p>현재 원가계산서의 항목·수식·요율을 양식으로 등록하고, 다른 공사에서 열어 씁니다. 금액은 열 때 다시 계산합니다.</p><button data-action="wonga-form-register">현재 원가계산서 등록</button> <button data-action="wonga-form-export">파일로 내려받기</button> <button data-action="wonga-form-import">파일에서 불러오기</button><table class="sheet" style="margin-top:8px"><thead><tr><th>양식 이름</th><th>행</th><th>등록</th><th>작업</th></tr></thead><tbody>${forms.map((f) => `<tr><td>${esc(f.name)}</td><td>${f.data.length}</td><td>${esc(f.saved.slice(0, 10))}</td><td><button data-action="wonga-form-open" data-name="${esc(f.name)}">열기</button><button data-action="wonga-form-delete" data-name="${esc(f.name)}">삭제</button></td></tr>`).join("") || '<tr><td colspan="4">등록된 양식이 없습니다.</td></tr>'}</tbody></table>`,
|
|
);
|
|
state.formsLibrary = forms;
|
|
}
|
|
function apply(state, rows, name) {
|
|
validRows(rows);
|
|
if (!confirm(`현재 원가계산서를 「${name}」 양식으로 바꾸시겠습니까?`)) return;
|
|
EstApp.byId("dialog").close();
|
|
// 도급액 고정은 저장된 금액을 그대로 쓰므로 금액 없는 양식에서는 풀어야 다시 계산된다(기초 뼈대와 같음).
|
|
const fixed = state.project.options.TotalFix;
|
|
EstApp.change((project) => {
|
|
project.options.TotalFix = false;
|
|
project.tables.Wonga = structuredClone(rows);
|
|
});
|
|
state.notice = `원가계산서를 「${name}」 양식으로 바꿨습니다.${fixed ? " 도급액 고정은 풀었으니 필요하면 다시 맞추세요." : ""}`;
|
|
}
|
|
|
|
async function run(name, state, node) {
|
|
if (name === "wonga-forms") await show(state);
|
|
else if (name === "wonga-form-register") {
|
|
const title = prompt("양식 이름", state.project.name || "원가계산서");
|
|
if (!title) return;
|
|
const data = strip(validRows(state.project.tables.Wonga));
|
|
await EstApp.api("/api/forms", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ kind: "wonga", name: title, data }),
|
|
});
|
|
await show(state);
|
|
} else if (name === "wonga-form-open") {
|
|
const form = state.formsLibrary?.find((f) => f.name === node.dataset.name);
|
|
if (!form) throw Error("양식을 찾을 수 없습니다. 목록을 다시 여세요.");
|
|
apply(state, form.data, form.name);
|
|
} else if (name === "wonga-form-delete") {
|
|
if (!confirm(`「${node.dataset.name}」 양식을 지우시겠습니까?`)) return;
|
|
await EstApp.api("/api/forms/delete", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ kind: "wonga", name: node.dataset.name }),
|
|
});
|
|
await show(state);
|
|
} else if (name === "wonga-form-export") {
|
|
const data = strip(validRows(state.project.tables.Wonga));
|
|
EstApp.download(
|
|
`${state.project.name || "원가계산서"}.wonga.json`,
|
|
JSON.stringify({ kind: "est-web-wonga-1", rows: data }, null, 2),
|
|
);
|
|
} else if (name === "wonga-form-import") {
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.accept = ".json";
|
|
input.addEventListener("change", async () => {
|
|
try {
|
|
const file = input.files[0];
|
|
if (!file) return;
|
|
const parsed = JSON.parse(await file.text());
|
|
if (parsed?.kind !== "est-web-wonga-1") throw Error("원가계산서 양식 파일이 아닙니다.");
|
|
apply(state, parsed.rows, file.name);
|
|
} catch (error) {
|
|
state.notice = error.message;
|
|
}
|
|
EstApp.render();
|
|
});
|
|
input.click();
|
|
}
|
|
}
|
|
const names = new Set([
|
|
"wonga-forms",
|
|
"wonga-form-register",
|
|
"wonga-form-open",
|
|
"wonga-form-delete",
|
|
"wonga-form-export",
|
|
"wonga-form-import",
|
|
]);
|
|
// 동작 사슬은 동기 — 이름만 먼저 가리고 서버 요청은 뒤에서 돌린다.
|
|
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 { strip, validRows, handle };
|
|
})();
|