165 lines
8.8 KiB
JavaScript
165 lines
8.8 KiB
JavaScript
// 원본 통합 검색(TFRMMULTISERCH) 화면 — 저장된 다른 작업에서 단가·호표를 찾아, 고른 자료를 하위 연결 자료와 함께
|
|
// 현재 공사로 가져온다(EstTransfer). 같은 코드는 현재 공사 자료를 그대로 둔다. 가져온 뒤 단가 고르기로 행에 넣는다.
|
|
const EstMultiSearch = (() => {
|
|
const kinds = [
|
|
[103, "일위대가"],
|
|
[104, "단가산출"],
|
|
[105, "기계경비"],
|
|
[101, "견적단가"],
|
|
[21, "실적단가"],
|
|
[5, "자재단가"],
|
|
[6, "노임단가"],
|
|
];
|
|
|
|
function form(state) {
|
|
const { esc } = EstApp,
|
|
saved = state.multiSearch || { query: "", kinds: kinds.map(([k]) => k), hits: [], total: 0 };
|
|
state.multiSearch = saved;
|
|
const rows = saved.hits
|
|
.map(
|
|
(h, i) =>
|
|
`<tr><td><input type="checkbox" name="ms-hit" value="${i}" ${saved.picked?.has(i) ? "checked" : ""}>${[103, 104, 105].includes(h.kind) ? `<button data-action="multi-search-detail" data-i="${i}" title="근거자료보기">근거</button>` : ""}</td><td>${esc(h.project)}</td><td>${esc(h.label)}</td><td>${esc(h.Code)}</td><td style="text-align:left">${esc(h.Name)}</td><td>${esc(h.Size)}</td><td>${esc(h.Unite)}</td><td style="text-align:right">${EstApp.number(h.price)}</td></tr>`,
|
|
)
|
|
.join("");
|
|
EstApp.showDialog(
|
|
"통합 검색",
|
|
`<p>검색위치: <label><input type="radio" name="ms-where" value="all" ${saved.where !== "main" ? "checked" : ""}>저장된 모든 작업</label> <label><input type="radio" name="ms-where" value="main" ${saved.where === "main" ? "checked" : ""}>설계참고철(Main Data)</label> <button data-action="main-list">설계참고철 관리</button> · 검색어에 「10000~20000」처럼 쓰면 단가 범위</p><div class="form-fields">${kinds.map(([k, label]) => `<label><input type="checkbox" name="ms-kind" value="${k}" ${saved.kinds.includes(k) ? "checked" : ""}>${label}</label>`).join("")}<label>검색어<input name="ms-query" value="${esc(saved.query)}"></label></div><button data-action="multi-search-run">검색</button> <button data-action="multi-search-take">고른 자료를 이 공사로 가져오기</button><div style="max-height:360px;overflow:auto;border:1px solid #ccc;margin-top:6px"><table class="sheet" style="font-size:12px"><thead><tr><th></th><th>작업</th><th>구분</th><th>코드</th><th>품명</th><th>규격</th><th>단위</th><th>단가</th></tr></thead><tbody>${rows}</tbody></table></div><p>찾은 자료 ${saved.total}건${saved.total > saved.hits.length ? ` 중 앞 ${saved.hits.length}건` : ""}</p>`,
|
|
);
|
|
}
|
|
|
|
async function run(name, state, node) {
|
|
if (name === "multi-search-detail") return detail(state, Number(node.dataset.i));
|
|
if (name === "multi-search-pick")
|
|
(state.multiSearch.picked ||= new Set()).add(Number(node.dataset.i));
|
|
if (name === "multi-search-pick" || name === "multi-search-back") return form(state);
|
|
if (name === "main-list") return mainForm(state);
|
|
if (name === "main-list-save") {
|
|
await mainSave();
|
|
return form(state);
|
|
}
|
|
if (name === "multi-search") form(state);
|
|
else if (name === "multi-search-run") {
|
|
const saved = state.multiSearch;
|
|
saved.query = document.querySelector('#dialog-content [name="ms-query"]').value;
|
|
saved.kinds = [...document.querySelectorAll('#dialog-content [name="ms-kind"]:checked')].map(
|
|
(n) => Number(n.value),
|
|
);
|
|
saved.where = document.querySelector('#dialog-content [name="ms-where"]:checked')?.value;
|
|
const params = new URLSearchParams({ q: saved.query, kinds: saved.kinds.join(",") });
|
|
if (saved.where === "main") {
|
|
const ids = await mainList();
|
|
if (!ids.length)
|
|
throw Error("설계참고철에 등록한 작업이 없습니다. 설계참고철 관리에서 추가하세요.");
|
|
params.set("only", ids.join(","));
|
|
}
|
|
if (state.project.id) params.set("exclude", state.project.id);
|
|
Object.assign(saved, await EstApp.api(`/api/search?${params}`));
|
|
saved.picked = new Set();
|
|
form(state);
|
|
} else if (name === "multi-search-take") {
|
|
const picked = [...document.querySelectorAll('#dialog-content [name="ms-hit"]:checked')].map(
|
|
(n) => state.multiSearch.hits[Number(n.value)],
|
|
);
|
|
if (!picked.length) throw Error("가져올 자료를 고르세요.");
|
|
const projects = new Map();
|
|
for (const hit of picked)
|
|
if (!projects.has(hit.id))
|
|
projects.set(hit.id, await EstApp.api(`/api/projects/${hit.id}`));
|
|
const deps = picked.map((hit) =>
|
|
EstTransfer.collect(projects.get(hit.id), [{ LinkIndex: hit.kind, Code: hit.Code }]),
|
|
);
|
|
let added = 0,
|
|
kept = 0;
|
|
EstApp.change((project) => {
|
|
for (const dep of deps) {
|
|
const result = EstTransfer.merge(project, dep);
|
|
added += result.added.length;
|
|
kept += result.kept.length;
|
|
}
|
|
});
|
|
EstApp.byId("dialog").close();
|
|
state.notice = `자료 ${added}건을 가져왔습니다${kept ? `, 같은 코드 ${kept}건은 이 공사 자료를 그대로 씀` : ""}. 단가 고르기로 행에 넣으세요.`;
|
|
EstApp.render();
|
|
}
|
|
}
|
|
// 설계참고철(TfrmMainTableList 「MAIN DATA 추가 및 삭제」): 저장된 작업 중 최대 10개 — 양식 보관함(종류 options, 이름 「설계참고철」)
|
|
const MAIN = "설계참고철";
|
|
async function mainList() {
|
|
return (
|
|
(await EstApp.api("/api/forms")).find((f) => f.kind === "options" && f.name === MAIN)?.data
|
|
?.ids || []
|
|
);
|
|
}
|
|
async function mainForm(state) {
|
|
const ids = await mainList(),
|
|
projects = await EstApp.api("/api/projects"),
|
|
{ esc } = EstApp;
|
|
EstApp.showDialog(
|
|
"MAIN DATA(설계참고철) 추가 및 삭제",
|
|
`<p>통합 검색 「설계참고철」에서 찾을 작업을 고르세요(최대 10개).</p><div style="max-height:360px;overflow:auto">${projects.map((p) => `<label style="display:block"><input type="checkbox" name="ml-id" value="${esc(p.id)}" ${ids.includes(p.id) ? "checked" : ""}>${esc(p.name || p.id)}</label>`).join("")}</div><button data-action="main-list-save">적용</button> <button data-action="multi-search">통합 검색으로</button>`,
|
|
);
|
|
}
|
|
async function mainSave() {
|
|
const ids = [...document.querySelectorAll('#dialog-content [name="ml-id"]:checked')].map(
|
|
(n) => n.value,
|
|
);
|
|
if (ids.length > 10) throw Error("Main Data는 10개 이상 추가할수없습니다.");
|
|
await EstApp.api("/api/forms", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ kind: "options", name: MAIN, data: { ids } }),
|
|
});
|
|
}
|
|
// 근거자료보기(TfrmFoundDetail): 찾은 일위·단산·기계 호표의 세부 줄과 총액·노무·재료·경비, 「적용」 = 그 결과를 고름
|
|
const BODY = { 103: "HeadMainDat", 104: "CostMainDat", 105: "MachMainDat" };
|
|
async function detail(state, i) {
|
|
remember(state);
|
|
const hit = state.multiSearch.hits[i],
|
|
project = await EstApp.api(`/api/projects/${hit.id}`),
|
|
{ esc, number } = EstApp,
|
|
rows = project.tables[BODY[hit.kind]] || [],
|
|
group = EstNative.groups(rows).get(hit.Code);
|
|
if (!group) throw Error("근거 자료를 찾지 못했습니다.");
|
|
const head = group.rows[0],
|
|
body = group.rows
|
|
.slice(1)
|
|
.map((r) =>
|
|
hit.kind === 104
|
|
? `<tr><td colspan="5" style="text-align:left">${esc(EstText.clean(r.LineData || ""))}</td></tr>`
|
|
: `<tr><td>${esc(r.Code)}</td><td style="text-align:left">${esc(r.Name)}</td><td>${esc(r.Size)}</td><td>${esc(r.Unite)}</td><td style="text-align:right">${number(r.GN)}</td></tr>`,
|
|
)
|
|
.join("");
|
|
EstApp.showDialog(
|
|
`[${hit.Name}]의 근거자료보기`,
|
|
`<p>총 액 : ${number(head.TC)} · 노무비 : ${number(head.NC)} · 재료비 : ${number(head.GC)} · 경 비 : ${number(head.JC)}</p><div style="max-height:360px;overflow:auto"><table class="sheet"><tbody>${body}</tbody></table></div><button data-action="multi-search-pick" data-i="${i}">적 용</button> <button data-action="multi-search-back">돌아가기</button>`,
|
|
);
|
|
}
|
|
function remember(state) {
|
|
const boxes = document.querySelectorAll('#dialog-content [name="ms-hit"]');
|
|
if (boxes.length)
|
|
state.multiSearch.picked = new Set(
|
|
[...boxes].filter((n) => n.checked).map((n) => Number(n.value)),
|
|
);
|
|
}
|
|
const names = new Set([
|
|
"multi-search",
|
|
"multi-search-run",
|
|
"multi-search-take",
|
|
"main-list",
|
|
"main-list-save",
|
|
"multi-search-detail",
|
|
"multi-search-pick",
|
|
"multi-search-back",
|
|
]);
|
|
function handle(name, state, node) {
|
|
if (!names.has(name)) return false;
|
|
run(name, state, node).catch((error) => {
|
|
state.notice = error.message;
|
|
EstApp.render();
|
|
});
|
|
return true;
|
|
}
|
|
|
|
return { handle };
|
|
})();
|