feat: Release v3.1.6 - Amélioration complète des flux de passages

- Optimisation des listes de passages (user/admin)
- Amélioration du flux de création avec validation temps réel
- Amélioration du flux de consultation avec export multi-formats
- Amélioration du flux de modification avec suivi des changements
- Ajout de la génération PDF pour les reçus
- Migration de la structure des uploads
- Implémentation de la file d'attente d'emails
- Ajout des permissions de suppression de passages
- Corrections de bugs et optimisations performances

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-21 17:57:27 +02:00
parent 890da22329
commit 4c2e809a35
24 changed files with 4605 additions and 1082 deletions

View File

@@ -0,0 +1,138 @@
# Gestion du champ chk_user_delete_pass
## 📋 Description
Le champ `chk_user_delete_pass` permet de contrôler si les membres d'une amicale peuvent supprimer des passages.
## 🔄 Modifications API
### 1. Base de données
- **Table** : `entites`
- **Champ** : `chk_user_delete_pass` TINYINT(1) DEFAULT 0
- **Valeurs** :
- `0` : Les membres NE peuvent PAS supprimer de passages (par défaut)
- `1` : Les membres PEUVENT supprimer des passages
### 2. Endpoints modifiés
#### POST /api/entites (Création)
- Le champ est automatiquement initialisé à `0` (false) lors de la création
- Non modifiable à la création
#### PUT /api/entites/{id} (Modification)
**Entrée JSON :**
```json
{
"chk_user_delete_pass": 1
}
```
- **Type** : Boolean (0 ou 1)
- **Obligatoire** : Non
- **Accès** : Administrateurs uniquement (fk_role > 1)
#### GET /api/entites/{id} (Récupération)
**Sortie JSON :**
```json
{
"id": 5,
"name": "Amicale de Pompiers",
"code_postal": "75001",
"ville": "Paris",
"chk_active": 1,
"chk_user_delete_pass": 0
}
```
#### GET /api/entites (Liste)
Retourne `chk_user_delete_pass` pour chaque entité dans la liste.
### 3. Route /api/login
Le champ `chk_user_delete_pass` est maintenant inclus dans la réponse de login dans les objets `amicale` :
**Réponse JSON :**
```json
{
"user": { ... },
"amicale": {
"id": 5,
"name": "Amicale de Pompiers",
"code_postal": "75001",
"ville": "Paris",
"chk_demo": 0,
"chk_mdp_manuel": 0,
"chk_username_manuel": 0,
"chk_copie_mail_recu": 0,
"chk_accept_sms": 0,
"chk_active": 1,
"chk_stripe": 0,
"chk_user_delete_pass": 0 // ← NOUVEAU CHAMP
}
}
```
## 🎯 Utilisation côté client
### Flutter/Web
Le client doit :
1. **Récupérer** la valeur de `chk_user_delete_pass` depuis la réponse login
2. **Stocker** cette valeur dans l'état de l'application
3. **Conditionner** l'affichage du bouton de suppression selon cette valeur
**Exemple Flutter :**
```dart
// Dans le modèle Amicale
class Amicale {
final int id;
final String name;
final bool chkUserDeletePass; // Nouveau champ
bool get canUserDeletePassage => chkUserDeletePass;
}
// Dans l'UI
if (amicale.canUserDeletePassage) {
// Afficher le bouton de suppression
IconButton(
icon: Icon(Icons.delete),
onPressed: () => deletePassage(passageId),
)
}
```
## ⚠️ Points importants
1. **Valeur par défaut** : Toujours `0` (false) pour la sécurité
2. **Modification** : Seuls les administrateurs (fk_role > 1) peuvent modifier ce champ
3. **Rétrocompatibilité** : Les entités existantes ont la valeur `0` par défaut
4. **Validation côté serveur** : L'API vérifiera également ce droit lors de la tentative de suppression
## 📝 Script SQL
Le script de migration est disponible dans :
```
/scripts/sql/add_chk_user_delete_pass.sql
```
## ✅ Checklist d'implémentation
### Côté API (déjà fait) :
- [x] Ajout du champ en base de données
- [x] Modification EntiteController (create, update, get)
- [x] Modification LoginController (réponse login)
- [x] Script SQL de migration
### Côté Client (à faire) :
- [ ] Ajouter le champ dans le modèle Amicale
- [ ] Parser le champ depuis la réponse login
- [ ] Stocker dans l'état de l'application
- [ ] Conditionner l'affichage du bouton suppression
- [ ] Tester avec des valeurs 0 et 1
## 🔒 Sécurité
Même si `chk_user_delete_pass = 1`, l'API devra vérifier :
- L'authentification de l'utilisateur
- L'appartenance à l'entité
- Le droit de suppression sur le passage spécifique
- Les règles métier (ex: pas de suppression après export)
---
**Date :** 20/08/2025
**Version API :** 3.1.4

