chore: Désactivation temporaire export SEPA + amélioration validation

- Désactivation du bouton export SEPA avec tooltip 'Bientôt disponible'
- Suppression de la card informative SEPA (en attente tests élargis)
- Amélioration validation IBAN : ajout checksum modulo 97
- Ajout fichier d'audit complet SEPA_EXPORT_AUDIT.md

Raison : Système fonctionnel (testé avec succès sur Qonto) mais
nécessite tests avec autres banques avant activation production.

Le bouton 'Marquer comme payé' reste actif et fonctionnel.
This commit is contained in:
odentas 2025-11-03 00:14:10 +01:00
parent 91e0919274
commit 265ed6ce67
3 changed files with 343 additions and 28 deletions

300
SEPA_EXPORT_AUDIT.md Normal file
View file

@ -0,0 +1,300 @@
# Audit : Système d'Export SEPA ISO 20022
**Date de création** : 3 novembre 2025
**Statut actuel** : EN TEST (désactivé en production)
**Version** : 1.0
**Norme** : ISO 20022 pain.001.001.03
---
## 📊 Résumé Exécutif
Le système d'export SEPA permet aux clients gérant eux-mêmes leurs virements de salaires de générer un fichier XML standardisé pour effectuer des virements groupés depuis leur banque.
**Statut** : ✅ Fonctionnel mais désactivé en production
**Raison** : Tests en conditions réelles incomplets (1 seule banque testée)
---
## ✅ Tests Réussis
### Test Banque Qonto (3 novembre 2025)
- ✅ Fichier XML accepté sans erreur
- ✅ Salariés correctement identifiés
- ✅ IBAN des salariés validés
- ✅ Montants corrects (incluant regroupement par employé)
- ✅ BIC optionnel (non requis par Qonto)
- ✅ Format SEPA conforme
**Verdict Qonto** : 100% fonctionnel ✓
---
## 🔧 Implémentation Technique
### Fichiers concernés
1. **`app/api/virements-salaires/export-sepa/route.ts`** (378 lignes)
- Génération XML ISO 20022 pain.001.001.03
- Validation IBAN avec checksum modulo 97
- Regroupement des paiements par employé
- Sanitization XML (caractères spéciaux, limites de longueur)
2. **`app/(app)/virements-salaires/page.tsx`** (modifié)
- Interface de sélection multiple (checkboxes)
- Bouton d'export (actuellement désactivé)
- Modal de succès/avertissements
- Gestion des salariés sans IBAN
3. **`app/api/virements-salaires/[id]/route.ts`** (nouveau)
- Route PATCH pour marquage individuel/groupé
- Mise à jour `transfer_done` et `transfer_done_at`
### Fonctionnalités implémentées
- ✅ Sélection multiple via checkboxes
- ✅ Export XML SEPA conforme
- ✅ Validation IBAN (checksum modulo 97)
- ✅ Regroupement automatique par employé
- ✅ Gestion des salariés sans IBAN (exclusion + avertissement)
- ✅ Référence de virement : "Nom organisation - Période"
- ✅ Montant : `net_after_withholding` (net à payer après PAS)
- ✅ Limites SEPA respectées (70 char noms, 140 char libellés)
- ✅ Modal de confirmation/résultats
- ✅ Marquage groupé des paies comme payées
---
## 🎯 Validations Techniques
### Format XML
- ✅ Namespace correct : `urn:iso:std:iso:20022:tech:xsd:pain.001.001.03`
- ✅ Structure conforme : GrpHdr + PmtInf + CdtTrfTxInf
- ✅ Montants : format `0.00` (2 décimales)
- ✅ Dates : format ISO 8601
- ✅ Message ID unique : `MSG{timestamp}{random}`
- ✅ Currency : EUR
- ✅ Payment method : TRF (Transfer)
- ✅ Service level : SEPA
- ✅ Charge bearer : SLEV (Service Level)
### Validation des données
- ✅ IBAN : validation avec checksum modulo 97
- ✅ IBAN : longueur 15-34 caractères
- ✅ IBAN : format `AA11XXXX...`
- ✅ BIC : optionnel (laissé vide si absent)
- ✅ Montants : agrégation par employé si plusieurs paies
- ✅ Sanitization : suppression caractères XML dangereux
- ✅ Troncature : limites SEPA respectées
### Sécurité
- ✅ Authentification Supabase requise
- ✅ Vérification organization_id
- ✅ Isolation des données par organisation
- ✅ Pas d'exposition de données sensibles côté client
- ✅ Validation des paramètres d'entrée
---
## ⚠️ Limitations Connues
### 1. Support contrats
- ❌ **Seulement CDDU** : La requête SQL ne récupère que `cddu_contracts`
- ⚠️ **Pas de RG** : Les contrats régime général ne sont pas inclus
- **Impact** : Export incomplet pour organisations mixtes CDDU+RG
### 2. Tests bancaires
- ✅ **Qonto** : Testé et validé
- ❓ **Autres banques** : Non testées
- BNP Paribas
- Société Générale
- Crédit Agricole
- LCL
- Banque Postale
- etc.
### 3. Validation XML
- ⚠️ Pas de validation contre le schéma XSD officiel
- ✅ Structure conforme au standard
- ✅ Accepté par Qonto (premier test réel)
### 4. BIC
- ⚠️ BIC optionnel (peut être requis par certaines banques)
- ✅ Qonto n'en a pas besoin
- ❓ Autres banques : à vérifier
---
## 📋 Plan de Déploiement
### Phase 1 : Tests Élargis (EN COURS)
**Objectif** : Tester avec 3-5 banques différentes
- [ ] BNP Paribas
- [ ] Crédit Agricole
- [ ] LCL
- [x] Qonto ✓
- [ ] Banque Postale
**Critères de validation** :
- Fichier accepté sans erreur
- Virements exécutés correctement
- Montants corrects
- Bénéficiaires corrects
### Phase 2 : Support Contrats RG
**Objectif** : Inclure les contrats régime général
**Modifications nécessaires** :
1. Modifier la requête SQL pour inclure `rg_contracts`
2. Unifier la structure de données (CDDU + RG)
3. Tester avec organisations mixtes
4. Mettre à jour la documentation
**Estimation** : 2-4 heures de développement
### Phase 3 : Validation XSD (Optionnel)
**Objectif** : Valider le XML contre le schéma officiel
**Avantages** :
- Garantie de conformité totale
- Détection d'erreurs avant envoi à la banque
**Inconvénients** :
- Complexité accrue
- Dépendance externe (bibliothèque XSD)
**Décision** : À évaluer selon retours Phase 1
### Phase 4 : Activation Production
**Pré-requis** :
- ✅ Tests réussis sur 3+ banques
- ✅ Support CDDU + RG
- ✅ Documentation utilisateur complète
- ✅ Support client prêt
**Actions** :
1. Réactiver le bouton d'export
2. Ajouter la card informative
3. Communiquer la nouveauté aux clients
4. Monitorer les premiers exports
---
## 🐛 Problèmes Connus
### Aucun problème critique identifié
Les fonctionnalités testées fonctionnent correctement. Les limitations sont documentées ci-dessus.
---
## 📝 Recommandations
### Avant activation en production
1. **Tester avec plus de banques** (priorité haute)
- Demander à 2-3 clients volontaires de tester
- Vérifier compatibilité BNP, CA, SG
- Documenter les retours
2. **Ajouter support RG** (priorité haute)
- Nécessaire pour organisations mixtes
- Évite confusion clients
3. **Améliorer les messages d'erreur** (priorité moyenne)
- Erreurs plus explicites côté client
- Guide de résolution des problèmes
4. **Documentation utilisateur** (priorité haute)
- Guide pas-à-pas
- FAQ
- Captures d'écran
5. **Monitoring** (priorité basse)
- Logger les exports réussis/échoués
- Alertes en cas d'erreurs répétées
- Analytics PostHog
### Après activation
1. **Collecte de feedback**
- Enquête satisfaction
- Taux d'utilisation
- Problèmes rencontrés
2. **Amélioration continue**
- Optimiser l'UX selon retours
- Ajouter fonctionnalités demandées
- Corriger bugs éventuels
---
## 📊 Métriques de Succès
### KPIs à suivre (après activation)
- Nombre d'exports par mois
- Taux de succès (exports acceptés par les banques)
- Nombre d'organisations utilisatrices
- Temps moyen de traitement
- Taux de satisfaction client
### Objectifs (6 mois après activation)
- 30% des organisations non-Odentas utilisent l'export SEPA
- 95%+ de taux de succès
- Satisfaction client > 4/5
---
## 🔐 Sécurité et Conformité
### RGPD
- ✅ Pas de stockage des fichiers SEPA générés
- ✅ Données uniquement en mémoire pendant génération
- ✅ Téléchargement direct côté client
- ✅ Pas de logs contenant des IBAN
### Sécurité bancaire
- ✅ Pas de stockage de coordonnées bancaires sensibles (déjà en base)
- ✅ Validation stricte des montants
- ✅ Impossibilité de modifier les montants (lecture seule depuis base)
- ✅ Authentification requise
---
## 📅 Historique des Versions
### Version 1.0 (3 novembre 2025)
- Implémentation initiale
- Test réussi avec Qonto
- Support CDDU uniquement
- Validation IBAN avec checksum modulo 97
- Interface de sélection multiple
- Marquage groupé des paies
- **Statut** : Désactivé en production (en attente tests élargis)
---
## 🎓 Ressources
### Documentation de référence
- [ISO 20022 pain.001.001.03](https://www.iso20022.org/)
- [SEPA Credit Transfer](https://www.europeanpaymentscouncil.eu/what-we-do/sepa-credit-transfer)
- [Format IBAN](https://fr.iban.com/structure)
### Tests internes
- Organisation test : Atelier Moz
- Banque test : Qonto
- Date test : 3 novembre 2025
- Résultat : ✅ Succès
---
## 📞 Contact
Pour questions ou retours sur le système SEPA :
- Équipe Tech Odentas
- Documentation : `/SEPA_EXPORT_FEATURE.md`
- Code : `/app/api/virements-salaires/export-sepa/`
---
**Dernière mise à jour** : 3 novembre 2025
**Prochaine révision** : Après tests banques supplémentaires

View file

@ -912,24 +912,6 @@ export default function VirementsPage() {
</div>
</div>
{/* Card informative SEPA pour clients non-Odentas */}
{!isOdentas && (
<div className="mt-4 rounded-xl border border-blue-200 bg-blue-50 p-4">
<div className="flex items-start gap-3">
<div className="rounded-lg bg-blue-100 p-2 mt-0.5">
<Info className="h-4 w-4 text-blue-700" />
</div>
<div className="flex-1">
<p className="text-sm text-blue-900 mb-2">
<span className="font-semibold">Nouveau : Export SEPA</span> Générez un fichier XML (norme ISO 20022) pour effectuer vos virements salaires groupés depuis votre banque, si celle-ci le permet. Il vous suffit de cocher les paies concernées.
</p>
<p className="text-sm text-blue-900">
Vous pouvez également marquer plusieurs salaires comme payés en une seule fois en sélectionnant les paies et en cliquant sur Marquer comme payé.
</p>
</div>
</div>
</div>
)}
</section>
{/* Tableau principal */}
@ -1052,14 +1034,22 @@ export default function VirementsPage() {
<Check className="w-4 h-4" />
{isMarkingPaid ? 'Marquage en cours...' : 'Marquer comme payé'}
</button>
<button
onClick={handleExportSepa}
disabled={isExporting}
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<Download className="w-4 h-4" />
{isExporting ? 'Export en cours...' : 'Exporter fichier bancaire (SEPA)'}
</button>
<div className="relative group">
<button
disabled
className="inline-flex items-center gap-2 px-4 py-2 bg-slate-300 text-slate-500 rounded-lg cursor-not-allowed opacity-60"
>
<Download className="w-4 h-4" />
Exporter fichier bancaire (SEPA)
</button>
<div
role="tooltip"
className="pointer-events-none absolute right-0 top-full mt-2 w-48 px-3 py-2 rounded-lg bg-slate-900 text-white text-xs shadow-lg opacity-0 group-hover:opacity-100 translate-y-1 group-hover:translate-y-0 transition z-10"
>
Bientôt disponible
<div className="absolute -top-1 right-6 w-2 h-2 rotate-45 bg-slate-900" />
</div>
</div>
</div>
</div>
</div>

View file

@ -39,10 +39,35 @@ function sanitizeXML(str: string, maxLength?: number): string {
return cleaned;
}
// Fonction pour valider un IBAN (basique)
// Fonction pour valider un IBAN avec checksum (modulo 97)
function isValidIBAN(iban: string): boolean {
const cleaned = iban.replace(/\s/g, '').toUpperCase();
return /^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(cleaned) && cleaned.length >= 15 && cleaned.length <= 34;
// Vérification du format de base
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(cleaned) || cleaned.length < 15 || cleaned.length > 34) {
return false;
}
// Vérification du checksum (modulo 97)
// Déplacer les 4 premiers caractères à la fin
const rearranged = cleaned.slice(4) + cleaned.slice(0, 4);
// Convertir les lettres en chiffres (A=10, B=11, ..., Z=35)
const numeric = rearranged.split('').map(char => {
const code = char.charCodeAt(0);
return code >= 65 && code <= 90 ? (code - 55).toString() : char;
}).join('');
// Calculer le modulo 97
let remainder = '';
for (let i = 0; i < numeric.length; i++) {
remainder += numeric[i];
if (remainder.length > 9) {
remainder = (parseInt(remainder, 10) % 97).toString();
}
}
return parseInt(remainder, 10) % 97 === 1;
}
// Fonction pour extraire le BIC depuis l'IBAN si non fourni (approximatif)