140 lines
4.8 KiB
JavaScript
140 lines
4.8 KiB
JavaScript
/**
|
||
* Script de seed : migration des données OPEX 2025 et 2026
|
||
* depuis les fichiers JSON sources vers la base de données MySQL.
|
||
*
|
||
* Usage : node scripts/seed-opex.mjs
|
||
*/
|
||
|
||
import { createRequire } from 'module';
|
||
import { readFileSync } from 'fs';
|
||
import { resolve, dirname } from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
import mysql from 'mysql2/promise';
|
||
import dotenv from 'dotenv';
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const projectRoot = resolve(__dirname, '..');
|
||
|
||
// Charger les variables d'environnement
|
||
dotenv.config({ path: resolve(projectRoot, '.env') });
|
||
|
||
const DATABASE_URL = process.env.DATABASE_URL;
|
||
if (!DATABASE_URL) {
|
||
console.error('❌ DATABASE_URL manquant dans .env');
|
||
process.exit(1);
|
||
}
|
||
|
||
// Lire les fichiers JSON sources
|
||
const data2025 = JSON.parse(readFileSync(resolve(projectRoot, 'client/src/data_opex_2025.json'), 'utf-8'));
|
||
const data2026 = JSON.parse(readFileSync(resolve(projectRoot, 'client/src/data_opex.json'), 'utf-8'));
|
||
|
||
async function seedOpex(conn, data) {
|
||
const annee = data.annee;
|
||
const postes = data.postes;
|
||
const etablissements = data.etablissements; // liste ou vide
|
||
|
||
console.log(`\n📅 Migration OPEX ${annee} — ${postes.length} postes, ${etablissements?.length ?? 0} établissements`);
|
||
|
||
// 1. Vérifier si des données existent déjà pour cette année
|
||
const [existing] = await conn.execute(
|
||
'SELECT COUNT(*) as cnt FROM opex_postes WHERE annee = ?',
|
||
[annee]
|
||
);
|
||
const count = existing[0].cnt;
|
||
|
||
if (count > 0) {
|
||
console.log(` ⚠️ ${count} postes déjà présents pour ${annee} — suppression et réimport...`);
|
||
await conn.execute('DELETE FROM opex_montants_etab WHERE annee = ?', [annee]);
|
||
await conn.execute('DELETE FROM opex_postes WHERE annee = ?', [annee]);
|
||
}
|
||
|
||
// 2. Insérer les postes
|
||
const montantKey = `montant_previsionnel_${annee}`;
|
||
let nbPostesInseres = 0;
|
||
|
||
for (const poste of postes) {
|
||
const montant = poste[montantKey] ?? poste.montant_previsionnel ?? null;
|
||
await conn.execute(
|
||
`INSERT INTO opex_postes
|
||
(annee, colIdx, libelle, libelleCourt, libelleDetail, fournisseur, categorie,
|
||
type, facturation, modeVentilation, compte, detail, budgetN1, montant, isCustom)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)`,
|
||
[
|
||
annee,
|
||
poste.col_idx ?? 0,
|
||
poste.libelle ?? '',
|
||
poste.libelle_court ?? null,
|
||
poste.libelle_detail ?? null,
|
||
poste.fournisseur ?? null,
|
||
poste.categorie ?? null,
|
||
poste.type ?? null,
|
||
poste.facturation ?? null,
|
||
poste.mode_ventilation ?? 'Prorata C 6',
|
||
poste.compte ?? null,
|
||
poste.detail ?? null,
|
||
poste.budget_n1 ?? null,
|
||
montant,
|
||
]
|
||
);
|
||
nbPostesInseres++;
|
||
}
|
||
console.log(` ✅ ${nbPostesInseres} postes insérés`);
|
||
|
||
// 3. Insérer les montants par établissement (si disponibles) — batch INSERT
|
||
if (Array.isArray(etablissements) && etablissements.length > 0) {
|
||
const rows = [];
|
||
for (const etab of etablissements) {
|
||
const code = etab.code;
|
||
const montants = etab.montants ?? {};
|
||
for (const [libellePoste, montantVal] of Object.entries(montants)) {
|
||
if (montantVal === null || montantVal === undefined) continue;
|
||
rows.push([annee, code, libellePoste, montantVal]);
|
||
}
|
||
}
|
||
|
||
// Insérer par lots de 500 pour éviter les limites MySQL
|
||
const BATCH_SIZE = 500;
|
||
let nbMontants = 0;
|
||
for (let i = 0; i < rows.length; i += BATCH_SIZE) {
|
||
const batch = rows.slice(i, i + BATCH_SIZE);
|
||
const placeholders = batch.map(() => '(?, ?, ?, ?)').join(', ');
|
||
const values = batch.flat();
|
||
await conn.execute(
|
||
`INSERT INTO opex_montants_etab (annee, etablissementCode, libellePoste, montant)
|
||
VALUES ${placeholders}`,
|
||
values
|
||
);
|
||
nbMontants += batch.length;
|
||
}
|
||
console.log(` ✅ ${nbMontants} montants établissements insérés`);
|
||
} else {
|
||
console.log(` ℹ️ Pas de montants par établissement pour ${annee} (seront calculés à la volée)`);
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('🚀 Démarrage du seed OPEX...');
|
||
console.log(`📡 Connexion à la base de données...`);
|
||
|
||
const conn = await mysql.createConnection(DATABASE_URL);
|
||
|
||
try {
|
||
await seedOpex(conn, data2025);
|
||
await seedOpex(conn, data2026);
|
||
|
||
// Résumé final
|
||
const [total] = await conn.execute('SELECT COUNT(*) as cnt FROM opex_postes');
|
||
const [totalMontants] = await conn.execute('SELECT COUNT(*) as cnt FROM opex_montants_etab');
|
||
console.log(`\n🎉 Seed terminé !`);
|
||
console.log(` Total postes en BDD : ${total[0].cnt}`);
|
||
console.log(` Total montants établissements : ${totalMontants[0].cnt}`);
|
||
} finally {
|
||
await conn.end();
|
||
}
|
||
}
|
||
|
||
main().catch(err => {
|
||
console.error('❌ Erreur lors du seed :', err.message);
|
||
process.exit(1);
|
||
});
|