View File

@@ -0,0 +1,165 @@
# API DELETE /passages/{id} - Documentation des permissions
## 📋 Endpoint
```
DELETE /api/passages/{id}
```
## 🔒 Authentification
- **Requise** : OUI (Bearer token)
- **Session** : Doit être valide
## 📊 Logique de permissions
### Règles par rôle :
| fk_role | Description | Peut supprimer ? | Conditions |
|---------|------------|------------------|------------|
| 1 | Membre | ✅ Conditionnel | Si `entites.chk_user_delete_pass = 1` |
| 2 | Admin amicale | ✅ OUI | Toujours autorisé |
| 3+ | Super admin | ✅ OUI | Toujours autorisé |
### Détail du contrôle pour les membres (fk_role = 1) :
```sql
-- L'API vérifie :
SELECT chk_user_delete_pass
FROM entites
WHERE id = {user.fk_entite}
-- Si chk_user_delete_pass = 0 → Erreur 403
-- Si chk_user_delete_pass = 1 → Continue
```
## 🔄 Flux de vérification
```mermaid
graph TD
A[DELETE /passages/{id}] --> B{Utilisateur authentifié ?}
B -->|Non| C[Erreur 401]
B -->|Oui| D{Récupérer fk_role}
D --> E{fk_role = 1 ?}
E -->|Non| F[Autorisé - Admin]
E -->|Oui| G{Vérifier chk_user_delete_pass}
G -->|= 0| H[Erreur 403 - Non autorisé]
G -->|= 1| F
F --> I{Passage existe ?}
I -->|Non| J[Erreur 404]
I -->|Oui| K{Passage appartient à l'entité ?}
K -->|Non| L[Erreur 404]
K -->|Oui| M[Soft delete : chk_active = 0]
M --> N[Succès 200]
```
## 📝 Réponses
### ✅ Succès (200)
```json
{
"status": "success",
"message": "Passage supprimé avec succès"
}
```
### ❌ Erreur 401 - Non authentifié
```json
{
"status": "error",
"message": "Vous devez être connecté pour effectuer cette action"
}
```
### ❌ Erreur 403 - Permission refusée (membre sans autorisation)
```json
{
"status": "error",
"message": "Vous n'avez pas l'autorisation de supprimer des passages"
}
```
### ❌ Erreur 404 - Passage non trouvé
```json
{
"status": "error",
"message": "Passage non trouvé"
}
```
## 📊 Logging
L'API enregistre :
### En cas de tentative non autorisée :
```php
LogService::log('Tentative de suppression de passage non autorisée', [
'level' => 'warning',
'userId' => $userId,
'userRole' => $userRole,
'entiteId' => $entiteId,
'passageId' => $passageId,
'chk_user_delete_pass' => 0
]);
```
### En cas de succès :
```php
LogService::log('Suppression d\'un passage', [
'level' => 'info',
'userId' => $userId,
'passageId' => $passageId
]);
```
## 🎯 Exemple d'utilisation
### Requête
```bash
curl -X DELETE https://api.geosector.fr/api/passages/19500576 \
-H "Authorization: Bearer {session_token}" \
-H "Content-Type: application/json"
```
### Scénarios
#### Scénario 1 : Membre avec permission ✅
- Utilisateur : fk_role = 1
- Entité : chk_user_delete_pass = 1
- **Résultat** : Suppression autorisée
#### Scénario 2 : Membre sans permission ❌
- Utilisateur : fk_role = 1
- Entité : chk_user_delete_pass = 0
- **Résultat** : Erreur 403
#### Scénario 3 : Admin amicale ✅
- Utilisateur : fk_role = 2
- **Résultat** : Suppression autorisée (pas de vérification chk_user_delete_pass)
## ⚠️ Notes importantes
1. **Soft delete** : Le passage n'est pas supprimé physiquement, seulement `chk_active = 0`
2. **Traçabilité** : `updated_at` et `fk_user_modif` sont mis à jour
3. **Contrôle entité** : Un utilisateur ne peut supprimer que les passages de son entité
4. **Log warning** : Toute tentative non autorisée est loggée en niveau WARNING
## 🔧 Configuration côté amicale
Pour autoriser les membres à supprimer des passages :
```sql
UPDATE entites
SET chk_user_delete_pass = 1
WHERE id = {entite_id};
```
Cette modification ne peut être faite que par un administrateur (fk_role > 1) via l'endpoint :
```
PUT /api/entites/{id}
{
"chk_user_delete_pass": 1
}
```
---
**Version API** : 3.1.4
**Date** : 20/08/2025

90
api/docs/INSTALL_FPDF.md Normal file
View File

