- Enhanced backend asset code generation logic to handle multiple tables - Integrated asset code generation button in HWModal - Included utility scripts for asset code migration and DB synchronization - Resolved issues with missing purchase dates and duplicate asset codes
151 lines
5.2 KiB
JavaScript
151 lines
5.2 KiB
JavaScript
import mysql from 'mysql2/promise';
|
|
import XLSX from 'xlsx';
|
|
import dotenv from 'dotenv';
|
|
|
|
dotenv.config();
|
|
|
|
const { DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT } = process.env;
|
|
|
|
async function run() {
|
|
const pool = mysql.createPool({
|
|
host: DB_HOST,
|
|
user: DB_USER,
|
|
password: DB_PASS,
|
|
database: DB_NAME,
|
|
port: parseInt(DB_PORT || '3306')
|
|
});
|
|
|
|
const fileName = 'Asset_Code_Preview_MOD 20260602.xlsx';
|
|
console.log(`📖 Reading modified Excel: ${fileName}`);
|
|
const workbook = XLSX.readFile(fileName);
|
|
|
|
// 1. Fetch current DB state for matching
|
|
console.log('📡 Fetching current DB state...');
|
|
const [dbPcs] = await pool.query('SELECT * FROM asset_pc');
|
|
const [dbServers] = await pool.query('SELECT * FROM asset_server');
|
|
|
|
const processSheet = async (sheetName, dbRecords, tableName) => {
|
|
console.log(`🔍 Processing sheet: ${sheetName}`);
|
|
const sheet = workbook.Sheets[sheetName];
|
|
if (!sheet) {
|
|
console.warn(`⚠️ Sheet ${sheetName} not found.`);
|
|
return;
|
|
}
|
|
const data = XLSX.utils.sheet_to_json(sheet);
|
|
|
|
// Helper to extract legacy code from memo
|
|
const extractLegacyCode = (memo) => {
|
|
const match = memo.match(/\[Legacy:\s*([^\]]+)\]/);
|
|
return match ? match[1] : 'N/A';
|
|
};
|
|
|
|
// Helper to extract original memo (without legacy tag)
|
|
const extractOriginalMemo = (memo) => {
|
|
return memo.replace(/\[Legacy:\s*[^\]]+\]\s*/, '').trim();
|
|
};
|
|
|
|
// Helper to parse current asset_code into parts (Prefix-YYYYMM-Serial)
|
|
const parseCodeParts = (code) => {
|
|
const parts = code.split('-');
|
|
if (parts.length >= 3) {
|
|
return {
|
|
prefix: parts[0],
|
|
yyyymm: parts[1],
|
|
yyyy: parts[1].substring(0, 4), // Extract Year
|
|
serial: parts[parts.length - 1]
|
|
};
|
|
}
|
|
return { prefix: 'ETC', yyyymm: '000000', yyyy: '0000', serial: '0000' };
|
|
};
|
|
|
|
// 2. Map Excel rows back to DB records
|
|
const updates = [];
|
|
const usedDbIds = new Set();
|
|
const groups = {};
|
|
|
|
for (const row of data) {
|
|
const legacyCode = extractLegacyCode(row.memo || '');
|
|
const originalMemo = extractOriginalMemo(row.memo || '');
|
|
|
|
let match = null;
|
|
if (legacyCode !== 'N/A') {
|
|
match = dbRecords.find(db => !usedDbIds.has(db.id) && (db.asset_code || 'N/A') === legacyCode);
|
|
}
|
|
if (!match) {
|
|
match = dbRecords.find(db => !usedDbIds.has(db.id) && db.asset_type === row.asset_type && (db.asset_purpose || '') === row.asset_purpose);
|
|
}
|
|
if (!match) {
|
|
match = dbRecords.find(db => !usedDbIds.has(db.id) && db.asset_type === row.asset_type);
|
|
}
|
|
|
|
if (match) {
|
|
usedDbIds.add(match.id);
|
|
const parts = parseCodeParts(row.asset_code);
|
|
// Group by Prefix and Year (YYYY)
|
|
const groupKey = `${parts.prefix}-${parts.yyyy}`;
|
|
|
|
if (!groups[groupKey]) groups[groupKey] = [];
|
|
groups[groupKey].push({
|
|
dbId: match.id,
|
|
originalRow: row,
|
|
yyyymm: parts.yyyymm, // Keep the specific month for the code
|
|
requestedSerial: parts.serial
|
|
});
|
|
}
|
|
}
|
|
|
|
// 3. Resolve Duplicates and Re-sequence BY YEAR
|
|
console.log(`⚖️ Resolving duplicates for ${sheetName} (Yearly Sequencing)...`);
|
|
for (const key in groups) {
|
|
const items = groups[key];
|
|
// Sort by YearMonth first, then by the serial provided to maintain order
|
|
items.sort((a, b) => {
|
|
const monthCompare = a.yyyymm.localeCompare(b.yyyymm);
|
|
if (monthCompare !== 0) return monthCompare;
|
|
return a.requestedSerial.localeCompare(b.requestedSerial);
|
|
});
|
|
|
|
items.forEach((item, index) => {
|
|
const newSerial = String(index + 1).padStart(4, '0');
|
|
// Format remains Prefix-YYYYMM-Serial
|
|
const finalCode = `${item.originalRow.asset_code.split('-').slice(0, 2).join('-')}-${newSerial}`;
|
|
updates.push({ id: item.dbId, asset_code: finalCode, memo: extractOriginalMemo(item.originalRow.memo || '') });
|
|
});
|
|
}
|
|
|
|
// 4. Perform DB Updates
|
|
console.log(`💾 Updating ${tableName} in DB...`);
|
|
const connection = await pool.getConnection();
|
|
try {
|
|
await connection.beginTransaction();
|
|
|
|
// IMPORTANT: To avoid UNIQUE constraint errors during the update process,
|
|
// we first set all asset_codes to a temporary unique value.
|
|
console.log(`🔄 Clearing existing codes in ${tableName} for safe update...`);
|
|
await connection.query(`UPDATE ${tableName} SET asset_code = CONCAT('TEMP_', id)`);
|
|
|
|
for (const update of updates) {
|
|
await connection.query(
|
|
`UPDATE ${tableName} SET asset_code = ?, memo = ? WHERE id = ?`,
|
|
[update.asset_code, update.memo, update.id]
|
|
);
|
|
}
|
|
await connection.commit();
|
|
console.log(`✅ Updated ${updates.length} records in ${tableName}.`);
|
|
} catch (err) {
|
|
await connection.rollback();
|
|
console.error(`❌ Failed to update ${tableName}:`, err);
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
};
|
|
|
|
await processSheet('asset_server', dbServers, 'asset_server');
|
|
await processSheet('asset_pc', dbPcs, 'asset_pc');
|
|
|
|
console.log('🏁 All updates completed.');
|
|
await pool.end();
|
|
}
|
|
|
|
run().catch(console.error);
|