Files
Aislo/tools/est-test/native-sources.js
T
eomsangdonandClaude Opus 5.5 7222577442 feat(est-test): 발췌근거 출처 열 이름·보이기
- 원본 barbtnColumn: 자재·노임 단가의 출처 열(Cost1~6) 이름·쪽수·보기 편집, 자재는 8가지 출처 이름·중복 거부
- 숨긴 출처는 최저·평균·최고 단가 고르기에서 제외(브라우저·서버 같은 엔진)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:48:37 +09:00

94 lines
3.8 KiB
JavaScript

// 원본 「발췌근거」(barbtnColumn — 일반 TfrmHeaderChange 0x110b7ec · 자재 TfrmStuffcHeadChange 0xf61b84):
// 단가 표의 가격 출처 열(Cost1~6) 머리글 이름·쪽수·보이기를 고친다. 숨긴 출처는 최저·평균·최고 단가 고르기(113e918)에서 빠진다.
// 자재 표는 출처 이름을 정해진 8가지에서 고르고 같은 이름이 둘이면 거부한다. 값은 계산 환경 PriceSources[표] 에 둔다.
const EstSources = (() => {
const NAMES = [
"가격정보",
"물가자료",
"유통물가",
"물가정보",
"거래가격",
"견적가1",
"견적가2",
"견적가3",
];
const TABLES = ["StuffcDat", "LaborcDat"];
function get(options, table) {
const saved = options?.PriceSources?.[table] || [];
return Array.from({ length: 6 }, (_, i) => ({
caption: saved[i]?.caption || `출처${i + 1}`,
page: saved[i]?.page ?? "",
visible: saved[i]?.visible !== false,
}));
}
function hidden(options, table) {
return new Set(
get(options, table)
.map((s, i) => (s.visible ? -1 : i + 1))
.filter((i) => i > 0),
);
}
function columns(options, table, cols) {
const off = hidden(options, table);
return off.size ? cols.filter((k) => !off.has(Number(/^Cost(\d)$/.exec(k)?.[1]))) : cols;
}
function label(options, table, key) {
const m = /^Cost(\d)$/.exec(key);
if (!m || !TABLES.includes(table)) return "";
const s = get(options, table)[Number(m[1]) - 1];
return s.page ? `${s.caption} (${s.page})` : s.caption;
}
function set(options, table, list) {
if (table === "StuffcDat") {
const seen = new Set();
for (const s of list) {
if (seen.has(s.caption)) throw Error("같은 발췌근거가 있습니다.");
seen.add(s.caption);
}
}
options.PriceSources = { ...(options.PriceSources || {}), [table]: list };
}
function dialog(state) {
const table = state.view;
if (!TABLES.includes(table)) throw Error("자재단가·노임단가 화면에서 쓰세요.");
const { esc } = EstApp,
list = get(state.project.options, table),
stuffc = table === "StuffcDat";
EstApp.showDialog(
"발췌근거 수정 및 보기",
`<table class="sheet"><thead><tr><th>No</th><th>발췌근거</th>${stuffc ? "<th>페이지</th>" : ""}<th>보기</th></tr></thead><tbody>${list
.map(
(s, i) =>
`<tr><td>${i + 1}</td><td>${
stuffc
? `<select name="src-caption" data-i="${i}">${[...new Set([s.caption, ...NAMES])].map((n) => `<option${n === s.caption ? " selected" : ""}>${esc(n)}</option>`).join("")}</select>`
: `<input name="src-caption" data-i="${i}" value="${esc(s.caption)}">`
}</td>${stuffc ? `<td><input name="src-page" data-i="${i}" value="${esc(s.page)}" size="6"></td>` : ""}<td><input type="checkbox" name="src-visible" data-i="${i}"${s.visible ? " checked" : ""}></td></tr>`,
)
.join("")}</tbody></table><button data-action="sources-apply">적 용</button>`,
);
}
function apply(state) {
const table = state.view,
q = (name, i) => document.querySelector(`#dialog-content [name="${name}"][data-i="${i}"]`);
const list = Array.from({ length: 6 }, (_, i) => ({
caption: q("src-caption", i).value.trim() || `출처${i + 1}`,
page: q("src-page", i)?.value.trim() ?? "",
visible: q("src-visible", i).checked,
}));
set(structuredClone(state.project.options), table, list);
EstApp.change((project) => set(project.options, table, list));
EstApp.byId("dialog").close();
}
function handle(name, state) {
if (name === "sources") dialog(state);
else if (name === "sources-apply") apply(state);
else return false;
return true;
}
return { NAMES, get, hidden, columns, label, set, handle };
})();