@@ -0,0 +1,90 @@
# Installation de FPDF pour la génération des reçus PDF avec logo
## Installation via Composer (RECOMMANDÉ)
Sur chaque serveur (DEV, REC, PROD), exécuter :
```bash
cd /var/www/geosector/api
composer require setasign/fpdf
```
Ou si composer.json est déjà mis à jour :
```bash
cd /var/www/geosector/api
composer update
```
## Fichiers à déployer
1. **Nouveaux fichiers** :
- `/src/Services/ReceiptPDFGenerator.php` - Nouvelle classe de génération PDF avec FPDF
- `/docs/_logo_recu.png` - Logo par défaut (casque de pompier)
2. **Fichiers modifiés** :
- `/src/Services/ReceiptService.php` - Utilise maintenant ReceiptPDFGenerator
- `/composer.json` - Ajout de la dépendance FPDF
## Vérification
Après installation, tester la génération d'un reçu :
```bash
# Vérifier que FPDF est installé
ls -la vendor/setasign/fpdf/
# Tester la génération d'un PDF
php -r "
require 'vendor/autoload.php';
\$pdf = new FPDF();
\$pdf->AddPage();
\$pdf->SetFont('Arial','B',16);
\$pdf->Cell(40,10,'Test FPDF OK');
echo 'FPDF fonctionne' . PHP_EOL;
"
```
## Fonctionnalités du nouveau générateur
**Support des vrais logos PNG/JPG**
**Logo par défaut** si l'entité n'a pas de logo
**Taille du logo** : 40x40mm
**Mise en page professionnelle** avec cadre pour le montant
**Conversion automatique** des caractères UTF-8
**PDF léger** (~20-30KB avec logo)
## Structure du reçu généré
1. **En-tête** :
- Logo (40x40mm) à gauche
- Nom et ville de l'entité à droite du logo
2. **Titre** :
- "REÇU FISCAL DE DON"
- Numéro du reçu
- Article 200 CGI
3. **Corps** :
- Informations du donateur
- Montant en gros dans un cadre grisé
- Date du don
- Mode de règlement et campagne
4. **Pied de page** :
- Mentions légales (réduction 66%)
- Date et signature
## Résolution de problèmes
Si erreur "Class 'FPDF' not found" :
```bash
composer dump-autoload
```
Si problème avec le logo :
- Vérifier que `/docs/_logo_recu.png` existe
- Vérifier les permissions : `chmod 644 docs/_logo_recu.png`
Si caractères accentués mal affichés :
- FPDF utilise ISO-8859-1, la conversion est automatique dans ReceiptPDFGenerator

237
api/docs/PREPA_PROD.md Normal file
View File

