feat: Add bulk asset code generation in upload review modal
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { openModal, closeModals } from './BaseModal';
|
import { openModal, closeModals } from './BaseModal';
|
||||||
import { createIcons, X, Check, Database, Save, FileSpreadsheet, Layers } from 'lucide';
|
import { createIcons, X, Check, Database, Save, FileSpreadsheet, Layers, RefreshCcw } from 'lucide';
|
||||||
import { state, loadMasterDataFromDB } from '../../core/state';
|
import { state, loadMasterDataFromDB } from '../../core/state';
|
||||||
|
import { TYPE_PREFIX_MAP } from './SharedData';
|
||||||
|
|
||||||
let parsedData: any = null;
|
let parsedData: any = null;
|
||||||
let currentTab: string = '';
|
let currentTab: string = '';
|
||||||
@@ -37,6 +38,9 @@ const UPLOAD_PREVIEW_MODAL_HTML = `
|
|||||||
<div style="display:flex; align-items:center; gap:0.5rem;">
|
<div style="display:flex; align-items:center; gap:0.5rem;">
|
||||||
<span id="current-tab-name" style="font-weight:700; font-size:16px;">선택된 탭 없음</span>
|
<span id="current-tab-name" style="font-weight:700; font-size:16px;">선택된 탭 없음</span>
|
||||||
<span id="current-tab-count" class="badge badge-primary">0건</span>
|
<span id="current-tab-count" class="badge badge-primary">0건</span>
|
||||||
|
<button id="btn-bulk-generate-codes" class="btn btn-outline btn-sm hidden" style="margin-left:1rem; height:28px; font-size:12px; padding:0 0.75rem;">
|
||||||
|
<i data-lucide="refresh-ccw" style="width:14px; height:14px; margin-right:4px;"></i> 자산코드 일괄 생성
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:12px; color:var(--text-muted);">
|
<div style="font-size:12px; color:var(--text-muted);">
|
||||||
* 아래 데이터가 신규로 추가되거나 기존 데이터가 갱신됩니다.
|
* 아래 데이터가 신규로 추가되거나 기존 데이터가 갱신됩니다.
|
||||||
@@ -72,6 +76,9 @@ export function initUploadPreviewModal(onSuccess?: () => void) {
|
|||||||
document.getElementById('btn-confirm-upload')?.addEventListener('click', () => {
|
document.getElementById('btn-confirm-upload')?.addEventListener('click', () => {
|
||||||
confirmUpload();
|
confirmUpload();
|
||||||
});
|
});
|
||||||
|
document.getElementById('btn-bulk-generate-codes')?.addEventListener('click', () => {
|
||||||
|
generateBulkCodes();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openUploadPreview(data: any) {
|
export function openUploadPreview(data: any) {
|
||||||
@@ -87,7 +94,7 @@ export function openUploadPreview(data: any) {
|
|||||||
renderCurrentTable();
|
renderCurrentTable();
|
||||||
|
|
||||||
openModal('upload-preview-modal');
|
openModal('upload-preview-modal');
|
||||||
createIcons({ icons: { X, Check, Database, Save, FileSpreadsheet, Layers } });
|
createIcons({ icons: { X, Check, Database, Save, FileSpreadsheet, Layers, RefreshCcw } });
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTabs() {
|
function renderTabs() {
|
||||||
@@ -138,6 +145,13 @@ function renderCurrentTable() {
|
|||||||
tabNameEl.textContent = currentTab;
|
tabNameEl.textContent = currentTab;
|
||||||
tabCountEl.textContent = `${data.length}건`;
|
tabCountEl.textContent = `${data.length}건`;
|
||||||
|
|
||||||
|
const generateBtn = document.getElementById('btn-bulk-generate-codes');
|
||||||
|
const isHwTab = ['개인PC', '서버', '스토리지', '전산비품', '모바일기기'].includes(currentTab);
|
||||||
|
if (generateBtn) {
|
||||||
|
if (isHwTab) generateBtn.classList.remove('hidden');
|
||||||
|
else generateBtn.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
tableWrapper.innerHTML = '<div style="padding:4rem; text-align:center; color:var(--text-muted);">표시할 데이터가 없습니다.</div>';
|
tableWrapper.innerHTML = '<div style="padding:4rem; text-align:center; color:var(--text-muted);">표시할 데이터가 없습니다.</div>';
|
||||||
return;
|
return;
|
||||||
@@ -222,3 +236,63 @@ async function confirmUpload() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function generateBulkCodes() {
|
||||||
|
const data = parsedData[currentTab];
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
const generateBtn = document.getElementById('btn-bulk-generate-codes') as HTMLButtonElement;
|
||||||
|
if (generateBtn) {
|
||||||
|
generateBtn.disabled = true;
|
||||||
|
generateBtn.innerHTML = '<i data-lucide="refresh-ccw" class="animate-spin"></i> 생성 중...';
|
||||||
|
createIcons({ icons: { RefreshCcw } });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Group rows by prefix (type + purchase_ym)
|
||||||
|
const rowsToProcess = data.filter((r: any) => !r.자산코드);
|
||||||
|
if (rowsToProcess.length === 0) {
|
||||||
|
alert('이미 모든 항목에 자산코드가 부여되어 있습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups: Record<string, any[]> = {};
|
||||||
|
rowsToProcess.forEach((r: any) => {
|
||||||
|
const type = r.비품유형 || r.기기유형 || r.type || 'ETC';
|
||||||
|
const typeCode = TYPE_PREFIX_MAP[type] || 'ETC';
|
||||||
|
const purchaseYM = String(r.구매연월 || '').replace(/[^0-9]/g, '');
|
||||||
|
if (purchaseYM.length < 6) {
|
||||||
|
// Fallback or skip
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prefix = `${typeCode}-${purchaseYM.substring(0, 6)}-`;
|
||||||
|
if (!groups[prefix]) groups[prefix] = [];
|
||||||
|
groups[prefix].push(r);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const prefix in groups) {
|
||||||
|
const rows = groups[prefix];
|
||||||
|
// Fetch current next code for this prefix
|
||||||
|
const res = await fetch(`http://172.16.40.100:3000/api/generate-asset-code?prefix=${prefix}`);
|
||||||
|
const result = await res.json();
|
||||||
|
if (result.nextCode) {
|
||||||
|
let baseNum = parseInt(result.nextCode.replace(prefix, ''));
|
||||||
|
rows.forEach((r, idx) => {
|
||||||
|
r.자산코드 = `${prefix}${(baseNum + idx).toString().padStart(4, '0')}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCurrentTable();
|
||||||
|
alert(`${rowsToProcess.length}건의 자산코드가 생성되었습니다.`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
alert('자산코드 생성 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
if (generateBtn) {
|
||||||
|
generateBtn.disabled = false;
|
||||||
|
generateBtn.innerHTML = '<i data-lucide="refresh-ccw"></i> 자산코드 일괄 생성';
|
||||||
|
createIcons({ icons: { RefreshCcw } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user