feat(est-test): 다른 공사 불러오기 창(같은 표 보기·찾기·가져오기)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -489,6 +489,7 @@ 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] 다른 공사 불러오기 창(원본 barbtnSelfLoad → TfrmOtherLoad, 찾기 popSerch) — 저장된 다른 작업을 골라 같은 표를 띄우고 찾기(품명·규격, A&B 둘 다)·고른 줄을 하위 자료와 함께 가져오기(내역서는 고른 줄 아래에 줄, 나머지는 자료·호표 합침, 같은 코드는 이 공사 것). 검증완료: 바인딩 통과, ORCA 브라우저에서 산불진화임도 작업의 일위대가 3개 → 「취수장」 1개 → 가져오기 호표 8→9·하위 41건, 샘플 복구.
|
||||
- [x] 사용위치 검색 창(원본 TfrmUsesDataSerch btnSerch·btnAllDisplay) — 현재 표 항목을 품명·규격(빈칸·|·괄호 무시)으로 거르고 골라 사용위치 보기. 검증완료: 바인딩 통과, ORCA 브라우저에서 노임 9개 → 「보 통」 1개 → 보통인부 사용위치 11곳.
|
||||
- [x] 인쇄양식 불러오기·저장하기(원본 mnuPrintFormatLoad·Save 0x10ed318·0x10ed000 — spec_pc §3) — .pfot(인쇄정보표 MIDAS 이진) 읽기·쓰기 `print-format.cjs`(설명 BLOB·빈 칸 표지까지 보존, 원본 파일 그대로 다시 쓰면 바이트 같음). 불러오면 공사 인쇄표를 그 값으로, 저장은 지금 인쇄표를 원본 설치 main.pfot 필드 설명자 위에(`EST_PFOT_TEMPLATE`). .gfot(cxGrid 열 배치)는 웹 격자와 호환되지 않아 하지 않음.
|
||||
- 검증완료: `test_est_print_format.py`(설치 .pfot 두 개 바이트 같음·고친 값 반영) 및 바인딩 시험 통과, 서버 경로로 읽고 다시 쓴 파일 cmp 같음. ORCA 브라우저에서 샘플 인쇄양식 저장(32,755 바이트).
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<script defer src="native-price-tools.js"></script>
|
||||
<script defer src="native-misc-tools.js"></script>
|
||||
<script defer src="native-excel-to-est.js"></script>
|
||||
<script defer src="native-other-load.js"></script>
|
||||
<script defer src="native-actions-more.js"></script>
|
||||
<script defer src="native-wonga.js"></script>
|
||||
<script defer src="native-jungsang.js"></script>
|
||||
@@ -317,6 +318,7 @@
|
||||
><button id="sources-btn" data-action="sources" hidden title="발췌근거 삭제,삽입 및 변경">발췌근거</button
|
||||
><button id="uses-search-btn" data-action="uses-data-search">사용위치</button
|
||||
><button data-action="uses-list">사용위치 검색</button
|
||||
><button data-action="other-load">다른 공사 불러오기</button
|
||||
><button data-action="book">품셈</button
|
||||
><button data-action="book-paste">품셈자료 붙여넣기</button
|
||||
><button data-action="mul-paste">수량붙여넣기(연결)</button
|
||||
|
||||
@@ -6,6 +6,7 @@ const EstActionsMore = (() => {
|
||||
typeof EstPriceTools !== "undefined" && EstPriceTools,
|
||||
typeof EstMiscTools !== "undefined" && EstMiscTools,
|
||||
typeof EstExcelToEst !== "undefined" && EstExcelToEst,
|
||||
typeof EstOtherLoad !== "undefined" && EstOtherLoad,
|
||||
].filter(Boolean);
|
||||
function handle(name, state, node) {
|
||||
for (const module of modules()) if (module.handle(name, state, node)) return true;
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// 원본 「다른 공사 불러오기」 창(barbtnSelfLoad → f135b8 → TfrmOtherLoad — spec_pb2 §5.2·§11): 저장된 다른 작업의 같은 표를 띄워
|
||||
// 찾기(Ctrl+F: 품명·규격 빈칸·|·괄호 무시, 「A&B」 는 둘 다)·고른 줄을 하위 자료와 함께 이 공사로 가져오기(EstTransfer, 같은 코드는 이 공사 것).
|
||||
// 내역서는 고른 줄 아래에 줄을 넣고, 자재·노임·표준·견적·일위·단산·기계는 자료(호표)를 합친다.
|
||||
const EstOtherLoad = (() => {
|
||||
const KIND = {
|
||||
StuffcDat: 5,
|
||||
LaborcDat: 6,
|
||||
SilJukDat: 21,
|
||||
EstiMateDat: 101,
|
||||
HeadMainDat: 103,
|
||||
CostMainDat: 104,
|
||||
MachMainDat: 105,
|
||||
};
|
||||
const norm = (s) => String(s ?? "").replace(/[\s|()]/g, "");
|
||||
const match = (row, word) => {
|
||||
const k = norm(row.Name || EstText.clean(row.LineData || "")) + norm(row.Size);
|
||||
return norm(word)
|
||||
.split("&")
|
||||
.filter(Boolean)
|
||||
.every((p) => k.includes(p));
|
||||
};
|
||||
|
||||
async function open(state) {
|
||||
if (!state.project.tables[state.view]) throw Error("자료 화면에서 쓰세요.");
|
||||
const o = (state.otherLoad ||= { id: "", word: "", picked: new Set() });
|
||||
o.projects = await EstApp.api("/api/projects");
|
||||
draw(state);
|
||||
}
|
||||
function rowsOf(o, view) {
|
||||
const rows = o.source?.tables?.[view] || [];
|
||||
return rows
|
||||
.map((r, i) => [r, i])
|
||||
.filter(([r]) => {
|
||||
if (["HeadMainDat", "CostMainDat", "MachMainDat"].includes(view) && !(Number(r.Index) > 0))
|
||||
return false;
|
||||
return (r.Name || r.Code || r.LineData) && (!o.word || match(r, o.word));
|
||||
});
|
||||
}
|
||||
function draw(state) {
|
||||
const o = state.otherLoad,
|
||||
{ esc, number } = EstApp,
|
||||
list = rowsOf(o, state.view).slice(0, 400);
|
||||
EstApp.showDialog(
|
||||
"다른 공사 불러오기",
|
||||
`<div class="business-tools"><select data-ol-project><option value="">공사 고르기</option>${o.projects.map((p) => `<option value="${esc(p.id)}"${p.id === o.id ? " selected" : ""}>${esc(p.name || p.id)}</option>`).join("")}</select><input data-ol-word value="${esc(o.word)}" placeholder="찾기(Ctrl+F) A&B"><button data-action="ol-search">찾기</button><button data-action="ol-take">고른 줄 가져오기</button></div><div style="max-height:440px;overflow:auto"><table class="sheet"><tbody>${list.map(([r, i]) => `<tr><td><input type="checkbox" data-ol-pick="${i}"${o.picked.has(i) ? " checked" : ""}></td><td>${esc(r.Code ?? "")}</td><td style="text-align:left">${esc(r.Name || EstText.clean(r.LineData || ""))}</td><td>${esc(r.Size ?? "")}</td><td>${esc(r.Unite ?? "")}</td><td>${number(r.TC)}</td></tr>`).join("")}</tbody></table></div>`,
|
||||
);
|
||||
}
|
||||
function take(state) {
|
||||
const o = state.otherLoad,
|
||||
view = state.view,
|
||||
picked = [...o.picked].sort((a, b) => a - b).map((i) => o.source.tables[view][i]);
|
||||
if (!picked.length) throw Error("가져올 줄을 고르세요.");
|
||||
let added = 0,
|
||||
kept = 0;
|
||||
EstApp.change((project) => {
|
||||
if (view === "EstMainDat") {
|
||||
const deps = EstTransfer.collect(o.source, picked);
|
||||
const r = EstTransfer.merge(project, deps);
|
||||
added += r.added.length;
|
||||
kept += r.kept.length;
|
||||
const at = Math.max(-1, ...state.selection) + 1 || project.tables.EstMainDat.length;
|
||||
added += EstTransfer.paste(project, picked, at);
|
||||
} else {
|
||||
const deps = EstTransfer.collect(
|
||||
o.source,
|
||||
picked.map((r) => ({ LinkIndex: KIND[view], Code: r.Code })),
|
||||
);
|
||||
const r = EstTransfer.merge(project, deps);
|
||||
added += r.added.length;
|
||||
kept += r.kept.length;
|
||||
}
|
||||
});
|
||||
EstApp.byId("dialog").close();
|
||||
state.notice = `${added}건을 가져왔습니다${kept ? `, 같은 코드 ${kept}건은 이 공사 자료를 그대로 씀` : ""}.`;
|
||||
o.picked = new Set();
|
||||
}
|
||||
|
||||
function install() {
|
||||
document.addEventListener("change", async (event) => {
|
||||
const t = event.target,
|
||||
o = EstApp.state.otherLoad;
|
||||
if (!o) return;
|
||||
try {
|
||||
if (t.dataset.olProject !== undefined) {
|
||||
o.id = t.value;
|
||||
o.source = t.value ? await EstApp.api(`/api/projects/${t.value}`) : null;
|
||||
o.picked = new Set();
|
||||
draw(EstApp.state);
|
||||
} else if (t.dataset.olPick !== undefined) {
|
||||
const i = Number(t.dataset.olPick);
|
||||
if (t.checked) o.picked.add(i);
|
||||
else o.picked.delete(i);
|
||||
}
|
||||
} catch (error) {
|
||||
EstApp.state.notice = error.message;
|
||||
EstApp.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (typeof document !== "undefined" && document.addEventListener) install();
|
||||
|
||||
function handle(name, state) {
|
||||
if (name === "other-load")
|
||||
open(state).catch((e) => ((state.notice = e.message), EstApp.render()));
|
||||
else if (name === "ol-search") {
|
||||
state.otherLoad.word = document.querySelector("[data-ol-word]")?.value || "";
|
||||
draw(state);
|
||||
} else if (name === "ol-take") take(state);
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
return { match, handle };
|
||||
})();
|
||||
@@ -103,6 +103,7 @@ const allowed = new Set([
|
||||
"native-price-tools.js",
|
||||
"native-misc-tools.js",
|
||||
"native-excel-to-est.js",
|
||||
"native-other-load.js",
|
||||
"native-actions-more.js",
|
||||
"native-wonga-tools.js",
|
||||
"native-estimate-print.js",
|
||||
|
||||
Reference in New Issue
Block a user