@@ -0,0 +1,237 @@
# PRÉPARATION PRODUCTION - Process Email Queue + Permissions Suppression Passages
## 📅 Date de mise en production prévue : _____________
## 🎯 Objectif
1. Mettre en place le système de traitement automatique de la queue d'emails pour l'envoi des reçus fiscaux de dons.
2. Ajouter le champ de permission pour autoriser les membres à supprimer des passages.
## ✅ Prérequis
- [ ] Backup de la base de données effectué
- [ ] Accès SSH au serveur PROD
- [ ] Accès à la base de données PROD
- [ ] Droits pour éditer le crontab
## 📝 Fichiers à déployer
Les fichiers suivants doivent être présents sur le serveur PROD :
- `/scripts/cron/process_email_queue.php`
- `/scripts/cron/process_email_queue_with_daily_log.sh`
- `/scripts/cron/test_email_queue.php`
- `/src/Services/ReceiptPDFGenerator.php` (nouveau)
- `/src/Services/ReceiptService.php` (mis à jour)
- `/src/Core/MonitoredDatabase.php` (mis à jour)
- `/src/Controllers/EntiteController.php` (mis à jour)
- `/src/Controllers/LoginController.php` (mis à jour)
- `/scripts/sql/add_chk_user_delete_pass.sql` (nouveau)
---
## 🔧 ÉTAPES DE MISE EN PRODUCTION
### 1⃣ Mise à jour de la base de données
Se connecter à la base de données PROD et exécuter :
```sql
-- Vérifier d'abord la structure actuelle de email_queue
DESCRIBE email_queue;
-- Ajouter les champs manquants pour email_queue si nécessaire
ALTER TABLE `email_queue`
ADD COLUMN IF NOT EXISTS `sent_at` TIMESTAMP NULL DEFAULT NULL
COMMENT 'Date/heure d\'envoi effectif de l\'email'
AFTER `status`;
ALTER TABLE `email_queue`
ADD COLUMN IF NOT EXISTS `error_message` TEXT NULL DEFAULT NULL
COMMENT 'Message d\'erreur en cas d\'échec'
AFTER `attempts`;
-- Ajouter les index pour optimiser les performances
ALTER TABLE `email_queue`
ADD INDEX IF NOT EXISTS `idx_status_attempts` (`status`, `attempts`);
ALTER TABLE `email_queue`
ADD INDEX IF NOT EXISTS `idx_sent_at` (`sent_at`);
-- Vérifier les modifications email_queue
DESCRIBE email_queue;
-- ⚠️ IMPORTANT : Ajouter le nouveau champ chk_user_delete_pass dans entites
source /var/www/geosector/api/scripts/sql/add_chk_user_delete_pass.sql;
```
### 2⃣ Test du script avant mise en production
```bash
# Se connecter au serveur PROD
ssh user@prod-server
# Aller dans le répertoire de l'API
cd /var/www/geosector/api
# Rendre les scripts exécutables
chmod +x scripts/cron/process_email_queue.php
chmod +x scripts/cron/test_email_queue.php
# Tester l'état de la queue (lecture seule)
php scripts/cron/test_email_queue.php
# Si tout est OK, faire un test d'envoi sur 1 email
# (modifier temporairement BATCH_SIZE à 1 dans le script si nécessaire)
php scripts/cron/process_email_queue.php
```
### 3⃣ Configuration du CRON avec logs journaliers
```bash
# Rendre le script wrapper exécutable
chmod +x /var/www/geosector/api/scripts/cron/process_email_queue_with_daily_log.sh
# Éditer le crontab
crontab -e
# Ajouter cette ligne pour exécution toutes les 5 minutes avec logs journaliers
*/5 * * * * /var/www/geosector/api/scripts/cron/process_email_queue_with_daily_log.sh
# Sauvegarder et quitter (:wq sous vi/vim)
# Vérifier que le cron est bien enregistré
crontab -l | grep email_queue
# Vérifier que le service cron est actif
systemctl status cron
```
**Note** : Les logs seront créés automatiquement dans `/var/www/geosector/api/logs/` avec le format : `email_queue_20250820.log`, `email_queue_20250821.log`, etc. Les logs de plus de 30 jours sont supprimés automatiquement.
### 4⃣ Surveillance post-déploiement
Pendant les premières heures après la mise en production :
```bash
# Surveiller les logs en temps réel (fichier du jour)
tail -f /var/www/geosector/api/logs/email_queue_$(date +%Y%m%d).log
# Vérifier le statut de la queue
php scripts/cron/test_email_queue.php
# Compter les emails traités
mysql -u geo_app_user_prod -p geo_app -e "
SELECT status, COUNT(*) as count
FROM email_queue
WHERE DATE(created_at) = CURDATE()
GROUP BY status;"
# Vérifier les erreurs éventuelles
mysql -u geo_app_user_prod -p geo_app -e "
SELECT id, to_email, subject, attempts, error_message
FROM email_queue
WHERE status='failed'
ORDER BY created_at DESC
LIMIT 10;"
```
---
## 🚨 ROLLBACK (si nécessaire)
En cas de problème, voici comment revenir en arrière :
```bash
# 1. Stopper le cron
crontab -e
# Commenter la ligne du process_email_queue
# 2. Marquer les emails en attente pour traitement manuel
mysql -u geo_app_user_prod -p geo_app -e "
UPDATE email_queue
SET status='pending', attempts=0
WHERE status='failed' AND DATE(created_at) = CURDATE();"
# 3. Informer l'équipe pour traitement manuel si nécessaire
```
---
## 📊 VALIDATION POST-DÉPLOIEMENT
### Critères de succès :
- [ ] Aucune erreur dans les logs
- [ ] Les emails sont envoyés dans les 5 minutes
- [ ] Les reçus PDF sont correctement attachés
- [ ] Le champ `date_sent_recu` est mis à jour dans `ope_pass`
- [ ] Pas d'accumulation d'emails en status 'pending'
### Commandes de vérification :
```bash
# Statistiques générales
mysql -u geo_app_user_prod -p geo_app -e "
SELECT
status,
COUNT(*) as count,
MIN(created_at) as oldest,
MAX(created_at) as newest
FROM email_queue
GROUP BY status;"
# Vérifier les passages avec reçus envoyés aujourd'hui
mysql -u geo_app_user_prod -p geo_app -e "
SELECT COUNT(*) as recus_envoyes_aujourdhui
FROM ope_pass
WHERE DATE(date_sent_recu) = CURDATE();"
# Performance du cron (dernières exécutions du jour)
tail -20 /var/www/geosector/api/logs/email_queue_$(date +%Y%m%d).log | grep "Traitement terminé"
```
---
## 📞 CONTACTS EN CAS DE PROBLÈME
- **Responsable technique** : _____________
- **DBA** : _____________
- **Support O2Switch** : support@o2switch.fr
---
## 📋 NOTES IMPORTANTES
1. **Limite d'envoi** : 1500 emails/heure max (limite O2Switch)
2. **Batch size** : 50 emails par exécution (toutes les 5 min = 600/heure max)
3. **Lock file** : `/tmp/process_email_queue.lock` empêche l'exécution simultanée
4. **Nettoyage auto** : Les emails envoyés > 30 jours sont supprimés automatiquement
## 🔒 SÉCURITÉ
- Les mots de passe SMTP ne sont jamais loggués
- Les emails en erreur conservent le message d'erreur pour diagnostic
- Le PDF est envoyé en pièce jointe encodée en base64
---
## ✅ CHECKLIST FINALE
### Email Queue :
- [ ] Table email_queue mise à jour (sent_at, error_message, index)
- [ ] Scripts cron testés avec succès
- [ ] Cron configuré et actif
- [ ] Logs accessibles et fonctionnels
- [ ] Premier batch d'emails envoyé avec succès
### Permissions Suppression Passages :
- [ ] Champ chk_user_delete_pass ajouté dans la table entites
- [ ] EntiteController.php mis à jour pour gérer le nouveau champ
- [ ] LoginController.php mis à jour pour retourner le champ dans amicale
- [ ] Test de modification de permissions via l'interface admin
### Général :
- [ ] Documentation mise à jour
- [ ] Équipe informée de la mise en production
---
**Date de mise en production** : _______________
**Validé par** : _______________
**Signature** : _______________

