feat(est-test): 프로젝트 추가로 다른 공사 내역 합치기

- 다른 공사 파일의 순공사비 위 내역 전체를 하위 자료와 함께 이 공사 순공사비 위에 합침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-11 17:12:15 +09:00
co-authored by Claude Opus 5.5
parent 5e9b6a7bea
commit dc365e2140
5 changed files with 77 additions and 4 deletions
+3 -2
View File
@@ -446,12 +446,13 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD
- 남음: 일위대가·단가산출·표준·견적·자재·노임 탭, 원본 「셀부호 밀기·당기기」.
- [x] 연결 자료와 함께 복사·붙여넣기(원본 TFRMOTHERLOAD) — 내역 행의 자재·노임·실적·견적 행과 일위대가·단가산출·기계경비 호표를 하위까지 모아 다른 공사에 붙여넣기. 같은 코드는 붙여넣을 공사 자료 유지(덮어쓰지 않음), 기계경비 적용기준은 붙여넣을 공사에 없을 때만 옮김.
- 검증완료: `test_est_transfer.py` 2개(빈 공사로 옮겨 연결 오류 0·금액 동일·하위 기계경비 호표 포함·PkID 중복 없음, 같은 코드·적용기준 덮어쓰지 않음), 관련 시험 통과. ORCA 브라우저에서 샘플 내역 3행 복사 → 새 내역 붙여넣기 → 연결 자료 12건 추가·금액 1,285,046·417,024·680,414 원본과 동일·계산 오류 0, 샘플 복구.
- 남음: 프로젝트 추가(다른 파일의 내역 전체 합치기), 두 공사 나란히 보기.
- 남음: 두 공사 나란히 보기.
- [x] 프로젝트 추가(원본 mnuFileLoadProjectAs) — 다른 공사 파일(ESTX·JSON)의 순공사비 위 내역 전체를 하위 자료와 함께 이 공사 순공사비 위에 합침. 같은 코드 자료 유지.
- 검증완료: `test_est_transfer.py` 3개 통과(합친 뒤 순공사비 2배·계산 오류 0·빈 공사 거부). ORCA 브라우저에서 메뉴 확인, 샘플에 샘플 합치기 → 35행·같은 코드 46건 유지·순공사비 45,293,066 → 90,586,132, 샘플 복구. 파일 고르기 창 자체는 자동 조작 불가로 기존 가져오기 경로(`/api/import`)를 그대로 씀.
- 대조 합계(CAD 제외 882개): 구현 283 · 부분 193 · 없음 273 · 제외 133.
- [ ] 남은 핵심(실무 빈도순):
1. 통합검색 — 저장된 다른 작업에서 단가·호표 찾기.
3. 수량산출서 — 누계·4방향 합계, 제출용 전체 인쇄.
5. 프로젝트 추가(다른 파일의 내역 합치기).
6. 낙찰률 전체·범위·공종별 적용.
7. 무관자료 삭제, 데이타 정리(정렬·번호), 품명정리.
8. 원가계산서 — 정산원가계산서, 기타경비·요율 대화, 서식 저장·불러오기, 기성 원가 편집.
+2
View File
@@ -22,6 +22,8 @@
내역서·일위대가 화면의 `편집 → 단가 고르기`는 자재·노임·기계경비·일위대가·단가산출·견적·실적 자료를 검색어로 찾아 선택 행 아래에 넣거나, 선택 행 하나의 연결을 바꿉니다. 여러 개를 골라 한 번에 넣을 수 있고, 바꿀 때는 변경 전·후를 확인한 뒤 같은 코드를 쓰는 이 표의 다른 행도 함께 바꿀지 고릅니다. 넣거나 바꾼 행만 원천 단가를 다시 읽습니다. 단가산출 산출근거 행은 대상이 아닙니다.
`파일 → 프로젝트 추가`는 다른 공사 파일(ESTX·웹 JSON)의 순공사비 위 내역 행 전체를 하위 자료와 함께 이 공사의 순공사비 위에 합칩니다. 같은 코드 자료는 이 공사의 것을 그대로 씁니다.
`편집 → 내역 행 연결 자료와 함께 복사`는 고른 내역 행이 쓰는 자재·노임·실적·견적 자료와 일위대가·단가산출·기계경비 호표를 하위까지 모아 복사합니다. 다른 공사를 연 뒤 `연결 자료와 함께 붙여넣기`를 누르면 없는 자료만 추가하고, 같은 코드가 이미 있으면 그 공사의 자료를 그대로 씁니다. 붙여넣을 공사에 기계경비 적용기준이 비어 있을 때만 적용기준도 함께 옮깁니다.
`파일 → Excel 내역 열 맞춰 가져오기`는 원본 엑셀→EST 변환처럼 다른 프로그램이나 발주처의 Excel 내역을 읽습니다. 시트·시작행·끝행과 번호·품명·규격·단위·수량·단가 등 항목별 Excel 열 문자를 적고 미리보기로 확인한 뒤, 순공사비 줄 위에 추가하거나 그 위 행을 바꿉니다. 단위와 수량이 없는 줄은 공종 제목이 되고, 금액은 단가×수량으로 다시 계산합니다. 수식 셀은 Excel이 저장해 둔 값을 씁니다. 합계단가만 있고 노무·재료·경비로 나눈 단가가 없으면 비목을 정할 수 없어 가져오지 않습니다.
+1
View File
@@ -74,6 +74,7 @@
<button data-action="import-xml">조달청 XML 불러오기</button>
<button data-action="export-estx">원본 구조 유지 ESTX 수정 저장</button>
<button data-action="blank-save">공내역 파일로 저장</button>
<button data-action="project-add">프로젝트 추가(다른 공사 내역 합치기)</button>
<button data-action="export-jdl">JDL 기본 표 내보내기</button>
<button data-action="import-jdl">JDL 불러오기</button>
</div>
+45 -2
View File
@@ -118,6 +118,48 @@ const EstTransfer = (() => {
return copies.length;
}
// 프로젝트 추가: 다른 공사의 순공사비 위 내역 행 전체를 하위 자료와 함께 이 공사의 순공사비 위에 붙인다.
function addProject(target, source) {
const list = source.tables?.EstMainDat || [];
let end = list.findIndex((row) => Number(row.LinkIndex) === 54);
if (end < 0) end = list.length;
const rows = list.slice(0, end);
if (!rows.length) throw Error("추가할 공사에 순공사비 위 내역 행이 없습니다.");
const result = merge(target, collect(source, rows));
let at = target.tables.EstMainDat.findIndex((row) => Number(row.LinkIndex) === 54);
if (at < 0) at = target.tables.EstMainDat.length;
paste(target, rows, at);
return { ...result, rows: rows.length };
}
function chooseProject(state) {
if (state.view !== "EstMainDat") throw Error("내역서 화면에서 프로젝트를 추가하세요.");
const input = document.createElement("input");
input.type = "file";
input.accept = ".estx,.ebkx,.json";
input.addEventListener("change", async () => {
try {
const file = input.files[0];
if (!file) return;
const source = /\.(estx|ebkx)$/i.test(file.name)
? await EstApp.api(`/api/import?name=${encodeURIComponent(file.name)}`, {
method: "POST",
body: await file.arrayBuffer(),
})
: JSON.parse(await file.text());
if (!confirm(`「${source.name || file.name}」의 내역을 이 공사의 순공사비 위에 합칩니다.`))
return;
let result;
EstApp.change((project) => (result = addProject(project, source)));
state.notice = `내역 ${result.rows}행을 합쳤습니다. 연결 자료 ${result.added.length}건 추가${result.kept.length ? `, 같은 코드 ${result.kept.length}건은 이 공사 자료를 그대로 씀` : ""}.`;
} catch (error) {
state.notice = error.message;
}
EstApp.render();
});
input.click();
}
// 화면
let clipboard = null;
function handle(name, state) {
@@ -146,9 +188,10 @@ const EstTransfer = (() => {
paste(project, clipboard.rows, at);
});
state.notice = `내역 ${clipboard.rows.length}행을 붙여넣었습니다. 연결 자료 ${result.added.length}건 추가${result.kept.length ? `, 같은 코드 ${result.kept.length}건은 이 공사 자료를 그대로 씀` : ""}${result.criteria ? ", 비어 있던 기계경비 적용기준도 함께 옮김" : ""}.`;
} else return false;
} else if (name === "project-add") chooseProject(state);
else return false;
return true;
}
return { collect, merge, paste, handle };
return { collect, merge, paste, addProject, handle };
})();