feat(est-test): 통합 검색 근거자료보기

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 13:31:39 +09:00
co-authored by Claude Opus 5.5
parent 3900d88a7c
commit 515b683b82
3 changed files with 46 additions and 5 deletions
+2
View File
@@ -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」·숫자 하나는 단가 범위·같음. 원본의 검색중지는 서버 검색이 바로 끝나 두지 않음.
+1 -1
View File
@@ -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)) ||
+43 -4
View File
@@ -18,7 +18,7 @@ const EstMultiSearch = (() => {
const rows = saved.hits
.map(
(h, i) =>
`<tr><td><input type="checkbox" name="ms-hit" value="${i}"></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>`,
`<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(
@@ -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
? `<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) {
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();
});