// 원본 품명정리(TfrmHead ActionNameDeivde 0xf5a2b4·TfrmNameDevide 적용 0xf595c0·규격으로지정 0xf59640)와 // 자재단가·견적단가 등록(popMainStuffcReg·popMainEstimateReg → c_f00264·c_f00670). // 품명정리: 기존명칭에서 고른 자리 앞 = 품명(다듬지 않음), 뒤에서 「삭제할 칸」만큼 떼고 기존 규격을 붙여 다듬은 값 = 규격, // 단위는 비어 있을 때만 채움. 고른 일위대가 호표마다 같은 자리로 나누고 HeadTitleDat 의 같은 코드도 맞춘다. // 등록: 같은 품명·규격(빈칸·| 무시) 있으면 거부, 코드 = 머리글자(M·W) + PkID 10자리(쓰인 코드면 하나씩 올림), // 자재 Cost1 = TC = 행 재료단가 · 견적 NC/GC/JC = 행 노무/재료/경비단가, 원래 행은 새 코드·연결(5·101)로 바꾼다. const EstNameReg = (() => { const num = (value) => Number(value) || 0; const strip = (value) => String(value ?? "").replace(/[\s|]/g, ""); function divide(project, indices, position, cut, unit) { if (!Number.isInteger(position) || position < 0) throw Error("나눌 자리를 고르세요."); if (!Number.isInteger(cut) || cut < 0) throw Error("삭제할 칸은 0 이상의 정수입니다."); const rows = project.tables.HeadMainDat, titles = project.tables.HeadTitleDat || []; const picked = indices.filter((i) => num(rows[i]?.Index) > 0); if (!picked.length) throw Error("품명을 정리할 일위대가 호표(제목 줄)를 고르세요."); for (const i of picked) { const row = rows[i], name = String(row.Name ?? ""); const rest = name.slice(position).slice(cut); row.Name = name.slice(0, position); row.Size = `${rest}${row.Size ?? ""}`.trim(); if (!strip(row.Unite) && unit) row.Unite = unit; const title = titles.find((t) => t.Code === row.Code); if (title) Object.assign(title, { Name: row.Name, Size: row.Size, Unite: row.Unite }); } return picked.length; } const targets = { stuff: { table: "StuffcDat", kind: 5, letter: "M", label: "자재단가" }, estimate: { table: "EstiMateDat", kind: 101, letter: "W", label: "견적단가" }, }; function register(project, view, index, which) { const target = targets[which], row = project.tables[view]?.[index]; if (!target || !row) throw Error("등록할 행을 고르세요."); if (view === "HeadMainDat" && num(row.Index) > 0) throw Error(`호표는 ${target.label}에 등록할수 없습니다.`); if (!String(row.Name ?? "").trim()) throw Error("명칭을 입력하세요."); const rows = (project.tables[target.table] ||= []); const same = rows.find( (r) => strip(r.Name) === strip(row.Name) && strip(r.Size) === strip(row.Size), ); if (same) throw Error(`같은 명칭의 데이타가 있습니다 . ${target.label} (${num(same.Index)} 번)`); let pk = Math.max(0, ...rows.map((r) => num(r.PkID))) + 1; const used = new Set(rows.map((r) => r.Code)); let code = `${target.letter}${String(pk).padStart(10, "0").slice(-10)}`; while (used.has(code)) code = `${target.letter}${String(++pk).padStart(10, "0").slice(-10)}`; const schema = rows[0] ? Object.keys(rows[0]) : ["Name", "Size", "Unite"]; const record = {}; for (const key of schema) if (!["PkID", "Index", "Code"].includes(key) && Object.hasOwn(row, key)) record[key] = row[key]; Object.assign(record, { PkID: pk, Code: code, Index: rows.length + 1 }); if (which === "stuff") Object.assign(record, { Cost1: num(row.GU), TC: num(row.GU), OTC: num(row.GU) }); else { const [nc, gc, jc] = [num(row.NU), num(row.GU), num(row.JU)]; Object.assign(record, { NC: nc, GC: gc, JC: jc, TC: nc + gc + jc, ONC: nc, OGC: gc, OJC: jc, OTC: nc + gc + jc, }); } rows.push(record); row.Code = code; row.LinkIndex = target.kind; return code; } function divideDialog(state) { if (state.view !== "HeadMainDat") throw Error("일위대가 화면에서 품명을 정리하세요."); const rows = state.project.tables.HeadMainDat, picked = [...state.selection].sort((a, b) => a - b).filter((i) => num(rows[i]?.Index) > 0); if (!picked.length) throw Error("품명을 정리할 일위대가 호표(제목 줄)를 고르세요."); const { esc } = EstApp; EstApp.showDialog( "품명정리", `
기존명칭에서 품명과 규격을 나눌 자리에 커서를 두고 「규격으로 지정」을 누르세요. 고른 ${picked.length}개 호표에 같은 자리로 적용합니다.
`, ); state.nameDivide = { picked, position: null }; } function field(name) { return document.querySelector(`#dialog-content [name="${name}"]`); } function handle(name, state) { if (name === "name-divide") divideDialog(state); else if (name === "name-divide-set") { const all = field("nd-all"), position = all.selectionStart ?? 0; state.nameDivide.position = position; field("nd-name").value = all.value.slice(0, position); field("nd-size").value = all.value.slice(position); } else if (name === "name-divide-apply") { const info = state.nameDivide; if (info?.position == null) throw Error("먼저 「규격으로 지정」으로 나눌 자리를 정하세요."); const cut = Number(field("nd-cut").value), unit = field("nd-unit").value.trim(); EstApp.byId("dialog").close(); let count; EstApp.change((project) => (count = divide(project, info.picked, info.position, cut, unit))); state.notice = `${count}개 호표의 품명을 정리했습니다.`; } else if (name === "register-stuff" || name === "register-estimate") { const which = name === "register-stuff" ? "stuff" : "estimate", index = [...state.selection][0], row = state.project.tables[state.view]?.[index]; if (!["EstMainDat", "HeadMainDat"].includes(state.view) || state.selection.size !== 1 || !row) throw Error("내역서·일위대가에서 등록할 행 하나를 고르세요."); const label = targets[which].label; register(structuredClone(state.project), state.view, index, which); if (!confirm(`"${row.Name ?? ""}" ${row.Size ?? ""} 를 ${label}에 등록하시겠습니까?`)) return true; let code; EstApp.change((project) => (code = register(project, state.view, index, which))); state.notice = `${label}에 ${code}로 등록하고 이 행을 연결했습니다.`; } else return false; return true; } return { divide, register, handle }; })();