- DBF 고친 칸만 다시 씀(한글 깨짐 · 2칸을 1칸으로 · 3칸 87.745% 덮어쓰기 없앰) · 단가 → 내역 행 → 소계 → 직공비 다시 셈 · 행 끼우기·지우기 - SIxMemo(COSTN.SMT · PRJLST.SMT) 블록 구조 유지 · 넘치면 뒤에 붙이고 가리키는 값 갱신 - STC 다시 묶기(같은 파일은 원본 바이트) · 검사 · 자동 백업·복원 · 새 공사 · 복사등록(차수 인계) · 해당분 압축 - BDQTY 역변환(쓰기 준비) · 모듈 dbf_rw · dbf_memo · dbf_crypt · stc_archive · stc_save · stc_read · stc_project · api_file · scr_file - 지킴 시험 test_stmate_file_roundtrip · file_project · file_payload — 실무 STC 7건 왕복 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
147 lines
5.5 KiB
JavaScript
147 lines
5.5 KiB
JavaScript
/**
|
|
* STMATE w 8.0 — 파일 작업 끼워 넣기 (scr_file.js) · 공사목록표 단추와 1번 메뉴
|
|
* 복사등록(차수/단계 변환) · 해당분만 압축(.STC) · 새 공사 · STC 구조·버전 검사(Chk_STC_Ver) · 백업·복원(wM_D_Restore)
|
|
* 서버: api_file.py — 쓰기는 work_dbf/ 아래만 · 단추는 data-file-act 로 문서 전체에서 받음
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
|
|
const PLUG = (global.STMATE_PLUGINS = global.STMATE_PLUGINS || {
|
|
screens: {},
|
|
actions: {},
|
|
menus: [],
|
|
});
|
|
const app = () => global.STMATE_APP;
|
|
|
|
async function post(url, body) {
|
|
const res = await fetch(url, {
|
|
method: body === undefined ? "GET" : "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
if (!res.ok) throw new Error(await res.text());
|
|
return res.json();
|
|
}
|
|
|
|
const curPath = (ctx) => {
|
|
if (!ctx.currentSourcePath) throw new Error("먼저 프로젝트를 불러오세요.");
|
|
return ctx.currentSourcePath;
|
|
};
|
|
const pickedPcode = (ctx) => {
|
|
const el = document.querySelector('input[name="prj-pcode"]:checked');
|
|
const pc = el ? el.value : ctx.uiState.filePcode || "11";
|
|
ctx.uiState.filePcode = pc;
|
|
return pc;
|
|
};
|
|
|
|
async function openSaved(path) {
|
|
const a = app();
|
|
const src = await post("/api/sources");
|
|
if (src && src.projects) a.ctx.sourcesData = src;
|
|
const sel = document.getElementById("top-project-select");
|
|
if (sel && src && src.projects)
|
|
sel.innerHTML = src.projects
|
|
.map((p) => `<option value="${p.path}">${p.name}</option>`)
|
|
.join("");
|
|
await a.loadProjectFromServer(path, "wPrjList");
|
|
}
|
|
|
|
const ACTS = {
|
|
async check(ctx) {
|
|
const r = await post("/api/file/check", { source_path: curPath(ctx) });
|
|
const t = Object.entries(r.tables || {})
|
|
.map(([n, h]) => `${n} v${h.ver.toString(16)} ${h.nfield}칸 ${h.nrec}행`)
|
|
.join("\n");
|
|
alert(
|
|
`[STC 검사] ${r.ok ? "이상 없음" : "문제 있음"} · ${r.edition}\n${(r.problems || []).join("\n")}\n\n${t}`,
|
|
);
|
|
},
|
|
async pack(ctx) {
|
|
const pc = pickedPcode(ctx);
|
|
const name = prompt(
|
|
`BOQ${pc} 공사만 담은 STC 이름:`,
|
|
`${ctx.state.projectMeta.pname || "공사"}_${pc}`,
|
|
);
|
|
if (!name) return;
|
|
const r = await post("/api/file/pack", {
|
|
source_path: curPath(ctx),
|
|
pcode: pc,
|
|
target_name: name,
|
|
});
|
|
alert(`[해당분만 압축] ${r.saved_stc} (${r.stc_size}B)`);
|
|
},
|
|
async copy(ctx) {
|
|
const pc = pickedPcode(ctx);
|
|
const mode = prompt(
|
|
`BOQ${pc} 복사등록 방식 — copy(그대로) · change(변경 차수 변환) · contract(계약 변환):`,
|
|
"change",
|
|
);
|
|
if (!mode) return;
|
|
const body = { source_path: curPath(ctx), src_pcode: pc, mode: mode.trim() };
|
|
if (body.mode === "contract") {
|
|
const r = prompt("단가 적용율 % — 재료,노무,경비:", "87.745,87.745,87.745");
|
|
if (!r) return;
|
|
const [M, L, S] = r.split(",").map((x) => parseFloat(x) || 0);
|
|
body.rates = { M, L, S };
|
|
}
|
|
const res = await post("/api/file/copy-register", body);
|
|
alert(`[복사등록] 새 공사 BOQ${res.new_pcode} · ${res.saved_dir}`);
|
|
await openSaved(res.saved_dir);
|
|
},
|
|
async new(ctx) {
|
|
const pname = prompt("새 공사명:", "");
|
|
if (!pname) return;
|
|
const res = await post("/api/file/new", { template_path: curPath(ctx), pname });
|
|
alert(`[새 공사] 틀(요율·서식·설정)은 지금 공사에서, 내역·단가는 빈 채로 · ${res.saved_dir}`);
|
|
await openSaved(res.saved_dir);
|
|
},
|
|
async backups(ctx) {
|
|
const nm = (ctx.state.projectMeta.pname || "").slice(0, 28);
|
|
const r = await post(`/api/file/backups?name=${encodeURIComponent(nm)}`);
|
|
const list = r.backups || [];
|
|
if (!list.length) return alert("[백업] 이 공사 이름의 작업본 백업이 없습니다.");
|
|
const pick = prompt(
|
|
`[백업 ${list.length}벌] 되돌릴 번호(취소 = 닫기):\n` +
|
|
list.map((b, i) => `${i + 1}. ${b.stamp} (${b.size}B)`).join("\n"),
|
|
"1",
|
|
);
|
|
const b = list[parseInt(pick, 10) - 1];
|
|
if (!b) return;
|
|
const res = await post("/api/file/restore", {
|
|
backup_path: b.path,
|
|
target_folder_name: b.name,
|
|
});
|
|
await openSaved(res.saved_dir);
|
|
},
|
|
};
|
|
|
|
async function run(act) {
|
|
try {
|
|
await ACTS[act](app().ctx);
|
|
} catch (err) {
|
|
alert("파일 작업 실패: " + err.message);
|
|
}
|
|
}
|
|
|
|
document.addEventListener("click", (e) => {
|
|
const el = e.target && e.target.closest && e.target.closest("[data-file-act]");
|
|
if (!el || !ACTS[el.getAttribute("data-file-act")]) return;
|
|
e.preventDefault();
|
|
run(el.getAttribute("data-file-act"));
|
|
});
|
|
document.addEventListener("change", (e) => {
|
|
if (e.target && e.target.name === "prj-pcode") app().ctx.uiState.filePcode = e.target.value;
|
|
});
|
|
|
|
[
|
|
["Act_FileCopyReg", "복사등록 — 차수/단계 변환", "copy"],
|
|
["Act_FilePack", "해당분만 압축하기 (.STC)", "pack"],
|
|
["Act_FileNew", "새 공사 만들기", "new"],
|
|
["Act_FileCheck", "STC 구조·버전 검사 (Chk_STC_Ver)", "check"],
|
|
["Act_FileRestore", "작업본 백업·복원 (wM_D_Restore)", "backups"],
|
|
].forEach(([act, label, key]) => {
|
|
PLUG.actions[act] = () => run(key);
|
|
PLUG.menus.push({ menu: "m1", label, act });
|
|
});
|
|
})(window);
|