Files
itinova-budget-si/scripts/seed-opex.mjs

140 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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);
});