Checkpoint: Suppression des 6 lignes parasites en BDD (Infogérance, Sécurité, Applicatifs HEP, Applicatifs SMR, Téléphonie, TOTAL) + filtre PARASITE_PATTERNS dans handleFileImport pour empêcher leur réinsertion lors d'un import Excel.

This commit is contained in:
Manus
2026-06-11 08:48:10 +00:00
parent eb1684f04c
commit 60fd2b891e
4 changed files with 98 additions and 1 deletions

View File

@@ -521,8 +521,17 @@ function ClesRepartitionView({ annee, basesRepartitionRaw, isLoadingBases, isErr
} }
// ── Construction des lignes à importer ─────────────────────────────── // ── Construction des lignes à importer ───────────────────────────────
// Mots-clés qui indiquent une ligne parasite (catégorie ou synthèse, pas un établissement)
const PARASITE_PATTERNS = /^(infog|s.curit|t.l.phonie|app global|app hep|app smr|applicatifs|total|ventilation|r.partition|nouveaut)/i;
const importRows = data.slice(headerIdx + 1) const importRows = data.slice(headerIdx + 1)
.filter(row => row[codeIdx] && String(row[codeIdx]).trim()) .filter(row => {
const code = String(row[codeIdx] ?? '').trim();
if (!code) return false;
// Rejeter les lignes dont le code ressemble à un libellé de catégorie
const codeNorm = code.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
if (PARASITE_PATTERNS.test(codeNorm)) return false;
return true;
})
.map(row => ({ .map(row => ({
etablissementCode: String(row[codeIdx]).trim(), etablissementCode: String(row[codeIdx]).trim(),
etablissementNom: nomIdx >= 0 ? String(row[nomIdx]).trim() || null : null, etablissementNom: nomIdx >= 0 ? String(row[nomIdx]).trim() || null : null,

View File

@@ -0,0 +1,51 @@
import { createConnection } from 'mysql2/promise';
import { config } from 'dotenv';
config();
const conn = await createConnection(process.env.DATABASE_URL);
// Libellés parasites connus (noms de catégories ou lignes de synthèse)
const PARASITES_KEYWORDS = [
'Infogérance', 'Infogerance', 'Sécurité', 'Securite',
'Téléphonie', 'Telephonie',
'App global', 'App HEP', 'App SMR',
'Applicatifs', // commence par
'TOTAL', 'Total',
'Ventilation', 'Répartition', 'Repartition',
'Nouveautés', 'Nouveautes',
];
// 1. Lister les lignes parasites
const [rows] = await conn.execute(
`SELECT id, annee, etablissementCode, etablissementNom FROM opex_bases_repartition ORDER BY annee, etablissementCode`
);
console.log('=== Toutes les lignes opex_bases_repartition ===');
for (const r of rows) {
const code = r.etablissementCode ?? '';
const nom = r.etablissementNom ?? '';
const isParasite = PARASITES_KEYWORDS.some(k =>
code.toLowerCase().includes(k.toLowerCase()) ||
nom.toLowerCase().includes(k.toLowerCase())
) || /^(total|ventilation|répartition|applicatifs)/i.test(code.trim());
if (isParasite) {
console.log(` PARASITE id=${r.id} annee=${r.annee} code="${code}" nom="${nom}"`);
}
}
// 2. Supprimer les parasites
const [delResult] = await conn.execute(
`DELETE FROM opex_bases_repartition
WHERE etablissementCode REGEXP '^(Infog|S.curit|T.l.phonie|App global|App HEP|App SMR|Applicatifs|TOTAL|Total|Ventilation|R.partition|Nouveaut)'
OR etablissementNom REGEXP '^(Infog|S.curit|T.l.phonie|App global|App HEP|App SMR|Applicatifs|TOTAL|Total|Ventilation|R.partition|Nouveaut)'`
);
console.log(`\nSupprimé ${delResult.affectedRows} lignes parasites.`);
// 3. Vérification finale
const [remaining] = await conn.execute(
`SELECT annee, COUNT(*) as cnt FROM opex_bases_repartition GROUP BY annee ORDER BY annee`
);
console.log('=== Lignes restantes par année ===');
console.table(remaining);
await conn.end();

View File

@@ -0,0 +1,33 @@
import { createConnection } from 'mysql2/promise';
import { config } from 'dotenv';
config();
const conn = await createConnection(process.env.DATABASE_URL);
// 1. Voir les doublons
const [doublons] = await conn.execute(
'SELECT annee, libelle, COUNT(*) as cnt FROM opex_postes GROUP BY annee, libelle HAVING cnt > 1 ORDER BY cnt DESC LIMIT 30'
);
console.log('=== Doublons (annee, libelle) ===');
console.table(doublons);
// 2. Total par année
const [totaux] = await conn.execute(
'SELECT annee, COUNT(*) as cnt FROM opex_postes GROUP BY annee ORDER BY annee'
);
console.log('=== Total postes par année ===');
console.table(totaux);
// 3. Libellés qui ressemblent à des catégories (parasites)
const [parasites] = await conn.execute(
`SELECT id, annee, libelle, categorie FROM opex_postes
WHERE libelle IN ('Infogérance','Sécurité','Téléphonie','App global','App HEP','App SMR',
'Applicatifs communs','Applicatifs PA','Applicatifs HEP','Applicatifs SMR','Nouveautés','TOTAL',
'Ventilation cout','Ventilation coût')
OR libelle REGEXP '^(total|ventilation|répartition)'
ORDER BY annee, libelle`
);
console.log('=== Libellés parasites en BDD ===');
console.table(parasites);
await conn.end();

View File

@@ -61,3 +61,7 @@
- [x] Bug : doublon de ligne dans le tableau Clés de répartition au clic (mutation setBaseRepartition provoque un double rendu) - [x] Bug : doublon de ligne dans le tableau Clés de répartition au clic (mutation setBaseRepartition provoque un double rendu)
- [x] Fonctionnalité : mode "tout manuel" par établissement dans l'onglet Clés de répartition (toutes les colonnes OPEX de cet établissement saisies manuellement, ignorant le calcul prorata) - [x] Fonctionnalité : mode "tout manuel" par établissement dans l'onglet Clés de répartition (toutes les colonnes OPEX de cet établissement saisies manuellement, ignorant le calcul prorata)
## Corrections (post-checkpoint eb1684f0)
- [x] Bug : lignes parasites (catégories Infogérance, Sécurité, TOTAL, etc.) dans la vue Établissements OPEX — suppression en BDD + filtre anti-parasites dans l'import