View File

@@ -0,0 +1,149 @@
# Instructions de mise en place du CRON pour la queue d'emails
## Problème résolu
Les emails de reçus étaient insérés dans la table `email_queue` mais n'étaient jamais envoyés car il manquait le script de traitement.
## Fichiers créés
1. `/scripts/cron/process_email_queue.php` - Script principal de traitement
2. `/scripts/cron/test_email_queue.php` - Script de test/diagnostic
3. `/scripts/sql/add_email_queue_fields.sql` - Migration SQL pour les champs manquants
## Installation sur les serveurs (DVA, REC, PROD)
### 1. Appliquer la migration SQL
Se connecter à la base de données et exécuter :
```bash
mysql -u [user] -p [database] < /path/to/api/scripts/sql/add_email_queue_fields.sql
```
Ou directement dans MySQL :
```sql
-- Ajouter les champs manquants
ALTER TABLE `email_queue`
ADD COLUMN IF NOT EXISTS `sent_at` TIMESTAMP NULL DEFAULT NULL AFTER `status`;
ALTER TABLE `email_queue`
ADD COLUMN IF NOT EXISTS `error_message` TEXT NULL DEFAULT NULL AFTER `attempts`;
-- Ajouter les index pour les performances
ALTER TABLE `email_queue`
ADD INDEX IF NOT EXISTS `idx_status_attempts` (`status`, `attempts`);
ALTER TABLE `email_queue`
ADD INDEX IF NOT EXISTS `idx_sent_at` (`sent_at`);
```
### 2. Tester le script
Avant de mettre en place le cron, tester que tout fonctionne :
```bash
# Vérifier l'état de la queue
php /path/to/api/scripts/cron/test_email_queue.php
# Tester l'envoi (traite jusqu'à 50 emails)
php /path/to/api/scripts/cron/process_email_queue.php
```
### 3. Configurer le CRON
Ajouter la ligne suivante dans le crontab du serveur :
```bash
# Éditer le crontab
crontab -e
# Ajouter cette ligne (exécution toutes les 5 minutes)
*/5 * * * * /usr/bin/php /path/to/api/scripts/cron/process_email_queue.php >> /var/log/email_queue.log 2>&1
```
**Options de fréquence :**
- `*/5 * * * *` - Toutes les 5 minutes (recommandé)
- `*/10 * * * *` - Toutes les 10 minutes
- `*/2 * * * *` - Toutes les 2 minutes (si volume important)
### 4. Monitoring
Le script génère des logs via `LogService`. Vérifier les logs dans :
- `/path/to/api/logs/` (selon la configuration)
Points à surveiller :
- Nombre d'emails traités
- Emails en échec après 3 tentatives
- Erreurs de connexion SMTP
### 5. Configuration SMTP
Vérifier que la configuration SMTP est correcte dans `AppConfig` :
- Host SMTP
- Port (587 pour TLS, 465 pour SSL)
- Username/Password
- Encryption (tls ou ssl)
- From Email/Name
## Fonctionnement du script
### Caractéristiques
- **Batch size** : 50 emails par exécution
- **Max tentatives** : 3 essais par email
- **Lock file** : Empêche l'exécution simultanée
- **Nettoyage** : Supprime les emails envoyés > 30 jours
- **Pause** : 0.5s entre chaque email (anti-spam)
### Workflow
1. Récupère les emails avec `status = 'pending'` et `attempts < 3`
2. Pour chaque email :
- Incrémente le compteur de tentatives
- Envoie via PHPMailer avec la config SMTP
- Si succès : `status = 'sent'` + mise à jour du passage
- Si échec : réessai à la prochaine exécution
- Après 3 échecs : `status = 'failed'`
### Tables mises à jour
- `email_queue` : status, attempts, sent_at, error_message
- `ope_pass` : date_sent_recu, chk_email_sent
## Commandes utiles
```bash
# Voir les emails en attente
mysql -e "SELECT COUNT(*) FROM email_queue WHERE status='pending'" [database]
# Voir les emails échoués
mysql -e "SELECT * FROM email_queue WHERE status='failed' ORDER BY created_at DESC LIMIT 10" [database]
# Réinitialiser un email échoué pour réessai
mysql -e "UPDATE email_queue SET status='pending', attempts=0 WHERE id=[ID]" [database]
# Voir les logs du cron
tail -f /var/log/email_queue.log
# Vérifier que le cron est actif
crontab -l | grep process_email_queue
```
## Troubleshooting
### Le cron ne s'exécute pas
- Vérifier les permissions : `chmod +x process_email_queue.php`
- Vérifier le chemin PHP : `which php`
- Vérifier les logs système : `/var/log/syslog` ou `/var/log/cron`
### Emails en échec
- Vérifier la config SMTP avec `test_email_queue.php`
- Vérifier les logs pour les messages d'erreur
- Tester la connexion SMTP : `telnet [smtp_host] [port]`
### Lock bloqué
Si le message "Le processus est déjà en cours" persiste :
```bash
rm /tmp/process_email_queue.lock
```
## Contact support
En cas de problème, vérifier :
1. Les logs de l'application
2. La table `email_queue` pour les messages d'erreur
3. La configuration SMTP dans AppConfig

