diff --git a/batch_reformat_codes.js b/batch_reformat_codes.js new file mode 100644 index 0000000..d0ba02f --- /dev/null +++ b/batch_reformat_codes.js @@ -0,0 +1,124 @@ +import mysql from 'mysql2/promise'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const TYPE_PREFIX_MAP = { + '서버': 'SVR', '가상서버(VM)': 'VM', '워크스테이션': 'WKS', '서버PC': 'PC', + '개인PC': 'PC', '공용PC': 'PC', '노트북': 'NBK', '태블릿': 'TAB', + 'NAS': 'NAS', 'DAS': 'DAS', '스토리지': 'STO', '스토리지 렉': 'STO', + '스위치': 'NET', '방화벽': 'NET', '공유기': 'NET', '허브': 'NET', '네트워크': 'NET', + '모니터': 'MNT', '프린터': 'PRT', '스캐너': 'SCN', '복합기': 'MFP', '빔프로젝터': 'PRJ', '화상회의장비': 'VCF', '업무지원장비': 'EQP', + 'CPU': 'CPU', 'HDD': 'HDD', 'RAM': 'RAM', 'GPU': 'GPU', 'SSD': 'SSD', '메인보드': 'MBD', '파워서플라이': 'PWR', '쿨러': 'CLR', '케이스': 'CAS', 'PC부품': 'PRT', + '드론': 'DRO', '측량장비': 'SUR', '보조기기': 'SUR', '공간정보장비': 'SUR', + '책상': 'FRN', '의자': 'FRN', '캐비닛': 'FRN', '사무가구': 'FRN', + '구독SW': 'SW', '영구SW': 'SW', '외부': 'SW', '내부': 'INT', + '선물': 'GFT', 'VIP': 'VIP' +}; + +function formatPurchaseDate(date) { + if (!date) return '000000'; + let s = String(date).replace(/[^0-9]/g, ''); + if (s.length >= 6) { + return s.substring(0, 6); + } + return '000000'; +} + +async function reformatAllCodes() { + const connection = await mysql.createConnection({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: parseInt(process.env.DB_PORT || '3306') + }); + + try { + const tables = [ + 'asset_pc', 'asset_server', 'asset_network', 'asset_storage', + 'asset_equipment', 'asset_survey', 'asset_pc_parts', 'asset_office_supplies', + 'asset_sw_external', 'asset_sw_internal', 'asset_vip' + ]; + + let allAssets = []; + + for (const table of tables) { + try { + const [rows] = await connection.query(`SELECT * FROM ${table}`); + allAssets = allAssets.concat(rows.map(r => ({ ...r, sourceTable: table }))); + } catch (err) { + if (err.code === 'ER_NO_SUCH_TABLE') { + console.log(`Skipping missing table: ${table}`); + } else { + console.error(`Error querying ${table}:`, err.message); + } + } + } + + console.log(`Total assets loaded: ${allAssets.length}`); + + // Process each asset + const processed = allAssets.map(a => { + // 1. Determine prefix + let prefix = 'AST'; + if (a.asset_type && TYPE_PREFIX_MAP[a.asset_type]) { + prefix = TYPE_PREFIX_MAP[a.asset_type]; + } else if (a.category && TYPE_PREFIX_MAP[a.category]) { + prefix = TYPE_PREFIX_MAP[a.category]; + } else if (a.sourceTable === 'asset_sw_external') prefix = 'SW'; + else if (a.sourceTable === 'asset_sw_internal') prefix = 'INT'; + + // 2. Determine YYYYMM + const dateStr = a.purchase_date || a.start_date || ''; // start_date for SW + const yyyymm = formatPurchaseDate(dateStr); + + return { ...a, prefix, yyyymm }; + }); + + // Group by Prefix + const groups = {}; + processed.forEach(a => { + if (!groups[a.prefix]) groups[a.prefix] = []; + groups[a.prefix].push(a); + }); + + // Start renaming + for (const prefix in groups) { + const items = groups[prefix]; + + // Sort logic to maintain some order (by date then id) + items.sort((a, b) => { + if (a.yyyymm !== b.yyyymm) return a.yyyymm.localeCompare(b.yyyymm); + return String(a.id).localeCompare(String(b.id)); + }); + + console.log(`Processing group ${prefix}: ${items.length} items`); + + // Temporary rename to avoid UNIQUE constraint conflicts during sequential updates + for (const item of items) { + const tempCode = `TEMP-${Math.random().toString(36).substring(2, 10)}-${item.id}`; + await connection.query(`UPDATE ${item.sourceTable} SET asset_code = ? WHERE id = ?`, [tempCode, item.id]); + } + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const serial = String(i + 1).padStart(4, '0'); // SVR-202209-0001 + + // Some formats might want 3 or 4 digits. Defaulting to 4. + const newCode = `${prefix}-${item.yyyymm}-${serial}`; + + await connection.query(`UPDATE ${item.sourceTable} SET asset_code = ? WHERE id = ?`, [newCode, item.id]); + } + } + + console.log('✅ Asset codes reformatted successfully.'); + + } catch (err) { + console.error('❌ Reformatting failed:', err); + } finally { + await connection.end(); + } +} + +reformatAllCodes(); diff --git a/src/components/Modal/HWModal.ts b/src/components/Modal/HWModal.ts index babf962..207406f 100644 --- a/src/components/Modal/HWModal.ts +++ b/src/components/Modal/HWModal.ts @@ -107,10 +107,18 @@ const HW_MODAL_HTML = `
| No. | - ${headers.map(h => `${h} | `).join('')} -
|---|---|
| ${idx + 1} | - ${headers.map(h => `${row[h] || '-'} | `).join('')} -