Checkpoint: Seed des données OPEX depuis les fichiers JSON sources vers la base de données MySQL. 33 postes 2025 + 35 postes 2026 + 2030 montants établissements 2026 insérés. Script scripts/seed-opex.mjs créé pour réimport futur. La page DSI OPEX charge désormais les données depuis la BDD sans dépendre du localStorage.
This commit is contained in:
139
scripts/seed-opex.mjs
Normal file
139
scripts/seed-opex.mjs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user