View File

@@ -0,0 +1,155 @@
# 📋 RÉCAPITULATIF - Migration Arborescence Uploads
## ✅ Modifications effectuées
### 1. **EntiteController.php** (ligne 736)
```php
// Avant : "/entites/{$entiteId}/logo"
// Après : "/{$entiteId}/logo"
```
✅ Les logos sont maintenant stockés dans : `uploads/{entite_id}/logo/`
### 2. **ReceiptService.php** (ligne 95)
```php
// Avant : "/entites/{$entiteId}/recus/{$operationId}"
// Après : "/{$entiteId}/recus/{$operationId}"
```
✅ Les reçus PDF sont maintenant stockés dans : `uploads/{entite_id}/recus/{operation_id}/`
### 3. **ExportService.php** (lignes 40 et 141)
```php
// Avant Excel : "/{$entiteId}/operations/{$operationId}/exports/excel"
// Après Excel : "/{$entiteId}/operations/{$operationId}"
// Avant JSON : "/{$entiteId}/operations/{$operationId}/exports/json"
// Après JSON : "/{$entiteId}/operations/{$operationId}"
```
✅ Les exports sont maintenant stockés directement dans : `uploads/{entite_id}/operations/{operation_id}/`
## 📂 Nouvelle structure complète
```
uploads/
└── {entite_id}/ # Ex: 5, 1230, etc.
├── logo/ # Logo de l'entité
│ └── logo_{entite_id}_{timestamp}.{jpg|png}
├── operations/ # Exports d'opérations
│ └── {operation_id}/ # Ex: 1525, 3124
│ ├── geosector-export-{operation_id}-{timestamp}.xlsx
│ └── backup-{operation_id}-{timestamp}.json.enc
└── recus/ # Reçus fiscaux
└── {operation_id}/ # Ex: 3124
└── recu_{passage_id}.pdf
```
## 🔧 Script de migration
Un script a été créé pour migrer les fichiers existants :
**Fichier :** `/scripts/migrate_uploads_structure.php`
**Usage :**
```bash
# Mode simulation (voir ce qui sera fait sans modifier)
php scripts/migrate_uploads_structure.php --dry-run
# Mode réel (effectue la migration)
php scripts/migrate_uploads_structure.php
```
**Ce que fait le script :**
1. Déplace tout le contenu de `uploads/entites/*` vers `uploads/*`
2. Fusionne les dossiers si nécessaire
3. Simplifie la structure des exports (supprime `/documents/exports/excel/`)
4. Applique les bonnes permissions (nginx:nobody 775/664)
5. Crée un log détaillé dans `/logs/migration_uploads_YYYYMMDD_HHMMSS.log`
## 🚀 Procédure de déploiement
### Sur DEV (déjà fait)
✅ Code modifié
✅ Script de migration créé
### Sur REC
```bash
# 1. Déployer le nouveau code
./livre-api.sh rec
# 2. Faire un backup des uploads actuels
cd /var/www/geosector/api
tar -czf uploads_backup_$(date +%Y%m%d).tar.gz uploads/
# 3. Tester en mode dry-run
php scripts/migrate_uploads_structure.php --dry-run
# 4. Si OK, lancer la migration
php scripts/migrate_uploads_structure.php
# 5. Vérifier la nouvelle structure
ls -la uploads/
ls -la uploads/*/
```
### Sur PROD
Même procédure que REC après validation
## ⚠️ Points d'attention
1. **Backup obligatoire** avant migration
2. **Vérifier l'espace disque** disponible
3. **Tester d'abord en dry-run**
4. **Surveiller les logs** après migration
5. **Tester** upload logo, génération reçu, et export Excel
## 📊 Gains obtenus
| Aspect | Avant | Après |
|--------|-------|-------|
| **Profondeur max** | 8 niveaux | 4 niveaux |
| **Complexité** | 2 structures parallèles | 1 structure unique |
| **Clarté** | Confus (entites + racine) | Simple et logique |
| **Navigation** | Difficile | Intuitive |
## 🔍 Vérification post-migration
Après la migration, vérifier :
```bash
# Structure attendue pour l'entité 5
tree uploads/5/
# Devrait afficher :
# uploads/5/
# ├── logo/
# │ └── logo_5_*.png
# ├── operations/
# │ ├── 1525/
# │ │ └── *.xlsx
# │ └── 3124/
# │ └── *.xlsx
# └── recus/
# └── 3124/
# └── recu_*.pdf
# Vérifier les permissions
ls -la uploads/*/
# Devrait montrer : nginx:nobody avec 775 pour dossiers, 664 pour fichiers
```
## ✅ Checklist finale
- [ ] Code modifié et testé en DEV
- [ ] Script de migration créé
- [ ] Documentation mise à jour
- [ ] Backup effectué sur REC
- [ ] Migration testée en dry-run sur REC
- [ ] Migration exécutée sur REC
- [ ] Tests fonctionnels sur REC
- [ ] Backup effectué sur PROD
- [ ] Migration exécutée sur PROD
- [ ] Tests fonctionnels sur PROD
---
**Date de création :** 20/08/2025
**Auteur :** Assistant Claude
**Status :** Prêt pour déploiement

