// 원본 「다른 공사 불러오기」 창(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( "다른 공사 불러오기", `
${list.map(([r, i]) => ``).join("")}
${esc(r.Code ?? "")}${esc(r.Name || EstText.clean(r.LineData || ""))}${esc(r.Size ?? "")}${esc(r.Unite ?? "")}${number(r.TC)}
`, ); } 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 }; })();