diff --git a/tools/est-test/PLAN.md b/tools/est-test/PLAN.md
index 9948c58f1..72194e9a8 100644
--- a/tools/est-test/PLAN.md
+++ b/tools/est-test/PLAN.md
@@ -489,6 +489,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] 검색 결과 근거자료보기(원본 TfrmFoundDetail btnClose 0xefa7e8) — 통합 검색 결과의 일위·단산·기계 줄 「근거」: 그 작업의 호표 세부 줄(단산은 산출근거 글)과 총액·노무·재료·경비, 「적용」은 그 결과를 고르고 검색 창으로(가져오기는 검색 창에서). 고른 결과는 창을 오가도 유지.
+ - 검증완료: 바인딩·통합 검색 시험 통과. ORCA 브라우저에서 「거푸집」 일위 → 「[합판거푸집]의 근거자료보기」 총액 53,029·노무 38,289·재료 14,740·세부 6줄, 적용 → 그 결과 체크.
- [x] ES 정보(원본 mnuESInfo → TfrmESInfo 0x10b838c) — 기준시점·비교시점·최종기성일·선급금지급일(기본 오늘)·선급금지급액을 `project.esInfo` 에 기록(원본 [EscInfo] SDATE·CDATE·ACDATE·PreDATE·PreCost). 원본도 ES 제출 파일 작성에만 씀 — 웹 물가변동 계산과 잇지 않음, ESTX 에는 남지 않음.
- 검증완료: 바인딩 시험 통과. ORCA 브라우저에서 기본 오늘 · 기준시점 2025-01-15·선급금 1,000,000 저장 → 다시 열면 그대로, 지워 복구.
- [x] 설계참고철(Main Data)과 단가 범위 검색(원본 TfrmMainTableList 「MAIN DATA(설계참고철) 추가 및 삭제」 · TfrmFolderSerch 「Main Data에서 검색」·범위 검색 0xefefb0) — 통합 검색 「설계참고철 관리」에서 저장된 작업 중 최대 10개 지정(양식 보관함 종류 options), 검색위치 「설계참고철」이면 그 작업만, 검색어 「a~b」·숫자 하나는 단가 범위·같음. 원본의 검색중지는 서버 검색이 바로 끝나 두지 않음.
diff --git a/tools/est-test/native-business-actions.js b/tools/est-test/native-business-actions.js
index f2a9610ed..74f69f46f 100644
--- a/tools/est-test/native-business-actions.js
+++ b/tools/est-test/native-business-actions.js
@@ -612,7 +612,7 @@ const EstBusinessActions = (() => {
(typeof EstCleanup !== "undefined" && EstCleanup.handle(name, state)) ||
(typeof EstNameReg !== "undefined" && EstNameReg.handle(name, state)) ||
(typeof EstTidy !== "undefined" && EstTidy.handle(name, state)) ||
- (typeof EstMultiSearch !== "undefined" && EstMultiSearch.handle(name, state)) ||
+ (typeof EstMultiSearch !== "undefined" && EstMultiSearch.handle(name, state, node)) ||
(typeof EstDocs !== "undefined" && EstDocs.handle(name, state, node)) ||
(typeof EstWongaTools !== "undefined" && EstWongaTools.handle(name, state)) ||
(typeof EstEstimatePrint !== "undefined" && EstEstimatePrint.handle(name, state)) ||
diff --git a/tools/est-test/native-multi-search.js b/tools/est-test/native-multi-search.js
index 2ba87dc96..c98b7416b 100644
--- a/tools/est-test/native-multi-search.js
+++ b/tools/est-test/native-multi-search.js
@@ -18,7 +18,7 @@ const EstMultiSearch = (() => {
const rows = saved.hits
.map(
(h, i) =>
- `
| ${esc(h.project)} | ${esc(h.label)} | ${esc(h.Code)} | ${esc(h.Name)} | ${esc(h.Size)} | ${esc(h.Unite)} | ${EstApp.number(h.price)} |
`,
+ `| ${[103, 104, 105].includes(h.kind) ? `` : ""} | ${esc(h.project)} | ${esc(h.label)} | ${esc(h.Code)} | ${esc(h.Name)} | ${esc(h.Size)} | ${esc(h.Unite)} | ${EstApp.number(h.price)} |
`,
)
.join("");
EstApp.showDialog(
@@ -27,7 +27,11 @@ const EstMultiSearch = (() => {
);
}
- async function run(name, state) {
+ 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();
@@ -50,6 +54,7 @@ const EstMultiSearch = (() => {
}
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(
@@ -105,16 +110,50 @@ const EstMultiSearch = (() => {
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
+ ? `| ${esc(EstText.clean(r.LineData || ""))} |
`
+ : `| ${esc(r.Code)} | ${esc(r.Name)} | ${esc(r.Size)} | ${esc(r.Unite)} | ${number(r.GN)} |
`,
+ )
+ .join("");
+ EstApp.showDialog(
+ `[${hit.Name}]의 근거자료보기`,
+ `총 액 : ${number(head.TC)} · 노무비 : ${number(head.NC)} · 재료비 : ${number(head.GC)} · 경 비 : ${number(head.JC)}
`,
+ );
+ }
+ 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) {
+ function handle(name, state, node) {
if (!names.has(name)) return false;
- run(name, state).catch((error) => {
+ run(name, state, node).catch((error) => {
state.notice = error.message;
EstApp.render();
});