View File

@@ -0,0 +1,93 @@
# Réorganisation de l'arborescence des uploads
## 📅 Date : 20/08/2025
## 🎯 Objectif
Uniformiser et simplifier l'arborescence des fichiers uploads pour une meilleure organisation et maintenance.
## 📂 Arborescence actuelle (PROBLÈME)
```
uploads/
├── entites/
│ └── 5/
│ ├── logo/
│ ├── operations/
│ │ └── 1525/
│ │ └── documents/
│ │ └── exports/
│ │ └── excel/
│ │ └── geosector-export-*.xlsx
│ └── recus/
│ └── 3124/
│ └── recu_*.pdf
└── 5/
└── operations/
├── 1525/
└── 2021/
```
**Problèmes identifiés :**
- Duplication des structures (dossier `5` à la racine ET dans `entites/`)
- Chemins trop profonds pour les exports Excel (6 niveaux)
- Incohérence dans les chemins
## ✅ Nouvelle arborescence (SOLUTION)
```
uploads/
└── {entite_id}/ # Un seul dossier par entité à la racine
├── logo/ # Logo de l'entité
│ └── logo_*.{jpg,png}
├── operations/ # Exports par opération
│ └── {operation_id}/
│ └── *.xlsx # Exports Excel directement ici
└── recus/ # Reçus par opération
└── {operation_id}/
└── recu_*.pdf
```
## 📝 Fichiers à modifier
### 1. EntiteController.php (Upload logo)
**Actuel :** `/entites/{$entiteId}/logo`
**Nouveau :** `/{$entiteId}/logo`
### 2. ReceiptService.php (Stockage reçus PDF)
**Actuel :** `/entites/{$entiteId}/recus/{$operationId}`
**Nouveau :** `/{$entiteId}/recus/{$operationId}`
### 3. ExportService.php (Export Excel)
**Actuel :** `/{$entiteId}/operations/{$operationId}/exports/excel`
**Nouveau :** `/{$entiteId}/operations/{$operationId}`
### 4. ExportService.php (Export JSON)
**Actuel :** `/{$entiteId}/operations/{$operationId}/exports/json`
**Nouveau :** `/{$entiteId}/operations/{$operationId}` (ou supprimer si non utilisé)
## 🔄 Plan de migration
### Étape 1 : Modifier le code
1. Mettre à jour tous les chemins dans les contrôleurs et services
2. Tester en environnement DEV
### Étape 2 : Script de migration des fichiers existants
Créer un script PHP pour :
1. Lister tous les fichiers existants
2. Les déplacer vers la nouvelle structure
3. Supprimer les anciens dossiers vides
### Étape 3 : Déploiement
1. Exécuter le script de migration sur REC
2. Vérifier le bon fonctionnement
3. Exécuter sur PROD
## 🚀 Avantages de la nouvelle structure
- **Plus simple** : Chemins plus courts et plus logiques
- **Plus cohérent** : Une seule structure pour toutes les entités
- **Plus maintenable** : Facile de naviguer et comprendre
- **Performance** : Moins de niveaux de dossiers à parcourir
## ⚠️ Points d'attention
- Vérifier les permissions (nginx:nobody 775/664)
- S'assurer que les anciens fichiers sont bien migrés
- Mettre à jour la documentation
- Informer l'équipe du changement

