feat(est-test): 원가계산서 내용 삭제와 기본양식 저장

- 원본 popupContentDel: 고른 줄의 연결·금액·요율·산출근거·자리 비움
- 원본 popupSaveAsMain: 원가 양식 「기본양식」 저장, 새 내역서에 적용

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 12:58:32 +09:00
co-authored by Claude Opus 5.5
parent 9de0cb665a
commit 21f49df818
5 changed files with 50 additions and 3 deletions
+2
View File
@@ -487,6 +487,8 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD
- 남음: 구성비·요율 전환(CalcBase), 약어 표시(SName), 제비율 기준 문서 보기(원본 한글 파일).
- [x] 수량산출서 검색·전체보기(원본 TFRMMULLANG btnSerch·btnAllDisplay — 빈칸·|·괄호 무시 포함 검색) — 고르기 줄을 `native-quantity-sheets.js` 로 옮겨 700줄 제한 유지.
- 검증완료: `test_est_quantity_sheets.py` 검색 시험 포함 수량 시험 22개 통과. ORCA 브라우저에서 「집수」 검색 → 항목에 집수정이 있는 「배수」만·선택 이동, 전체보기 → 3개 복귀, 샘플 복구.
- [x] 원가계산서 내용 삭제·기본양식으로 저장(원본 TFRMWONGABASE popupContentDel A7 · popupSaveAsMain) — 원가계산서(W) 「선택 줄 내용 삭제」: 연결·금액·비율·요율·산출근거·자리 비움(이름·기호 유지, 재계산 없음). 양식 목록 「기본양식으로 저장」: 이름 「기본양식」 — 새 내역서를 만들 때 원가계산서로 씀.
- 검증완료: 양식·원가·바인딩 시험 8개 통과. ORCA 브라우저에서 간접노무비 줄 내용 삭제(이름 유지·식·요율·금액 비움), 기본양식 저장 → 새 내역서 원가 31줄, 양식 지우고 샘플 복구.
- [x] 갑지·표지 양식(원본 TFRMGABGI·TFRMCOVER popupSaveFormat·popupLoadFormat·popupFormatTableS·popupSaveToDefault·popupInit·popupPrint) — 편집 창 위 단추: 양식목록(불러오기·지우기), 양식저장하기(이름), 기본양식으로 저장(처음 여는 갑지·표지에 채움), 초기화(문서 지우고 다시 열기), 인쇄(서식 8·9 미리보기로). 양식은 서버 보관함 종류 document 에 { docKind, fields }, 공사명은 넣지 않음.
- 검증완료: 문서·바인딩 시험 4개 통과. ORCA 브라우저에서 갑지 공사위치 「X위치」 양식 저장·기본양식 저장 → 초기화 뒤 다시 열면 「X위치」 채움, 양식목록에서 불러오기, 인쇄 → 미리보기 서식 8 에 갑지, 양식·인쇄 설정 지워 복구.
- [x] 공사설명서·시방서 서식 편집(원본 TFRMESTEDITOR mnuFormatPar·mnuTableCreate·mnuFormatTable·popupBookMark·mnuFormatTableS·mnuSaveToDefault) — 서식 있는 편집 칸: 문단모양(정렬·들여쓰기·왼쪽 여백·줄간격), 표만들기(줄·칸), 표모양(테두리·칸 여백·너비), 책갈피(넣기·찾아가기), 양식목록(서버 양식 보관함 종류 document — 불러오기·지우기·현재 문서 저장), 기본양식으로 저장(빈 문서를 열면 채움). 본문은 `docs[kind].html`(허용 태그·속성만)과 글자 `text` 를 함께 두고 인쇄는 html 우선. 갑지에서 붙여넣기는 커서 자리에. 원본 WPT 서식 가져오기·ESTX 저장은 여전히 하지 않음.
+2 -1
View File
@@ -211,7 +211,8 @@
><button data-action="jungsang-init">당초금액 초기화</button>
<button data-action="wonga-gita">기타경비 계산</button
><button data-action="wonga-anjun">산업안전보건관리비 비교</button
><button data-action="wonga-sname">산출근거 약어로 표시</button>
><button data-action="wonga-sname">산출근거 약어로 표시</button
><button data-action="wonga-content-del">선택 줄 내용 삭제</button>
</div>
</details>
<details>
+1
View File
@@ -140,6 +140,7 @@ const EstActions = (() => {
if (confirm("저장하지 않은 입력을 비우고 새 내역서를 만드시겠습니까?")) {
EstApp.setProject(EstNative.empty());
if (typeof EstDefaults !== "undefined") await EstDefaults.applyNew();
if (typeof EstFormsLibrary !== "undefined") await EstFormsLibrary.applyDefault();
}
} else if (name === "sample") {
if (confirm("현재 작업을 REA 샘플로 바꾸시겠습니까?"))
+25 -2
View File
@@ -1,6 +1,8 @@
// 원본 원가계산서 양식(TFRMWONGABASE popupSaveAs·popupFormats·popupLoadFrom)과 양식목록(TFRMFORMATFILELIST)
// — 현재 원가 표를 이름 붙여 서버 보관함(data/forms.json)에 등록하고, 목록에서 열기·삭제, 파일로 내려받기·불러오기.
// 기본양식으로 저장(popupSaveAsMain): 이름 「기본양식」 — 새 내역서를 만들 때 원가계산서로 쓴다.
const EstFormsLibrary = (() => {
const DEFAULT = "기본양식";
// 원가 표 양식으로 남길 필드 — 금액(Cost)은 계산 결과라 남기지 않는다.
function strip(rows) {
return rows.map((row) => {
@@ -23,7 +25,7 @@ const EstFormsLibrary = (() => {
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>`,
`<p>현재 원가계산서의 항목·수식·요율을 양식으로 등록하고, 다른 공사에서 열어 씁니다. 금액은 열 때 다시 계산합니다.</p><button data-action="wonga-form-register">현재 원가계산서 등록</button> <button data-action="wonga-form-default">기본양식으로 저장</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;
}
@@ -52,6 +54,18 @@ const EstFormsLibrary = (() => {
body: JSON.stringify({ kind: "wonga", name: title, data }),
});
await show(state);
} else if (name === "wonga-form-default") {
if (!confirm("현재 원가계산서를 새 내역서의 기본양식으로 저장하시겠습니까?")) return;
await EstApp.api("/api/forms", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
kind: "wonga",
name: DEFAULT,
data: strip(validRows(state.project.tables.Wonga)),
}),
});
await show(state);
} else if (name === "wonga-form-open") {
const form = state.formsLibrary?.find((f) => f.name === node.dataset.name);
if (!form) throw Error("양식을 찾을 수 없습니다. 목록을 다시 여세요.");
@@ -92,6 +106,7 @@ const EstFormsLibrary = (() => {
const names = new Set([
"wonga-forms",
"wonga-form-register",
"wonga-form-default",
"wonga-form-open",
"wonga-form-delete",
"wonga-form-export",
@@ -107,5 +122,13 @@ const EstFormsLibrary = (() => {
return true;
}
return { strip, validRows, handle };
// 새 내역서에 기본양식 원가계산서
async function applyDefault() {
const form = (await list()).find((f) => f.name === DEFAULT);
if (form)
EstApp.change((project) => (project.tables.Wonga = structuredClone(validRows(form.data))));
return Boolean(form);
}
return { strip, validRows, applyDefault, handle };
})();
+20
View File
@@ -282,6 +282,26 @@ const EstWongaTools = (() => {
}
function handle(name, state) {
if (name === "wonga-content-del") {
// 내용 삭제(popupContentDel A7): 연결·금액·비율·요율·산출근거·자리 비움, 이름·기호·계산방식 유지, 재계산 없음
const rows = state.project.tables.Wonga || [],
picked = [...state.selection].filter((i) => rows[i]);
if (state.view !== "Wonga" || !picked.length)
throw Error("원가계산서에서 지울 줄을 고르세요.");
EstApp.change((project) => {
for (const i of picked)
Object.assign(project.tables.Wonga[i], {
LinkIndex: 0,
Cost: 0,
Biul: "",
Youl: "",
VSusic: "",
Diget: 0,
Checked: false,
});
}, false);
return true;
}
if (name === "wonga-sname") {
EstApp.change(
(project) => (project.options.WongaSNameShow = !project.options.WongaSNameShow),