View File

@@ -0,0 +1,19 @@
# Configuration logrotate pour email_queue.log
# À placer dans /etc/logrotate.d/geosector-email-queue
/var/www/geosector/api/logs/email_queue.log {
daily # Rotation journalière
rotate 30 # Garder 30 jours d'historique
compress # Compresser les anciens logs
delaycompress # Compresser le jour suivant
missingok # Pas d'erreur si le fichier n'existe pas
notifempty # Ne pas tourner si vide
create 664 www-data www-data # Créer nouveau fichier avec permissions
dateext # Ajouter la date au nom du fichier
dateformat -%Y%m%d # Format de date YYYYMMDD
maxsize 100M # Rotation si dépasse 100MB même avant la fin du jour
postrotate
# Optionnel : envoyer un signal au process si nécessaire
# /usr/bin/killall -SIGUSR1 php 2>/dev/null || true
endscript
}

View File

@@ -0,0 +1,93 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>
endobj
4 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>
endobj
5 0 obj
<< /Length 599 >>
stream
BT
/F1 14 Tf
217 792 Td
(AMICALE TEST DEV PIERRE) Tj
ET
BT
/F1 11 Tf
281 770 Td
(RENNES) Tj
ET
BT
/F1 14 Tf
213.5 726 Td
(RECU FISCAL N 19500582) Tj
ET
BT
/F1 9 Tf
263.75 704 Td
(Article 200 CGI) Tj
ET
BT
/F1 12 Tf
50 657 Td
(Dugues) Tj
ET
BT
/F1 11 Tf
50 637 Td
(8 le Petit Monthelon Acigne) Tj
ET
BT
/F1 16 Tf
257.5 598 Td
(8,00 euros) Tj
ET
BT
/F1 12 Tf
267.5 559 Td
(20/08/2025) Tj
ET
BT
/F1 10 Tf
277.5 529 Td
(OPE 2025) Tj
ET
BT
/F1 9 Tf
198.5 476 Td
(Don ouvrant droit a reduction d'impot de 66%) Tj
ET
BT
/F1 10 Tf
50 419 Td
(Le 20/08/2025) Tj
ET
BT
/F1 10 Tf
50 401 Td
(Le President) Tj
ET
endstream
endobj
xref
0 6
0000000000 65535 f
0000000019 00000 n
0000000068 00000 n
0000000125 00000 n
0000000251 00000 n
0000000353 00000 n
trailer
<< /Size 6 /Root 1 0 R >>
startxref
1003
%%EOF

View File

@@ -0,0 +1,75 @@
%PDF-1.3
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>
endobj
4 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj
5 0 obj
<< /Length 767 >>
stream
BT
/F1 12 Tf
50 750 Td
(AMICALE TEST DEV PIERRE) Tj
0 -20 Td
(17 place hoche 35000 RENNES) Tj
/F1 16 Tf
0 -40 Td
(RECU DE DON N° 19500586) Tj
/F1 10 Tf
0 -15 Td
(Article 200 du Code General des Impots) Tj
/F1 12 Tf
0 -45 Td
(DONATEUR) Tj
/F1 11 Tf
0 -20 Td
(Nom : M. Hermann) Tj
0 -15 Td
(Adresse : 12 le Petit Monthelon Acigne) Tj
0 -15 Td
(Email : pierre.vaissaire@gmail.com) Tj
0 -30 Td
/F1 12 Tf
(DETAILS DU DON) Tj
/F1 11 Tf
0 -20 Td
(Date : 19/08/2025) Tj
0 -15 Td
(Montant : 12,00 EUR) Tj
0 -15 Td
(Mode de reglement : Espece) Tj
0 -15 Td
(Campagne : OPE 2025) Tj
/F1 9 Tf
0 -40 Td
(Reduction d'impot egale a 66% du montant verse dans la limite de 20% du revenu imposable) Tj
/F1 11 Tf
0 -30 Td
(Fait a RENNES, le 19/08/2025) Tj
0 -20 Td
(Le President) Tj
ET
endstream
endobj
xref
0 6
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000241 00000 n
0000000311 00000 n
trailer
<< /Size 6 /Root 1 0 R >>
startxref
1129
%%EOF