- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1926 lines
56 KiB
Markdown
1926 lines
56 KiB
Markdown
# Documentation des Scripts de Migration GeoSector
|
||
|
||
## 📋 Vue d'ensemble
|
||
|
||
Ce dossier contient l'ensemble des scripts PHP permettant de migrer les données de l'ancienne base **geosector** (MySQL distante) vers la nouvelle base **pra_geo** (MariaDB sur maria4/IN4).
|
||
|
||
### Statistiques
|
||
- **Nombre total de scripts** : 21 fichiers
|
||
- **Base source** : MySQL geosector (sauvegardée depuis PM7)
|
||
- **Base cible** : MariaDB 11.4 pra_geo (maria4 sur IN4)
|
||
- **Méthode** : Restauration backup PM7 → Migration sur maria4
|
||
- **Ordre d'exécution** : Défini dans `migrate.php`
|
||
|
||
---
|
||
|
||
## 🚀 Guide Rapide de Migration depuis Backup PM7
|
||
|
||
### Architecture de migration
|
||
|
||
```
|
||
PM7 (11.1.2.17)
|
||
└─ Backup nocturne chiffré : geosector_YYYYMMDD.sql.tar.gz.enc
|
||
↓
|
||
Déchiffrement avec decpm7.sh
|
||
↓
|
||
Transfert SCP vers IN4:/var/back/
|
||
↓
|
||
Push vers maria4:/var/back/
|
||
↓
|
||
Décompression et import dans maria4
|
||
↓
|
||
Base geosector_YYYYMMDD créée dans maria4
|
||
↓
|
||
Migration vers pra_geo (même serveur maria4)
|
||
```
|
||
|
||
### Étape 1️⃣ : Récupération et déchiffrement du backup depuis PM7
|
||
|
||
**Sur le serveur PM7 (11.1.2.17)** :
|
||
|
||
```bash
|
||
# Se connecter à PM7
|
||
ssh root@11.1.2.17
|
||
|
||
# Aller dans le dossier des backups
|
||
cd /var/back/
|
||
|
||
# Lister les backups disponibles
|
||
ls -lh geosector_*.sql.tar.gz.enc | tail -5
|
||
|
||
# Identifier le dernier backup (exemple : geosector_20251007.sql.tar.gz.enc)
|
||
BACKUP_DATE=$(date +%Y%m%d)
|
||
BACKUP_FILE="geosector_${BACKUP_DATE}.sql.tar.gz.enc"
|
||
|
||
# Déchiffrer le backup avec le script decpm7.sh
|
||
./decpm7.sh ${BACKUP_FILE}
|
||
|
||
# Résultat : fichier geosector_20251007.sql.tar.gz
|
||
```
|
||
|
||
### Étape 2️⃣ : Transfert du backup vers IN4
|
||
|
||
**Depuis PM7 ou depuis votre poste local** :
|
||
|
||
```bash
|
||
# Définir les variables
|
||
BACKUP_DATE=$(date +%Y%m%d)
|
||
BACKUP_FILE="geosector_${BACKUP_DATE}.sql.tar.gz"
|
||
|
||
# Transférer depuis PM7 vers IN4
|
||
scp root@11.1.2.17:/var/back/${BACKUP_FILE} root@51.159.7.190:/var/back/
|
||
|
||
# Vérifier la présence du fichier sur IN4
|
||
ssh root@51.159.7.190 "ls -lh /var/back/${BACKUP_FILE}"
|
||
```
|
||
|
||
### Étape 3️⃣ : Push du backup vers le container maria4
|
||
|
||
**Sur le serveur IN4 (51.159.7.190)** :
|
||
|
||
```bash
|
||
# Se connecter à IN4
|
||
ssh root@51.159.7.190
|
||
|
||
# Définir la variable
|
||
BACKUP_DATE=$(date +%Y%m%d)
|
||
BACKUP_FILE="geosector_${BACKUP_DATE}.sql.tar.gz"
|
||
|
||
# Pousser le fichier vers le container maria4
|
||
incus file push /var/back/${BACKUP_FILE} maria4/var/back/
|
||
|
||
# Vérifier le fichier dans le container
|
||
incus exec maria4 -- ls -lh /var/back/${BACKUP_FILE}
|
||
```
|
||
|
||
### Étape 4️⃣ : Décompression et import dans maria4
|
||
|
||
**Dans le container maria4** :
|
||
|
||
```bash
|
||
# Se connecter au container maria4
|
||
incus exec maria4 bash
|
||
|
||
# Définir les variables
|
||
BACKUP_DATE=$(date +%Y%m%d)
|
||
BACKUP_FILE="geosector_${BACKUP_DATE}.sql.tar.gz"
|
||
SQL_FILE="geosector_${BACKUP_DATE}.sql"
|
||
|
||
# Décompresser l'archive
|
||
cd /var/back
|
||
tar -xzf ${BACKUP_FILE}
|
||
|
||
# Vérifier la présence du fichier SQL
|
||
ls -lh ${SQL_FILE}
|
||
|
||
# Importer le SQL dans MariaDB
|
||
# Note : Le fichier SQL contient déjà CREATE DATABASE et USE geosector_YYYYMMDD
|
||
mariadb -u root -p'MyAlpLocal,90b' < ${SQL_FILE}
|
||
|
||
# Vérifier la création de la base
|
||
mariadb -u root -p'MyAlpLocal,90b' -e "SHOW DATABASES LIKE 'geosector_%';"
|
||
|
||
# Vérifier le nombre de tables
|
||
mariadb -u root -p'MyAlpLocal,90b' geosector_${BACKUP_DATE} -e "SHOW TABLES;"
|
||
|
||
# Vérifier quelques comptages
|
||
mariadb -u root -p'MyAlpLocal,90b' geosector_${BACKUP_DATE} -e "
|
||
SELECT 'users' as table_name, COUNT(*) as count FROM users
|
||
UNION ALL SELECT 'operations', COUNT(*) FROM operations
|
||
UNION ALL SELECT 'ope_pass', COUNT(*) FROM ope_pass
|
||
UNION ALL SELECT 'entites', COUNT(*) FROM users_entites;"
|
||
|
||
# Sortir du container
|
||
exit
|
||
```
|
||
|
||
### Étape 5️⃣ : Migration des données vers pra_geo
|
||
|
||
**Important** : Le script PHP s'exécute sur le container **pra-geo** (pas maria4), car :
|
||
- ✅ PHP 8.3 est disponible sur pra-geo
|
||
- ✅ L'API GeoSector avec `ApiService::encryptData()` est présente
|
||
- ✅ pra-geo se connecte à maria4 via l'IP 13.23.33.4
|
||
|
||
**Option A : Migration globale (toutes les amicales)**
|
||
|
||
```bash
|
||
# Sur IN4, exécuter le script depuis le container pra-geo
|
||
incus exec pra-geo bash
|
||
|
||
# Aller dans le dossier de l'API
|
||
cd /var/www/geosector/api
|
||
|
||
# Lister les bases disponibles dans maria4 pour vérifier
|
||
# (optionnel - pour voir les backups restaurés)
|
||
echo "SHOW DATABASES LIKE 'geosector_%';" | \
|
||
mysql -h 13.23.33.4 -u root -p'MyAlpLocal,90b'
|
||
|
||
# Lancer la migration complète
|
||
php scripts/php/migrate_from_backup.php \
|
||
--source-db=geosector_20251007 \
|
||
--target-db=pra_geo \
|
||
--mode=global \
|
||
--log=/var/www/geosector/api/logs/migration_global_20251007.log
|
||
|
||
# Suivre la progression dans les logs
|
||
tail -f /var/www/geosector/api/logs/migration_global_20251007.log
|
||
```
|
||
|
||
**Option B : Migration par amicale (recommandé)**
|
||
|
||
```bash
|
||
# Sur IN4, dans le container pra-geo
|
||
incus exec pra-geo bash
|
||
|
||
# Aller dans le dossier de l'API
|
||
cd /var/www/geosector/api
|
||
|
||
# Lister les amicales disponibles dans la base source
|
||
mysql -h 13.23.33.4 -u root -p'MyAlpLocal,90b' geosector_20251007 -e "
|
||
SELECT rowid, libelle, cp, ville, active
|
||
FROM users_entites
|
||
WHERE active = 1
|
||
ORDER BY rowid;"
|
||
|
||
# Migrer une amicale spécifique (exemple : ID 45)
|
||
php scripts/php/migrate_from_backup.php \
|
||
--source-db=geosector_20251007 \
|
||
--target-db=pra_geo \
|
||
--mode=entity \
|
||
--entity-id=45 \
|
||
--log=/var/www/geosector/api/logs/migration_entity_45_20251007.log
|
||
|
||
# Suivre la progression dans un autre terminal
|
||
incus exec pra-geo -- tail -f /var/www/geosector/api/logs/migration_entity_45_20251007.log
|
||
```
|
||
|
||
### Étape 6️⃣ : Vérification de la migration
|
||
|
||
```bash
|
||
# Depuis pra-geo ou directement sur IN4, vérifier les données migrées
|
||
|
||
# Comparer les comptages source vs cible pour l'entité 45
|
||
mysql -h 13.23.33.4 -u root -p'MyAlpLocal,90b' << 'EOF'
|
||
SELECT
|
||
'Users' as table_name,
|
||
(SELECT COUNT(*) FROM geosector_20251007.users WHERE fk_entite = 45) as source,
|
||
(SELECT COUNT(*) FROM pra_geo.users WHERE fk_entite = 45) as cible
|
||
UNION ALL SELECT
|
||
'Operations',
|
||
(SELECT COUNT(*) FROM geosector_20251007.operations WHERE fk_entite = 45),
|
||
(SELECT COUNT(*) FROM pra_geo.operations WHERE fk_entite = 45)
|
||
UNION ALL SELECT
|
||
'Passages',
|
||
(SELECT COUNT(*) FROM geosector_20251007.ope_pass
|
||
WHERE fk_operation IN (SELECT rowid FROM geosector_20251007.operations WHERE fk_entite = 45)),
|
||
(SELECT COUNT(*) FROM pra_geo.ope_pass
|
||
WHERE fk_operation IN (SELECT id FROM pra_geo.operations WHERE fk_entite = 45));
|
||
EOF
|
||
|
||
# Vérifier l'intégrité des montants
|
||
mariadb -u root -p'MyAlpLocal,90b' pra_geo << 'EOF'
|
||
SELECT p.fk_type, COUNT(*) as nb_passages, SUM(p.montant) as total_montant
|
||
FROM ope_pass p
|
||
JOIN operations o ON p.fk_operation = o.id
|
||
WHERE o.fk_entite = 45
|
||
GROUP BY p.fk_type;
|
||
EOF
|
||
```
|
||
|
||
### Étape 7️⃣ : Nettoyage (optionnel)
|
||
|
||
```bash
|
||
# Supprimer la base temporaire geosector_YYYYMMDD après migration réussie
|
||
mariadb -u root -p'MyAlpLocal,90b' -e "DROP DATABASE IF EXISTS geosector_20251007;"
|
||
|
||
# Supprimer les fichiers de backup
|
||
rm -f /var/back/geosector_20251007.sql.tar.gz
|
||
rm -f /var/back/geosector_20251007.sql
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Notes importantes
|
||
|
||
### Sécurité et mot de passe decpm7.sh
|
||
|
||
Le script `decpm7.sh` sur PM7 nécessite un mot de passe de déchiffrement. Assurez-vous de :
|
||
- Connaître le mot de passe avant de lancer le déchiffrement
|
||
- Ne jamais commiter ce mot de passe dans Git
|
||
- Le stocker de manière sécurisée (gestionnaire de mots de passe)
|
||
|
||
### Durée estimée de la migration
|
||
|
||
| Taille de l'amicale | Temps estimé |
|
||
|---------------------|--------------|
|
||
| Petite (< 5000 passages) | 2-5 minutes |
|
||
| Moyenne (5000-20000 passages) | 5-15 minutes |
|
||
| Grande (> 20000 passages) | 15-60 minutes |
|
||
|
||
### Espace disque requis
|
||
|
||
- Backup chiffré : ~500 MB
|
||
- Backup déchiffré : ~1.2 GB
|
||
- Import dans MariaDB : ~2 GB
|
||
- Total recommandé : **5 GB d'espace libre**
|
||
|
||
### Gestion des erreurs courantes
|
||
|
||
**Erreur : "Disk full"**
|
||
```bash
|
||
# Vérifier l'espace disque
|
||
df -h /var/back
|
||
|
||
# Nettoyer les anciens backups
|
||
find /var/back -name "geosector_*.sql" -mtime +7 -delete
|
||
```
|
||
|
||
**Erreur : "Table already exists"**
|
||
```bash
|
||
# Si migration échouée à moitié, supprimer les données partielles
|
||
mariadb -u root -p'MyAlpLocal,90b' pra_geo -e "
|
||
DELETE FROM ope_pass WHERE fk_operation IN (SELECT id FROM operations WHERE fk_entite = 45);
|
||
DELETE FROM operations WHERE fk_entite = 45;
|
||
DELETE FROM users WHERE fk_entite = 45;
|
||
DELETE FROM entites WHERE id = 45;"
|
||
```
|
||
|
||
---
|
||
|
||
## 🏗️ Architecture
|
||
|
||
### Fichiers de configuration
|
||
|
||
#### `config.php`
|
||
**Rôle** : Configuration principale des connexions bases de données et tunnel SSH
|
||
|
||
**Fonctionnalités** :
|
||
- Constantes de connexion SSH (host, port, clé privée)
|
||
- Constantes de connexion base source (via tunnel SSH sur port 13306)
|
||
- Constantes de connexion base cible (localhost:3306)
|
||
- Fonctions utilitaires :
|
||
- `createSshTunnel()` : Établit le tunnel SSH
|
||
- `closeSshTunnel()` : Ferme le tunnel SSH
|
||
- `getSourceConnection()` : Retourne PDO vers base source
|
||
- `getTargetConnection()` : Retourne PDO vers base cible
|
||
- `logOperation()` : Journalise les opérations dans `logs/migration_YYYY-MM-DD.log`
|
||
|
||
**⚠️ Problèmes identifiés** :
|
||
- Mots de passe et clés SSH en dur (lignes 8-26)
|
||
- Pas de gestion de fichier `.env`
|
||
- Credentials visibles dans le code source
|
||
|
||
#### `MigrationConfig.php`
|
||
**Rôle** : Configuration simplifiée pour le chiffrement
|
||
|
||
**Fonctionnalités** :
|
||
- Classe `AppConfig` singleton
|
||
- Fournit la clé de chiffrement pour les données sensibles
|
||
- Pas de dépendance aux en-têtes HTTP (adapté pour CLI)
|
||
|
||
#### `migrate.php`
|
||
**Rôle** : Script orchestrateur principal
|
||
|
||
**Fonctionnalités** :
|
||
- Exécute toutes les migrations dans l'ordre
|
||
- Gestion des arguments CLI (`--truncate`, `--create-table`, `--help`)
|
||
- Peut exécuter une migration spécifique : `php migrate.php users`
|
||
- Peut exécuter toutes les migrations : `php migrate.php`
|
||
- Affiche des statistiques de migration (temps, succès, erreurs)
|
||
|
||
**Ordre d'exécution** :
|
||
1. Tables de référence `x_*` (devises, types, pays, régions, départements, villes)
|
||
2. `entites` (entities)
|
||
3. `users` (utilisateurs)
|
||
4. `operations` (opérations)
|
||
5. `ope_sectors` (secteurs d'opération)
|
||
6. `sectors_adresses` (adresses de secteurs)
|
||
7. `ope_users` (associations utilisateurs-opérations)
|
||
8. `ope_users_sectors` (associations utilisateurs-secteurs)
|
||
9. `ope_pass` (passages)
|
||
10. `ope_pass_histo` (historique des passages)
|
||
11. `medias` (fichiers médias)
|
||
|
||
---
|
||
|
||
## 🔄 Différences Structurelles entre les Bases
|
||
|
||
### Vue d'ensemble
|
||
|
||
**Base source (geosector)** - MariaDB 10.11.9 :
|
||
- Serveur version : `10.11.9-MariaDB-deb12`
|
||
- Clés primaires : `rowid` (int(11))
|
||
- Statut : `active` (tinyint(1))
|
||
- Dates : `date_creat`, `date_modif` (datetime)
|
||
- **Données en clair** (pas de chiffrement)
|
||
- **Pas de contraintes FK définies**
|
||
|
||
**Base cible (geo_app)** - MariaDB 11.4.5 :
|
||
- Serveur version : `11.4.5-MariaDB`
|
||
- Clés primaires : `id` (int(10) unsigned)
|
||
- Statut : `chk_active` (tinyint(1) unsigned)
|
||
- Dates : `created_at`, `updated_at` (timestamp avec ON UPDATE CURRENT_TIMESTAMP)
|
||
- **Données chiffrées** : `encrypted_name`, `encrypted_email`, `encrypted_phone`, `encrypted_user_name`, etc.
|
||
- **Contraintes FK définies** avec ON DELETE/ON UPDATE CASCADE
|
||
|
||
### Mappings globaux systématiques
|
||
|
||
```
|
||
rowid → id
|
||
active → chk_active
|
||
date_creat → created_at
|
||
date_modif → updated_at
|
||
date_eve → passed_at (ope_pass uniquement)
|
||
support_rowid → support_id (medias uniquement)
|
||
```
|
||
|
||
### Tables absentes de la source (nouvelles fonctionnalités)
|
||
|
||
Ces tables n'existent que dans `geo_app` et **ne sont pas migrées** :
|
||
|
||
#### Système de chat (4 tables)
|
||
- `chat_rooms` : Salles de conversation
|
||
- `chat_messages` : Messages
|
||
- `chat_participants` : Participants aux conversations
|
||
- `chat_read_receipts` : Accusés de lecture
|
||
|
||
#### Intégration Stripe (7 tables)
|
||
- `stripe_accounts` : Comptes Stripe connectés
|
||
- `stripe_terminal_readers` : Lecteurs de cartes (Tap to Pay)
|
||
- `stripe_android_certified_devices` : Devices Android certifiés
|
||
- `stripe_payment_history` : Historique des paiements
|
||
- `stripe_refunds` : Remboursements
|
||
- `stripe_webhooks` : Événements webhook Stripe
|
||
|
||
#### Sécurité et monitoring (4 tables)
|
||
- `sec_alerts` : Alertes de sécurité
|
||
- `sec_blocked_ips` : IPs bloquées
|
||
- `sec_failed_login_attempts` : Tentatives de connexion échouées
|
||
- `sec_performance_metrics` : Métriques de performance
|
||
|
||
#### Autres nouvelles tables
|
||
- `user_devices` : Informations des devices mobiles
|
||
- `x_departements_contours` : Contours géographiques des départements
|
||
- `x_users_titres` : Titres des utilisateurs (Mme, M., etc.)
|
||
|
||
### Tables renommées
|
||
|
||
| Source | Cible | Notes |
|
||
|--------|-------|-------|
|
||
| `users_entites` | `entites` | Migration via `migrate_entites.php` |
|
||
| `sectors` | `ope_sectors` | Intégrée dans les opérations |
|
||
|
||
### Différences par table migrée
|
||
|
||
#### ⚠️ `x_villes`
|
||
**Modifications structurelles** :
|
||
- Source : `cp` → Cible : `code_postal`
|
||
- Source : `departement` (varchar 65) → **Supprimé** (redondant avec `fk_departement`)
|
||
|
||
#### ⚠️ `x_departements`
|
||
**Ajouts dans cible** :
|
||
- `dept_limitrophes` (varchar 100) : Départements limitrophes
|
||
- `contour` (geometry) : Contour géographique
|
||
|
||
#### ✅ `entites` (source: `users_entites`)
|
||
**Champs chiffrés** :
|
||
- `libelle` → `encrypted_name`
|
||
- `email` → `encrypted_email`
|
||
- `tel1`, `tel2` → `encrypted_phone`, `encrypted_mobile` (avec détection 06/07)
|
||
- `iban` → `encrypted_iban`
|
||
- `bic` → `encrypted_bic`
|
||
|
||
**Nouveaux champs** :
|
||
- `chk_stripe` (tinyint) : Intégration Stripe activée
|
||
- `encrypted_stripe_id` (varchar 255) : ID Stripe chiffré
|
||
- `chk_username_manuel` (tinyint) : Gestion usernames manuelle/auto
|
||
- `chk_user_delete_pass` (tinyint) : Autorisation suppression passages
|
||
- `chk_lot_actif` (tinyint) : Lots actifs
|
||
|
||
**Champs supprimés** :
|
||
- Tous les champs métier spécifiques (`appname`, `http_host`, `tva_intra`, `rcs`, `siret`, `ape`, `couleur`, `prefecture`, `gerant_*`, `banque_*`, `genbase`, `groupebase`, `userbase`, `passbase`, `demo`, `lib_*`, `icon_*`, `btn_width`, `nbmembres`, `nbconnex`)
|
||
|
||
#### ✅ `users`
|
||
**Champs chiffrés** :
|
||
- `libelle` → `encrypted_name`
|
||
- `username` → `encrypted_user_name` (chiffrement recherchable)
|
||
- `telephone` → `encrypted_phone`
|
||
- `mobile` → `encrypted_mobile`
|
||
- `email` → `encrypted_email` (chiffrement recherchable)
|
||
|
||
**Mappings spécifiques** :
|
||
- `userpswd` ou `userpass` → `user_pass_hash`
|
||
- `prenom` → `first_name`
|
||
- `nom_tournee` → `sect_name`
|
||
- `alert_email` → `chk_alert_email`
|
||
|
||
**Champs supprimés** :
|
||
- Nombreux champs métier : `num_adherent`, `libelle_naissance`, `josh`, `email_secondaire`, `infos`, `ltt`, `lng`, `sector`, `dept_naissance`, `commune_naissance`, `anciennete`, `fk_categorie`, `fk_sous_categorie`, `adresse_*`, `cp`, `ville`, `matricule`, `fk_grade`, `chk_adherent_*`, `chk_archive`, `chk_double_affectation`
|
||
|
||
#### ✅ `operations`
|
||
**Modifications** :
|
||
- `chk_api_adresse` : **Supprimé** dans cible
|
||
- Dates : `date_deb`, `date_fin` changent de `date` à `NOT NULL DEFAULT '0000-00-00'`
|
||
|
||
#### ✅ `ope_sectors`
|
||
**Nouveaux champs** :
|
||
- `fk_old_sector` (int unsigned) : Référence à l'ancien `sectors.rowid` pour le mapping
|
||
|
||
#### ✅ `ope_users`
|
||
**Nouveaux champs dans cible** :
|
||
- `fk_role` (int unsigned) : Rôle de l'utilisateur dans l'opération
|
||
- `first_name` (varchar 45) : Prénom
|
||
- `encrypted_name` (varchar 255) : Nom chiffré
|
||
- `sect_name` (varchar 60) : Nom de tournée
|
||
|
||
**Impact** : Le script `migrate_ope_users.php` doit remplir ces champs ou les laisser vides
|
||
|
||
#### ✅ `ope_pass`
|
||
**Champs chiffrés** :
|
||
- `libelle` → `encrypted_name`
|
||
- `email` → `encrypted_email` (chiffrement recherchable)
|
||
- `phone` → `encrypted_phone`
|
||
|
||
**Nouveaux champs dans cible** :
|
||
- `residence` (varchar 75) : Nom de la résidence
|
||
- `date_recu` (timestamp) : Date de réception
|
||
- `date_creat_recu` (timestamp) : Date de création du reçu
|
||
- `date_sent_recu` (timestamp) : Date d'envoi du reçu
|
||
- `stripe_payment_id` (varchar 50) : ID du PaymentIntent Stripe
|
||
|
||
**Champs supprimés** :
|
||
- `lieudit` (varchar 75)
|
||
- `chk_habitat_vide` (tinyint)
|
||
- `lot_nb_passages` (int)
|
||
|
||
**Mappings spécifiques** :
|
||
- `recu` → `nom_recu`
|
||
- `date_eve` → `passed_at`
|
||
- `fk_type` : transformation 8→5, 9→6
|
||
|
||
#### ✅ `ope_pass_histo`
|
||
**Champ supprimé** :
|
||
- `fk_user` : **N'existe plus** dans la nouvelle structure
|
||
|
||
#### ✅ `medias`
|
||
**Mapping spécifique** :
|
||
- `support_rowid` → `support_id`
|
||
|
||
**Nouveaux champs dans cible** :
|
||
- `fk_entite` (int unsigned) : Propriétaire du média
|
||
- `fk_operation` (int unsigned) : Opération liée
|
||
- `file_type`, `file_category`, `file_size`, `mime_type` : Métadonnées fichier
|
||
- `original_name`, `file_path` : Informations fichier
|
||
- `original_width`, `original_height`, `processed_width`, `processed_height` : Dimensions images
|
||
- `is_processed` : Statut traitement image
|
||
|
||
**Champs supprimés** :
|
||
- `dir0`, `dir1`, `dir2` : Ancienne structure de dossiers
|
||
- `type_fichier`, `position`, `hauteur`, `largeur`, `niveaugris` : Anciens champs métier
|
||
|
||
#### ✅ `sectors_adresses`
|
||
**Nouveaux champs dans cible** :
|
||
- `id` (auto-increment) : Clé primaire ajoutée
|
||
- `osm_id` (int) : ID OpenStreetMap
|
||
- `osm_name` (varchar 50) : Nom OSM
|
||
- `osm_date_creat` (timestamp) : Date de création OSM
|
||
- `created_at`, `updated_at` : Timestamps standards
|
||
|
||
**Mappings** :
|
||
- `fk_sector` : ancien ID → nouvel ID via mapping `ope_sectors`
|
||
|
||
### Tables non migrées de la source
|
||
|
||
Ces tables existent dans `geosector` mais **ne sont pas migrées** vers `geo_app` :
|
||
|
||
- `articles`, `articles_pages` : Système d'articles (obsolète ?)
|
||
- `blog_articles`, `blog_pages` : Système de blog (obsolète ?)
|
||
- `email_counter`, `email_queue` : File d'attente emails (recréée dans cible)
|
||
- `ope_pass_recus` : Table séparée des reçus (intégrée dans `ope_pass`)
|
||
- `ope_users_suivis` : Suivi GPS des utilisateurs (archivé ?)
|
||
- `operations_docs`, `operations_eve_docs` : Documents opérations (archivé ?)
|
||
- `params` : Paramètres globaux (reconfiguré ?)
|
||
- `sectors`, `sectors_streets` : Tables sectors (transformée en `ope_sectors`)
|
||
- `users_lastpos` : Dernière position utilisateurs (archivé ?)
|
||
- `x_civilites` : Civilités (remplacé par `x_users_titres`)
|
||
- `x_users_categories`, `x_users_sous_categories`, `x_users_grades` : Catégories utilisateurs (supprimé)
|
||
- `y_conf`, `y_menus`, `y_modules`, `y_modules_rules`, `y_pages` : Configuration interface (obsolète)
|
||
- `z_logs`, `z_sessions`, `z_stats` : Logs et sessions (recréés dans cible)
|
||
|
||
### Contraintes de clés étrangères
|
||
|
||
**Source** : Aucune contrainte FK définie explicitement
|
||
|
||
**Cible** : Toutes les FK définies avec :
|
||
- `ON UPDATE CASCADE` : Mise à jour en cascade
|
||
- `ON DELETE CASCADE` ou `ON DELETE SET NULL` : Suppression gérée
|
||
|
||
**Impact migration** : Les scripts doivent respecter l'ordre des dépendances pour éviter les erreurs d'intégrité référentielle.
|
||
|
||
### Vues dans geo_app
|
||
|
||
- `chat_rooms_with_last_message` : Vue des salles de chat avec dernier message
|
||
- `v_stripe_entite_stats` : Statistiques Stripe par entité
|
||
- `v_stripe_payment_stats` : Statistiques paiements Stripe
|
||
|
||
**Note** : Les vues ne contiennent pas de données à migrer.
|
||
|
||
---
|
||
|
||
## 📊 Analyse Script par Script
|
||
|
||
### 1. Tables de référence `x_*`
|
||
|
||
#### `migrate_x_devises.php`
|
||
**Table** : `x_devises` (Devises)
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
|
||
**Particularités** :
|
||
- Crée automatiquement la table si elle n'existe pas
|
||
- Utilise `logOperation()` pour le logging
|
||
- Pattern : `ON DUPLICATE KEY UPDATE`
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_x_entites_types.php`
|
||
**Table** : `x_entites_types` (Types d'entités)
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
|
||
**Particularités** :
|
||
- Logging simple via `echo`
|
||
- Pattern : `ON DUPLICATE KEY UPDATE`
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_x_types_passages.php`, `migrate_x_types_reglements.php`, `migrate_x_users_roles.php`
|
||
**Tables** : Types de passages, types de règlements, rôles utilisateurs
|
||
**Pattern** : Identique aux autres tables `x_*`
|
||
|
||
**Statut** : ✅ Fonctionnel (non lu mais présumé identique)
|
||
|
||
---
|
||
|
||
#### `migrate_x_pays.php`
|
||
**Table** : `x_pays` (Pays)
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
- Conservation de `code`, `fk_continent`, `fk_devise`, `libelle`
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_x_regions.php`
|
||
**Table** : `x_regions` (Régions)
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
- Conservation de tous les champs métier (`fk_pays`, `libelle`, `libelle_long`, `table_osm`, `departements`)
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_x_departements.php`, `migrate_x_villes.php`
|
||
**Tables** : Départements, Villes
|
||
**Pattern** : Identique aux autres tables `x_*`
|
||
|
||
**Statut** : ✅ Fonctionnel (non lu mais présumé identique)
|
||
|
||
---
|
||
|
||
### 2. Tables métier principales
|
||
|
||
#### `migrate_entites.php`
|
||
**Table source** : `users_entites`
|
||
**Table cible** : `entites`
|
||
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
- `libelle` → `encrypted_name` (chiffré)
|
||
- `tel1`, `tel2` → `encrypted_phone`, `encrypted_mobile` (logique de détection 06/07)
|
||
- `email` → `encrypted_email` (chiffré et recherchable)
|
||
- `iban` → `encrypted_iban` (chiffré)
|
||
- `bic` → `encrypted_bic` (chiffré)
|
||
- `cp` → `code_postal`
|
||
|
||
**Particularités** :
|
||
- **Chiffrement** : Utilise `ApiService::encryptData()` et `ApiService::encryptSearchableData()`
|
||
- **Logique téléphones** : Détecte mobiles (06/07) vs fixes
|
||
- **Valeur par défaut** : `chk_demo = 0` (forcé)
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_users.php`
|
||
**Table** : `users`
|
||
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
- `libelle` → `encrypted_name` (chiffré)
|
||
- `prenom` → `first_name`
|
||
- `nom_tournee` → `sect_name`
|
||
- `username` → `encrypted_user_name` (chiffré et recherchable)
|
||
- `userpswd` ou `userpass` → `user_pass_hash`
|
||
- `telephone` → `encrypted_phone` (chiffré)
|
||
- `mobile` → `encrypted_mobile` (chiffré)
|
||
- `email` → `encrypted_email` (chiffré et recherchable)
|
||
- `alert_email` → `chk_alert_email`
|
||
|
||
**Particularités** :
|
||
- **Tests de chiffrement** : Pour les 100 premiers utilisateurs (lignes 130-158)
|
||
- Chiffre et déchiffre email et username
|
||
- Affiche les valeurs pour vérification
|
||
- **⚠️ Impact performance** : Ralentit la migration
|
||
- **Gestion rôle** : Force `fk_role=1` si `fk_role=0`
|
||
- **Gestion titre** : Force `fk_titre=1` si différent de 1 ou 2
|
||
|
||
**⚠️ PROBLÈME CRITIQUE** (lignes 227-239) :
|
||
```php
|
||
if ($exists) {
|
||
$insertStmt->execute($userData); // OK : update
|
||
$successCount++;
|
||
} else {
|
||
$errorCount++; // ❌ ERREUR : devrait insérer, pas compter comme erreur
|
||
}
|
||
```
|
||
**Conséquence** : Les nouveaux utilisateurs ne sont jamais insérés, comptés comme erreurs
|
||
|
||
**Statut** : 🔴 Bug critique - logique d'insertion inversée
|
||
|
||
---
|
||
|
||
#### `migrate_operations.php`
|
||
**Table** : `operations`
|
||
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
- `date_creat` → `created_at`
|
||
- `date_modif` → `updated_at`
|
||
|
||
**Particularités** :
|
||
- **Limitation arbitraire** : Ne migre que les **3 dernières opérations par entité** (lignes 54-70)
|
||
- Vérifie que les entités référencées ont été migrées
|
||
- Filtre basé sur `fk_entite IN (IDs des entités migrées)`
|
||
- Option `--truncate` pour vider la table avant migration
|
||
|
||
**⚠️ Problème** :
|
||
- Limite de 3 opérations non documentée et non paramétrable
|
||
- Pourrait perdre des données historiques importantes
|
||
|
||
**Statut** : ⚠️ Limitation fonctionnelle - à valider métier
|
||
|
||
---
|
||
|
||
#### `migrate_ope_sectors.php`
|
||
**Table** : `ope_sectors`
|
||
|
||
**Mappings** :
|
||
- Fusionne données de `sectors` et `ope_users_sectors`
|
||
- `rowid` de `sectors` → `fk_old_sector`
|
||
- Génère un nouvel `id` auto-incrémenté
|
||
- Conservation de `libelle`, `sector` (géométrie), `color`
|
||
|
||
**Particularités** :
|
||
- Crée une correspondance `fk_operation + fk_old_sector → id`
|
||
- Utile pour les migrations suivantes (ope_users_sectors, ope_pass)
|
||
- Ne migre que les secteurs liés aux opérations migrées
|
||
- Filtre `active = 1` sur source
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_sectors_adresses.php`
|
||
**Table** : `sectors_adresses`
|
||
|
||
**Mappings** :
|
||
- `fk_sector` (ancien ID) → `fk_sector` (nouvel ID via mapping)
|
||
- Ajout de colonnes OSM avec valeurs par défaut :
|
||
- `osm_id = 0`
|
||
- `osm_name = ''`
|
||
- `osm_date_creat = '0000-00-00 00:00:00'`
|
||
|
||
**Particularités** :
|
||
- Utilise le mapping créé par `migrate_ope_sectors.php`
|
||
- Recherche `fk_old_sector → id` dans `ope_sectors`
|
||
- Ignore les adresses dont le secteur n'a pas été migré
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_ope_users.php`
|
||
**Table** : `ope_users`
|
||
|
||
**Mappings** :
|
||
- `rowid` → `id`
|
||
- `active` → `chk_active`
|
||
- `date_creat` → `created_at`
|
||
- `date_modif` → `updated_at`
|
||
|
||
**Particularités** :
|
||
- Vérifie que `fk_operation` et `fk_user` existent dans les tables cibles
|
||
- Double filtrage par IDs migrés
|
||
- Ne migre que les associations valides
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_ope_users_sectors.php`
|
||
**Table** : `ope_users_sectors`
|
||
|
||
**Mappings** :
|
||
- `fk_sector` (ancien ID) → `fk_sector` (nouvel ID via mapping ope_sectors)
|
||
- Pas de `rowid` → utilise clé composite `(fk_operation, fk_user, fk_sector)`
|
||
|
||
**Particularités** :
|
||
- Triple vérification :
|
||
1. Opération migrée
|
||
2. Utilisateur migré
|
||
3. Secteur existe dans mapping ope_sectors
|
||
- Génère la clé de recherche : `fk_operation . '_' . fk_old_sector`
|
||
- Compteur `skipped` pour associations ignorées
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_ope_pass.php` ⭐ Script le plus complexe
|
||
**Table** : `ope_pass` (Passages)
|
||
|
||
**Mappings** :
|
||
- `date_eve` → `passed_at` 📅
|
||
- `libelle` → `encrypted_name` 🔒 (chiffré)
|
||
- `email` → `encrypted_email` 🔒 (chiffré et recherchable)
|
||
- `phone` → `encrypted_phone` 🔒 (chiffré)
|
||
- `recu` → `nom_recu`
|
||
- `fk_sector` (ancien ID) → `fk_sector` (nouvel ID via mapping)
|
||
- `fk_type` : transformation 8→5, 9→6
|
||
|
||
**Particularités** :
|
||
- **Gestion avancée des timeouts** (lignes 46-80) :
|
||
- Configure `PDO::ATTR_TIMEOUT = 600` (10 minutes)
|
||
- Configure variables MariaDB 10.11 :
|
||
- `wait_timeout = 3600` (1h)
|
||
- `net_read_timeout = 3600` (1h)
|
||
- `net_write_timeout = 3600` (1h)
|
||
- `innodb_lock_wait_timeout = 3600` (1h)
|
||
- **Suppression des contraintes FK** avant migration (lignes 98-120)
|
||
- **Suppression par lots** : Delete par lots de 100 000 (lignes 136-160)
|
||
- **Migration par lots** : 5 000 passages par lot (lignes 238-543)
|
||
- **Transactions par lot** : `BEGIN TRANSACTION` → traitement → `COMMIT`
|
||
- **Garbage collector** : Appel explicite `gc_collect_cycles()` pour libérer mémoire
|
||
- **Validation email** : `filter_var($email, FILTER_VALIDATE_EMAIL)` avant chiffrement
|
||
- **Gestion type_reglement** : Force à 4 si différent de 1, 2 ou 3
|
||
- **Recréation des FK** après migration (lignes 547-575)
|
||
|
||
**⚠️ Points d'attention** :
|
||
- Tue le processus SSH sur port 13306 au démarrage (ligne 23)
|
||
- Désactive `FOREIGN_KEY_CHECKS` pendant suppression
|
||
- Gestion silencieuse des passages dont secteur/utilisateur non migré
|
||
|
||
**Statut** : ✅ Fonctionnel - Optimisé pour gros volumes
|
||
|
||
---
|
||
|
||
#### `migrate_ope_pass_histo.php`
|
||
**Table** : `ope_pass_histo`
|
||
|
||
**Mappings** :
|
||
- `rowid` (non conservé, auto-increment)
|
||
- `date_histo` (conversion datetime)
|
||
- `fk_user` → **supprimé** (n'existe plus dans nouvelle structure)
|
||
|
||
**Particularités** :
|
||
- Ne migre que si `fk_pass` existe dans `ope_pass` cible
|
||
- Suppression complète avant migration (`DELETE FROM ope_pass_histo`)
|
||
- Mode "silencieux" : affiche uniquement les erreurs
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
#### `migrate_medias.php`
|
||
**Table** : `medias`
|
||
|
||
**Mappings** :
|
||
- `support_rowid` → `support_id` 📝
|
||
- `date_creat` → `created_at`
|
||
- `date_modif` → `updated_at`
|
||
|
||
**Particularités** :
|
||
- Vérifie que `fk_user_creat` et `fk_user_modif` existent
|
||
- Force à `0` (système) si utilisateur non migré
|
||
- Suppression complète avant migration
|
||
- Pas de chiffrement (fichiers référencés, pas de données sensibles)
|
||
|
||
**Statut** : ✅ Fonctionnel
|
||
|
||
---
|
||
|
||
## 🔍 Problèmes Identifiés par Priorité
|
||
|
||
### 🔴 CRITIQUES (Bloquants)
|
||
|
||
#### 1. **Bug logique `migrate_users.php` (lignes 227-239)**
|
||
**Impact** : Aucun nouvel utilisateur ne peut être inséré
|
||
```php
|
||
// Logique actuelle (INCORRECTE)
|
||
if ($exists) {
|
||
$insertStmt->execute($userData); // Update OK
|
||
$successCount++;
|
||
} else {
|
||
$errorCount++; // ❌ Devrait faire INSERT
|
||
}
|
||
```
|
||
**Solution** :
|
||
```php
|
||
// Logique corrigée
|
||
$insertStmt->execute($userData); // ON DUPLICATE KEY fait le travail
|
||
$successCount++;
|
||
```
|
||
|
||
---
|
||
|
||
### ⚠️ MAJEURS (Fonctionnels)
|
||
|
||
#### 2. **Limitation arbitraire opérations `migrate_operations.php` (lignes 54-70)**
|
||
**Impact** : Perte potentielle de données historiques
|
||
- Ne migre que 3 dernières opérations par entité
|
||
- Pas paramétrable
|
||
- Pas documenté dans le code
|
||
|
||
**Solution proposée** :
|
||
- Ajouter paramètre `--limit-operations=N` (défaut : 0 = toutes)
|
||
- Documenter dans `--help`
|
||
|
||
---
|
||
|
||
#### 3. **Tests de chiffrement dans production `migrate_users.php` (lignes 130-158)**
|
||
**Impact** : Ralentissement x2 de la migration users
|
||
- Teste chiffrement/déchiffrement pour 100 premiers users
|
||
- S'exécute à chaque migration
|
||
|
||
**Solution proposée** :
|
||
- Créer flag `--test-encryption`
|
||
- Désactiver par défaut
|
||
|
||
---
|
||
|
||
#### 4. **Configuration sensible en dur `config.php`**
|
||
**Impact** : Sécurité compromise
|
||
- Mots de passe en clair
|
||
- Clé SSH en dur
|
||
- Visible dans Git
|
||
|
||
**Solution proposée** :
|
||
- Créer `scripts/.env.example`
|
||
- Utiliser `vlucas/phpdotenv` ou parser manuel
|
||
- Ajouter `scripts/.env` au `.gitignore`
|
||
|
||
---
|
||
|
||
### ℹ️ MINEURS (Améliorations)
|
||
|
||
#### 5. **Incohérence logging**
|
||
**Impact** : Difficulté debugging
|
||
- Certains scripts : `logOperation()`
|
||
- D'autres : `echo` direct
|
||
|
||
**Solution** : Uniformiser avec `logOperation()` partout
|
||
|
||
---
|
||
|
||
#### 6. **Pas de transaction globale**
|
||
**Impact** : État incohérent si échec en milieu de migration
|
||
- Chaque table = migration indépendante
|
||
- Si échec table N, tables 1..N-1 déjà modifiées
|
||
|
||
**Solution proposée** :
|
||
- Option `--transactional` pour tout englobé dans 1 transaction
|
||
- Par défaut : comportement actuel (plus sûr)
|
||
|
||
---
|
||
|
||
#### 7. **Gestion FK manuelle dans `migrate_ope_pass.php`**
|
||
**Impact** : Complexité maintenance
|
||
- Désactivation/réactivation manuelle
|
||
- Suppression/recréation manuelle
|
||
|
||
**Solution** :
|
||
- Vérifier si vraiment nécessaire
|
||
- Documenter pourquoi (gros volumes)
|
||
|
||
---
|
||
|
||
#### 8. **Dates avec valeurs `0000-00-00`**
|
||
**Impact** : Warnings MariaDB en mode strict
|
||
- Plusieurs scripts utilisent `'0000-00-00'` ou `'0000-00-00 00:00:00'`
|
||
- MariaDB 10.11 en mode strict refuse ces valeurs
|
||
|
||
**Solution** : Remplacer par `NULL`
|
||
|
||
---
|
||
|
||
#### 9. **Pas de rapport détaillé post-migration**
|
||
**Impact** : Difficile de valider la migration
|
||
- Pas de récapitulatif des données migrées
|
||
- Pas de comparaison source vs cible
|
||
|
||
**Solution proposée** :
|
||
- Créer script `scripts/php/verify_migration.php`
|
||
- Compare counts par table
|
||
- Liste les incohérences
|
||
|
||
---
|
||
|
||
## ✅ Points Positifs
|
||
|
||
1. ✨ **Idempotence** : Utilisation systématique de `ON DUPLICATE KEY UPDATE`
|
||
2. 🔒 **Sécurité données** : Chiffrement des données sensibles
|
||
3. 🎯 **Filtrage intelligent** : Ne migre que les données liées (pas d'orphelins)
|
||
4. 📊 **Optimisation gros volumes** : Migration par lots (`ope_pass`)
|
||
5. 🔗 **Gestion dépendances** : Ordre d'exécution respecté
|
||
6. 🧹 **Nettoyage mémoire** : Garbage collection explicite
|
||
7. 📝 **Logging** : Historique des migrations dans `logs/`
|
||
8. 🚀 **Tunnel SSH automatique** : Connexion transparente
|
||
|
||
---
|
||
|
||
## ⚙️ Incohérences Détectées (Scripts vs Structures Réelles)
|
||
|
||
Suite à l'analyse comparative des scripts de migration avec les structures SQL réelles, voici les incohérences identifiées :
|
||
|
||
### 🔴 CRITIQUES
|
||
|
||
#### 1. `migrate_users.php` - Logique d'insertion inversée
|
||
**Lignes** : 227-239
|
||
**Problème** : Les nouveaux utilisateurs ne sont jamais insérés
|
||
```php
|
||
// Code actuel (INCORRECT)
|
||
if ($exists) {
|
||
$insertStmt->execute($userData); // Update OK
|
||
$successCount++;
|
||
} else {
|
||
$errorCount++; // ❌ Devrait faire INSERT, pas compter comme erreur
|
||
}
|
||
```
|
||
**Impact** : **BLOQUANT** - Impossible d'ajouter de nouveaux utilisateurs
|
||
**Solution** : Utiliser directement `ON DUPLICATE KEY UPDATE` sans vérification préalable
|
||
|
||
---
|
||
|
||
### ⚠️ MAJEURS
|
||
|
||
#### 2. `migrate_x_departements.php` - Nouveaux champs non remplis
|
||
**Champs manquants** :
|
||
- `dept_limitrophes` (varchar 100) : Départements limitrophes
|
||
- `contour` (geometry) : Contour géographique
|
||
|
||
**Impact** : Ces champs resteront `NULL` après migration
|
||
**Solution** : Acceptable si ces données seront ajoutées ultérieurement
|
||
|
||
---
|
||
|
||
#### 3. `migrate_ope_users.php` - Nouveaux champs non remplis
|
||
**Champs manquants** :
|
||
- `fk_role` (int) : Rôle de l'utilisateur dans l'opération
|
||
- `first_name` (varchar 45) : Prénom
|
||
- `encrypted_name` (varchar 255) : Nom chiffré
|
||
- `sect_name` (varchar 60) : Nom de tournée
|
||
|
||
**Impact** : Fonctionnalités limitées - ces informations manqueront dans les opérations
|
||
**Solution** : Enrichir le script pour copier ces données depuis `users` si disponibles
|
||
|
||
---
|
||
|
||
#### 4. `migrate_medias.php` - Métadonnées fichiers manquantes
|
||
**Champs manquants** :
|
||
- `fk_entite`, `fk_operation` : Liens vers entités/opérations
|
||
- `file_type`, `file_category`, `file_size`, `mime_type` : Métadonnées
|
||
- `original_name`, `file_path` : Informations fichier
|
||
- `original_width`, `original_height`, `processed_width`, `processed_height` : Dimensions images
|
||
- `is_processed` : Statut traitement
|
||
|
||
**Impact** : Fonctionnalités de gestion de médias limitées
|
||
**Solution** :
|
||
- Analyser les fichiers existants pour extraire les métadonnées
|
||
- Déduire `fk_entite` et `fk_operation` depuis `support` et `support_id`
|
||
|
||
---
|
||
|
||
### ℹ️ MINEURS (Informations)
|
||
|
||
#### 5. `migrate_x_villes.php` - Champ `departement` ignoré
|
||
**Champ supprimé** : `departement` (varchar 65)
|
||
**Raison** : Redondant avec `fk_departement`
|
||
**Impact** : ✅ Correct - pas de perte de données
|
||
|
||
---
|
||
|
||
#### 6. `migrate_ope_pass.php` - Nouveaux champs Stripe
|
||
**Champs non remplis** :
|
||
- `residence` (varchar 75)
|
||
- `date_recu`, `date_creat_recu`, `date_sent_recu` (timestamps)
|
||
- `stripe_payment_id` (varchar 50)
|
||
|
||
**Impact** : Attendu - ces fonctionnalités sont nouvelles
|
||
**Solution** : ✅ Aucune action requise - champs remplis lors de l'utilisation future
|
||
|
||
---
|
||
|
||
#### 7. `migrate_entites.php` - Nouveaux champs Stripe/Config
|
||
**Champs non remplis** :
|
||
- `chk_stripe`, `encrypted_stripe_id` : Intégration Stripe
|
||
- `chk_username_manuel` : Gestion usernames
|
||
- `chk_user_delete_pass` : Autorisation suppression
|
||
- `chk_lot_actif` : Gestion lots
|
||
|
||
**Impact** : Attendu - nouvelles fonctionnalités
|
||
**Solution** : ✅ Valeurs par défaut appropriées définies dans la structure
|
||
|
||
---
|
||
|
||
### 📊 Résumé des vérifications
|
||
|
||
| Script | Statut | Problèmes critiques | Problèmes majeurs | Avertissements |
|
||
|--------|--------|---------------------|-------------------|----------------|
|
||
| `migrate_users.php` | 🔴 | 1 | 0 | 0 |
|
||
| `migrate_x_departements.php` | ⚠️ | 0 | 1 | 0 |
|
||
| `migrate_ope_users.php` | ⚠️ | 0 | 1 | 0 |
|
||
| `migrate_medias.php` | ⚠️ | 0 | 1 | 0 |
|
||
| `migrate_x_villes.php` | ✅ | 0 | 0 | 1 |
|
||
| `migrate_ope_pass.php` | ✅ | 0 | 0 | 1 |
|
||
| `migrate_entites.php` | ✅ | 0 | 0 | 1 |
|
||
| Autres scripts `x_*` | ✅ | 0 | 0 | 0 |
|
||
|
||
### 🛠️ Outil de vérification
|
||
|
||
Un script de vérification automatique a été créé : **`verify_migration_structure.php`**
|
||
|
||
**Usage** :
|
||
```bash
|
||
php scripts/php/verify_migration_structure.php
|
||
```
|
||
|
||
**Fonctionnalités** :
|
||
- Compare les colonnes source vs cible pour chaque table
|
||
- Identifie les colonnes non mappées
|
||
- Liste les nouvelles colonnes qui seront NULL
|
||
- Affichage coloré avec compteurs d'erreurs/avertissements
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 🚀 Migration via Endpoint API (Approche Recommandée)
|
||
|
||
### Vue d'ensemble
|
||
|
||
Au lieu d'exécuter les scripts PHP en ligne de commande, nous recommandons d'utiliser un **endpoint API REST** qui permet de migrer **UNE entité (amicale) à la fois** de manière contrôlée et testable.
|
||
|
||
### Avantages de cette approche
|
||
|
||
✅ **Migration progressive** : Une entité à la fois, avec validation entre chaque étape
|
||
✅ **Interface utilisateur** : Suivi visuel de la progression depuis Flutter/Web
|
||
✅ **Tests granulaires** : Vérification table par table avant de continuer
|
||
✅ **Rollback possible** : Annulation par entité en cas de problème
|
||
✅ **Logs détaillés** : Traçabilité complète dans l'API
|
||
✅ **Sécurité renforcée** : Authentification et autorisation via l'API
|
||
✅ **Moins risqué** : Pas de migration globale "big bang"
|
||
|
||
### Architecture de l'endpoint
|
||
|
||
#### Endpoint principal
|
||
```http
|
||
POST /api/migrations/entity
|
||
Authorization: Bearer {session_id}
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"entity_id": 45, // ID de l'entité dans l'ancienne base
|
||
"steps": ["users", "operations", "ope_pass"], // Étapes à exécuter (optionnel)
|
||
"dry_run": false, // Mode simulation (optionnel)
|
||
"truncate": false // Vider les tables cible avant migration (optionnel)
|
||
}
|
||
```
|
||
|
||
#### Réponse
|
||
```json
|
||
{
|
||
"status": "success",
|
||
"entity_id": 45,
|
||
"entity_name": "Amicale de Grenoble",
|
||
"migration_id": "mig_abc123",
|
||
"steps_completed": [
|
||
{
|
||
"step": "x_devises",
|
||
"status": "success",
|
||
"records_migrated": 1,
|
||
"duration_ms": 45
|
||
},
|
||
{
|
||
"step": "users",
|
||
"status": "success",
|
||
"records_migrated": 38,
|
||
"duration_ms": 234
|
||
}
|
||
],
|
||
"total_duration_ms": 1234,
|
||
"summary": {
|
||
"total_records": 156,
|
||
"total_errors": 0,
|
||
"total_warnings": 2
|
||
}
|
||
}
|
||
```
|
||
|
||
### Plan de migration étape par étape
|
||
|
||
#### Phase 0️⃣ : Préparation
|
||
|
||
**Actions à effectuer :**
|
||
- [ ] Créer le controller `MigrationController.php`
|
||
- [ ] Créer le service `MigrationService.php`
|
||
- [ ] Ajouter les routes dans `index.php`
|
||
- [ ] Configurer la connexion à la base source dans `AppConfig.php`
|
||
- [ ] Tester la connexion aux deux bases
|
||
|
||
**Tests de préparation :**
|
||
```bash
|
||
# Tester la connexion aux bases
|
||
GET /api/migrations/test-connections
|
||
|
||
# Lister les entités disponibles à migrer
|
||
GET /api/migrations/entities/available
|
||
|
||
# Récupérer les détails d'une entité source
|
||
GET /api/migrations/entities/{source_id}
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 1️⃣ : Tables de référence `x_*`
|
||
|
||
**Ordre d'exécution :**
|
||
1. `x_devises`
|
||
2. `x_entites_types`
|
||
3. `x_types_passages`
|
||
4. `x_types_reglements`
|
||
5. `x_users_roles`
|
||
6. `x_pays`
|
||
7. `x_regions`
|
||
8. `x_departements`
|
||
9. `x_villes`
|
||
|
||
**Endpoint de test :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "x_devises",
|
||
"dry_run": true
|
||
}
|
||
```
|
||
|
||
**Vérifications à effectuer :**
|
||
- [ ] Nombre d'enregistrements source == cible
|
||
- [ ] Champs `rowid` → `id` correctement mappés
|
||
- [ ] Champs `active` → `chk_active` correctement mappés
|
||
- [ ] Aucune erreur de contrainte FK
|
||
|
||
**Requêtes SQL de vérification :**
|
||
```sql
|
||
-- Comparer les counts
|
||
SELECT 'source' as db, COUNT(*) as count FROM geosector.x_devises
|
||
UNION ALL
|
||
SELECT 'cible' as db, COUNT(*) as count FROM geo_app.x_devises;
|
||
|
||
-- Vérifier les IDs manquants
|
||
SELECT s.rowid
|
||
FROM geosector.x_devises s
|
||
LEFT JOIN geo_app.x_devises t ON s.rowid = t.id
|
||
WHERE t.id IS NULL;
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 2️⃣ : Entité (Amicale)
|
||
|
||
**Tables concernées :**
|
||
- `users_entites` → `entites`
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "entites"
|
||
}
|
||
```
|
||
|
||
**Mappings critiques à vérifier :**
|
||
- [ ] `libelle` → `encrypted_name` (chiffrement AES-256)
|
||
- [ ] `email` → `encrypted_email` (chiffrement recherchable)
|
||
- [ ] `tel1`, `tel2` → `encrypted_phone`, `encrypted_mobile` (détection 06/07)
|
||
- [ ] `iban` → `encrypted_iban`
|
||
- [ ] `bic` → `encrypted_bic`
|
||
- [ ] `cp` → `code_postal`
|
||
|
||
**Tests fonctionnels :**
|
||
```http
|
||
# Récupérer l'entité migrée
|
||
GET /api/entites/45
|
||
|
||
# Vérifier le déchiffrement
|
||
# Le nom doit être lisible dans la réponse
|
||
```
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Vérifier la présence de l'entité
|
||
SELECT id, encrypted_name, encrypted_email
|
||
FROM geo_app.entites
|
||
WHERE id = 45;
|
||
|
||
-- Les champs chiffrés doivent contenir des données base64
|
||
-- encrypted_name devrait ressembler à : "eyJpdiI6Ij..."
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 3️⃣ : Utilisateurs de l'entité
|
||
|
||
**Tables concernées :**
|
||
- `users` (filtrés par `fk_entite = 45`)
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "users"
|
||
}
|
||
```
|
||
|
||
**Mappings critiques :**
|
||
- [ ] `libelle` → `encrypted_name`
|
||
- [ ] `username` → `encrypted_user_name` (chiffrement recherchable)
|
||
- [ ] `userpswd` ou `userpass` → `user_pass_hash`
|
||
- [ ] `prenom` → `first_name`
|
||
- [ ] `nom_tournee` → `sect_name`
|
||
- [ ] `telephone` → `encrypted_phone`
|
||
- [ ] `mobile` → `encrypted_mobile`
|
||
- [ ] `email` → `encrypted_email`
|
||
|
||
**Tests fonctionnels :**
|
||
```http
|
||
# Lister les utilisateurs de l'entité
|
||
GET /api/users?fk_entite=45
|
||
|
||
# Tester un login avec un utilisateur migré
|
||
POST /api/login
|
||
{
|
||
"username": "j.dupont",
|
||
"password": "MotDePasseOriginal123"
|
||
}
|
||
```
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Comparer les counts
|
||
SELECT COUNT(*) FROM geosector.users WHERE fk_entite = 45;
|
||
SELECT COUNT(*) FROM geo_app.users WHERE fk_entite = 45;
|
||
|
||
-- Vérifier les hash de mots de passe (doivent être identiques)
|
||
SELECT u1.rowid, u1.userpswd, u2.user_pass_hash
|
||
FROM geosector.users u1
|
||
JOIN geo_app.users u2 ON u1.rowid = u2.id
|
||
WHERE u1.fk_entite = 45
|
||
LIMIT 5;
|
||
```
|
||
|
||
**⚠️ Point critique :**
|
||
- Les mots de passe doivent être migrés **tels quels** (hash déjà fait)
|
||
- Ne PAS re-hasher les mots de passe
|
||
- Vérifier que le login fonctionne avec les anciens identifiants
|
||
|
||
---
|
||
|
||
#### Phase 4️⃣ : Opérations de l'entité
|
||
|
||
**Tables concernées :**
|
||
- `operations` (filtrées par `fk_entite = 45`)
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "operations",
|
||
"options": {
|
||
"limit": 0 // 0 = toutes les opérations (pas de limite à 3)
|
||
}
|
||
}
|
||
```
|
||
|
||
**Mappings :**
|
||
- [ ] `rowid` → `id`
|
||
- [ ] `date_creat` → `created_at`
|
||
- [ ] `date_modif` → `updated_at`
|
||
- [ ] `active` → `chk_active`
|
||
|
||
**Tests fonctionnels :**
|
||
```http
|
||
# Lister les opérations de l'entité
|
||
GET /api/operations?fk_entite=45
|
||
|
||
# Récupérer une opération spécifique
|
||
GET /api/operations/{operation_id}
|
||
```
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Comparer les counts
|
||
SELECT COUNT(*) FROM geosector.operations WHERE fk_entite = 45;
|
||
SELECT COUNT(*) FROM geo_app.operations WHERE fk_entite = 45;
|
||
|
||
-- Vérifier les 3 dernières opérations
|
||
SELECT id, libelle, date_deb, date_fin
|
||
FROM geo_app.operations
|
||
WHERE fk_entite = 45
|
||
ORDER BY id DESC
|
||
LIMIT 3;
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 5️⃣ : Secteurs des opérations
|
||
|
||
**Tables concernées :**
|
||
- `sectors` + `ope_users_sectors` → `ope_sectors`
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "ope_sectors"
|
||
}
|
||
```
|
||
|
||
**Logique spécifique :**
|
||
- Fusion de `sectors.rowid` → `ope_sectors.fk_old_sector`
|
||
- Génération de nouveaux `ope_sectors.id` (auto-increment)
|
||
- Création d'un mapping `old_id → new_id` pour les étapes suivantes
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Vérifier le mapping des secteurs
|
||
SELECT os.id, os.fk_old_sector, os.libelle, os.fk_operation
|
||
FROM geo_app.ope_sectors os
|
||
JOIN geo_app.operations o ON os.fk_operation = o.id
|
||
WHERE o.fk_entite = 45
|
||
ORDER BY os.fk_operation, os.id;
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 6️⃣ : Adresses des secteurs
|
||
|
||
**Tables concernées :**
|
||
- `sectors_adresses`
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "sectors_adresses"
|
||
}
|
||
```
|
||
|
||
**Mappings :**
|
||
- [ ] Utilisation du mapping `fk_old_sector → new_id` créé en Phase 5
|
||
- [ ] Ajout de colonnes OSM avec valeurs par défaut
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Comparer les counts
|
||
SELECT COUNT(*) FROM geosector.sectors_adresses sa
|
||
JOIN geosector.sectors s ON sa.fk_sector = s.rowid
|
||
JOIN geosector.ope_users_sectors ous ON ous.fk_sector = s.rowid
|
||
JOIN geosector.operations o ON ous.fk_operation = o.rowid
|
||
WHERE o.fk_entite = 45;
|
||
|
||
-- Vérifier dans la cible
|
||
SELECT COUNT(*) FROM geo_app.sectors_adresses sa
|
||
JOIN geo_app.ope_sectors os ON sa.fk_sector = os.id
|
||
JOIN geo_app.operations o ON os.fk_operation = o.id
|
||
WHERE o.fk_entite = 45;
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 7️⃣ : Associations opérations-utilisateurs
|
||
|
||
**Tables concernées :**
|
||
- `ope_users`
|
||
- `ope_users_sectors`
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "ope_users"
|
||
}
|
||
```
|
||
|
||
**⚠️ Point d'attention :**
|
||
- Nouveaux champs dans cible : `fk_role`, `first_name`, `encrypted_name`, `sect_name`
|
||
- **Solution recommandée** : Enrichir avec les données depuis `users` si disponibles
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Vérifier les associations
|
||
SELECT ou.id, ou.fk_operation, ou.fk_user, u.encrypted_user_name
|
||
FROM geo_app.ope_users ou
|
||
JOIN geo_app.users u ON ou.fk_user = u.id
|
||
JOIN geo_app.operations o ON ou.fk_operation = o.id
|
||
WHERE o.fk_entite = 45
|
||
LIMIT 10;
|
||
```
|
||
|
||
---
|
||
|
||
#### Phase 8️⃣ : Passages (Données critiques)
|
||
|
||
**Tables concernées :**
|
||
- `ope_pass`
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "ope_pass",
|
||
"options": {
|
||
"batch_size": 5000 // Migration par lots de 5000
|
||
}
|
||
}
|
||
```
|
||
|
||
**Mappings critiques :**
|
||
- [ ] `date_eve` → `passed_at`
|
||
- [ ] `libelle` → `encrypted_name` (chiffrement)
|
||
- [ ] `email` → `encrypted_email` (chiffrement recherchable + validation)
|
||
- [ ] `phone` → `encrypted_phone` (chiffrement)
|
||
- [ ] `recu` → `nom_recu`
|
||
- [ ] `fk_type` : transformation 8→5, 9→6
|
||
- [ ] `fk_sector` : ancien ID → nouvel ID via mapping Phase 5
|
||
|
||
**Tests fonctionnels :**
|
||
```http
|
||
# Compter les passages de l'entité
|
||
GET /api/passages/count?entity_id=45
|
||
|
||
# Récupérer les derniers passages
|
||
GET /api/passages?entity_id=45&limit=10&order=desc
|
||
```
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Comparer les counts par opération
|
||
SELECT o.id, o.libelle, COUNT(p.id) as nb_passages
|
||
FROM geo_app.operations o
|
||
LEFT JOIN geo_app.ope_pass p ON p.fk_operation = o.id
|
||
WHERE o.fk_entite = 45
|
||
GROUP BY o.id, o.libelle
|
||
ORDER BY o.id;
|
||
|
||
-- Vérifier les montants totaux
|
||
SELECT SUM(montant) FROM geosector.ope_pass
|
||
WHERE fk_operation IN (SELECT rowid FROM geosector.operations WHERE fk_entite = 45);
|
||
|
||
SELECT SUM(montant) FROM geo_app.ope_pass
|
||
WHERE fk_operation IN (SELECT id FROM geo_app.operations WHERE fk_entite = 45);
|
||
```
|
||
|
||
**⚠️ TRÈS IMPORTANT :**
|
||
- Cette phase peut prendre du temps (nombreux passages)
|
||
- Utiliser la migration par lots (5000 par 5000)
|
||
- Afficher une progression dans l'interface
|
||
- Vérifier que les emails sont valides avant chiffrement
|
||
|
||
---
|
||
|
||
#### Phase 9️⃣ : Historique et médias
|
||
|
||
**Tables concernées :**
|
||
- `ope_pass_histo`
|
||
- `medias`
|
||
|
||
**Endpoint :**
|
||
```http
|
||
POST /api/migrations/entity/step
|
||
{
|
||
"entity_id": 45,
|
||
"step": "ope_pass_histo"
|
||
}
|
||
```
|
||
|
||
**Mappings :**
|
||
- [ ] `ope_pass_histo` : Suppression du champ `fk_user` (n'existe plus)
|
||
- [ ] `medias` : Mapping `support_rowid` → `support_id`
|
||
|
||
**Vérifications SQL :**
|
||
```sql
|
||
-- Historique des passages
|
||
SELECT COUNT(*) FROM geo_app.ope_pass_histo h
|
||
JOIN geo_app.ope_pass p ON h.fk_pass = p.id
|
||
JOIN geo_app.operations o ON p.fk_operation = o.id
|
||
WHERE o.fk_entite = 45;
|
||
|
||
-- Médias de l'entité
|
||
SELECT COUNT(*) FROM geo_app.medias
|
||
WHERE support = 'entite' AND support_id = 45;
|
||
```
|
||
|
||
---
|
||
|
||
### Endpoints de gestion et monitoring
|
||
|
||
#### Endpoints de vérification
|
||
```http
|
||
# Statut de la migration d'une entité
|
||
GET /api/migrations/entity/{entity_id}/status
|
||
|
||
# Logs de migration
|
||
GET /api/migrations/entity/{entity_id}/logs
|
||
|
||
# Rapport de migration
|
||
GET /api/migrations/entity/{entity_id}/report
|
||
```
|
||
|
||
#### Endpoints de rollback
|
||
```http
|
||
# Annuler la migration d'une entité
|
||
DELETE /api/migrations/entity/{entity_id}
|
||
|
||
# Supprimer uniquement une étape
|
||
DELETE /api/migrations/entity/{entity_id}/step/{step_name}
|
||
```
|
||
|
||
#### Endpoints de comparaison
|
||
```http
|
||
# Comparer les données source vs cible
|
||
GET /api/migrations/entity/{entity_id}/compare
|
||
|
||
# Vérifier l'intégrité des données
|
||
GET /api/migrations/entity/{entity_id}/verify
|
||
```
|
||
|
||
---
|
||
|
||
### Checklist finale de validation
|
||
|
||
#### ✅ Données de base
|
||
- [ ] L'entité existe dans `geo_app.entites` avec toutes les données chiffrées
|
||
- [ ] Le déchiffrement fonctionne (appel `GET /api/entites/{id}`)
|
||
- [ ] Le logo de l'entité est présent (si applicable)
|
||
|
||
#### ✅ Utilisateurs
|
||
- [ ] Tous les utilisateurs de l'entité sont présents
|
||
- [ ] Le login fonctionne avec les anciens identifiants
|
||
- [ ] Les données chiffrées sont déchiffrables
|
||
- [ ] Les rôles sont corrects (`fk_role`)
|
||
|
||
#### ✅ Opérations
|
||
- [ ] Toutes les opérations sont migrées (pas de limite à 3)
|
||
- [ ] Les dates `date_deb` et `date_fin` sont correctes
|
||
- [ ] Les secteurs associés sont présents
|
||
|
||
#### ✅ Passages
|
||
- [ ] Le nombre total de passages correspond
|
||
- [ ] Les montants totaux correspondent
|
||
- [ ] Les emails chiffrés sont valides et déchiffrables
|
||
- [ ] Les `fk_type` sont corrects (vérifier transformations 8→5, 9→6)
|
||
|
||
#### ✅ Intégrité référentielle
|
||
- [ ] Aucune contrainte FK violée
|
||
- [ ] Tous les `fk_user` existent dans `users`
|
||
- [ ] Tous les `fk_operation` existent dans `operations`
|
||
- [ ] Tous les `fk_sector` existent dans `ope_sectors`
|
||
|
||
#### ✅ Tests fonctionnels
|
||
- [ ] Login avec un utilisateur de l'entité
|
||
- [ ] Affichage des opérations dans l'interface
|
||
- [ ] Affichage des passages dans une opération
|
||
- [ ] Création d'un nouveau passage (test post-migration)
|
||
- [ ] Génération d'un reçu fiscal (si applicable)
|
||
|
||
---
|
||
|
||
### Scripts SQL utiles
|
||
|
||
#### Comparer les totaux globaux
|
||
```sql
|
||
-- Script à exécuter après migration complète
|
||
SELECT
|
||
'Entité' as type,
|
||
(SELECT COUNT(*) FROM geosector.users_entites WHERE rowid = 45) as source,
|
||
(SELECT COUNT(*) FROM geo_app.entites WHERE id = 45) as cible
|
||
UNION ALL
|
||
SELECT
|
||
'Users',
|
||
(SELECT COUNT(*) FROM geosector.users WHERE fk_entite = 45),
|
||
(SELECT COUNT(*) FROM geo_app.users WHERE fk_entite = 45)
|
||
UNION ALL
|
||
SELECT
|
||
'Operations',
|
||
(SELECT COUNT(*) FROM geosector.operations WHERE fk_entite = 45),
|
||
(SELECT COUNT(*) FROM geo_app.operations WHERE fk_entite = 45)
|
||
UNION ALL
|
||
SELECT
|
||
'Passages',
|
||
(SELECT COUNT(*) FROM geosector.ope_pass WHERE fk_operation IN
|
||
(SELECT rowid FROM geosector.operations WHERE fk_entite = 45)),
|
||
(SELECT COUNT(*) FROM geo_app.ope_pass WHERE fk_operation IN
|
||
(SELECT id FROM geo_app.operations WHERE fk_entite = 45));
|
||
```
|
||
|
||
#### Vérifier l'intégrité des montants
|
||
```sql
|
||
-- Totaux des passages par type
|
||
SELECT p.fk_type, COUNT(*) as nb, SUM(p.montant) as total
|
||
FROM geo_app.ope_pass p
|
||
JOIN geo_app.operations o ON p.fk_operation = o.id
|
||
WHERE o.fk_entite = 45
|
||
GROUP BY p.fk_type;
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 TODO List
|
||
|
||
### 🔴 Priorité CRITIQUE (À faire IMMÉDIATEMENT)
|
||
|
||
- [ ] **Créer l'endpoint API de migration**
|
||
- Fichier : `src/Controllers/MigrationController.php`
|
||
- Service : `src/Services/MigrationService.php`
|
||
- Routes : Ajouter dans `index.php`
|
||
- Action : Implémenter la logique de migration par étape
|
||
|
||
- [ ] **Configurer la connexion à la base source**
|
||
- Fichier : `src/Config/AppConfig.php`
|
||
- Action : Ajouter les paramètres de connexion à `geosector` via tunnel SSH
|
||
|
||
### ⚠️ Priorité HAUTE (Cette semaine)
|
||
|
||
- [ ] **Externaliser configuration sensible**
|
||
- Créer `scripts/.env.example`
|
||
- Parser `.env` dans `config.php`
|
||
- Ajouter `scripts/.env` au `.gitignore`
|
||
- Mettre à jour documentation
|
||
|
||
- [ ] **Paramétrer limite opérations**
|
||
- Fichier : `scripts/php/migrate_operations.php`
|
||
- Ajouter argument CLI `--limit-operations=N`
|
||
- Documenter dans `--help`
|
||
- Valeur par défaut : 0 (toutes)
|
||
|
||
- [ ] **Tests de chiffrement optionnels**
|
||
- Fichier : `scripts/php/migrate_users.php`
|
||
- Créer flag `--test-encryption`
|
||
- Désactiver par défaut
|
||
|
||
### 📊 Priorité MOYENNE (Ce mois-ci)
|
||
|
||
- [ ] **Uniformiser logging**
|
||
- Remplacer tous les `echo` par `logOperation()`
|
||
- Ajouter niveaux : DEBUG, INFO, WARNING, ERROR
|
||
- Format uniforme
|
||
|
||
- [ ] **Script de vérification post-migration**
|
||
- Créer `scripts/php/verify_migration.php`
|
||
- Comparer counts source vs cible
|
||
- Vérifier intégrité référentielle
|
||
- Générer rapport HTML
|
||
|
||
- [ ] **Remplacer dates `0000-00-00` par `NULL`**
|
||
- Fichiers concernés : `migrate_operations.php`, `sectors_adresses.php`
|
||
- Remplacer par `NULL` pour compatibilité MariaDB strict mode
|
||
|
||
- [ ] **Documentation utilisateur**
|
||
- Guide pas-à-pas migration complète
|
||
- Prérequis (tunnel SSH, accès bases)
|
||
- Procédure de rollback
|
||
- FAQ troubleshooting
|
||
|
||
### 💡 Priorité BASSE (Nice to have)
|
||
|
||
- [ ] **Option transaction globale**
|
||
- Flag `--transactional` dans `migrate.php`
|
||
- Englober toutes migrations dans 1 transaction
|
||
- Warning si échec = rollback complet
|
||
|
||
- [ ] **Barre de progression**
|
||
- Utiliser library CLI (ex: `symfony/console`)
|
||
- Afficher progression en temps réel
|
||
- ETA par table
|
||
|
||
- [ ] **Mode dry-run**
|
||
- Flag `--dry-run` pour simuler sans écrire
|
||
- Afficher ce qui serait fait
|
||
- Utile pour tests
|
||
|
||
- [ ] **Export/Import rapide**
|
||
- Alternative : dump SQL + sed pour mapping IDs
|
||
- Plus rapide pour gros volumes
|
||
- Comparer perf vs PHP
|
||
|
||
- [ ] **Tests automatisés**
|
||
- PHPUnit tests pour chaque script
|
||
- Mock des connexions DB
|
||
- CI/CD avec GitHub Actions
|
||
|
||
---
|
||
|
||
## 📚 Références
|
||
|
||
### Fichiers clés
|
||
- **Schéma DB** : `docs/geo_app.sql`
|
||
- **Configuration** : `scripts/config.php`
|
||
- **Orchestrateur** : `scripts/php/migrate.php`
|
||
- **Logs** : `scripts/logs/migration_YYYY-MM-DD.log`
|
||
|
||
### Ordre d'exécution (défini dans `migrate.php:64-85`)
|
||
```
|
||
x_devises → x_entites_types → x_types_passages → x_types_reglements →
|
||
x_users_roles → x_pays → x_regions → x_departements → x_villes →
|
||
entites → users → operations → ope_sectors → sectors_adresses →
|
||
ope_users → ope_users_sectors → ope_pass → ope_pass_histo → medias
|
||
```
|
||
|
||
### Commandes utiles
|
||
```bash
|
||
# Migration complète
|
||
php scripts/php/migrate.php
|
||
|
||
# Migration d'une table spécifique
|
||
php scripts/php/migrate.php users
|
||
|
||
# Migration avec truncate
|
||
php scripts/php/migrate.php users --truncate
|
||
|
||
# Afficher l'aide
|
||
php scripts/php/migrate.php --help
|
||
|
||
# Consulter les logs
|
||
tail -f scripts/logs/migration_$(date +%Y-%m-%d).log
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Corrections Critiques Appliquées (10/10/2025)
|
||
|
||
### Correction #15 : Ajout de contraintes UNIQUE pour éviter les doublons
|
||
|
||
**Problème identifié** : Les tables `ope_users` et `ope_users_sectors` n'avaient PAS de contrainte UNIQUE sur leurs combinaisons de FK, permettant des doublons massifs lors de la migration.
|
||
|
||
**Diagnostic** :
|
||
- Table `ope_users` : Possibilité de centaines de doublons pour la même paire (fk_operation, fk_user)
|
||
- Table `ope_users_sectors` : Risque de doublons sur (fk_operation, fk_user, fk_sector)
|
||
- Le `ON DUPLICATE KEY UPDATE` dans le script PHP ne fonctionnait pas car aucune contrainte UNIQUE n'existait
|
||
|
||
**Solution appliquée** :
|
||
|
||
1. **Mise à jour de `geo_app_structure.sql`** (structure de référence) :
|
||
- Ajout de `UNIQUE KEY idx_operation_user (fk_operation, fk_user)` sur `ope_users` (ligne 403)
|
||
- Ajout de `UNIQUE KEY idx_operation_user_sector (fk_operation, fk_user, fk_sector)` sur `ope_users_sectors` (ligne 430)
|
||
|
||
2. **Correction du code PHP** dans `migrate_from_backup.php` :
|
||
```php
|
||
// AVANT (INCORRECT - créait des doublons)
|
||
$sql = "SELECT ou.rowid, ou.fk_operation, ou.fk_user, ...
|
||
INSERT INTO ope_users (id, fk_operation, fk_user, ...) VALUES (:id, ...
|
||
|
||
// APRÈS (CORRECT - évite les doublons)
|
||
$sql = "SELECT DISTINCT ou.fk_operation, ou.fk_user, ... // Pas de rowid
|
||
INSERT INTO ope_users (fk_operation, fk_user, ...) VALUES (... // Pas d'id, auto-increment
|
||
```
|
||
|
||
3. **Application sur les bases de données** :
|
||
```sql
|
||
-- Vérifier les doublons existants
|
||
SELECT fk_operation, fk_user, COUNT(*) as count
|
||
FROM ope_users GROUP BY fk_operation, fk_user HAVING count > 1;
|
||
|
||
-- Ajouter les contraintes UNIQUE
|
||
ALTER TABLE ope_users
|
||
ADD UNIQUE KEY `idx_operation_user` (`fk_operation`, `fk_user`);
|
||
|
||
ALTER TABLE ope_users_sectors
|
||
ADD UNIQUE KEY `idx_operation_user_sector` (`fk_operation`, `fk_user`, `fk_sector`);
|
||
|
||
-- Vérifier que les contraintes sont bien créées
|
||
SHOW INDEX FROM ope_users WHERE Key_name = 'idx_operation_user';
|
||
SHOW INDEX FROM ope_users_sectors WHERE Key_name = 'idx_operation_user_sector';
|
||
```
|
||
|
||
**Impact** :
|
||
- ✅ Empêche définitivement les doublons dans `ope_users` et `ope_users_sectors`
|
||
- ✅ Le `ON DUPLICATE KEY UPDATE` fonctionne désormais correctement
|
||
- ✅ Migration idempotente (peut être relancée sans créer de duplicatas)
|
||
|
||
**Fichiers modifiés** :
|
||
- `scripts/php/geo_app_structure.sql` - Structure de référence mise à jour (lignes 403, 430)
|
||
- `scripts/php/migrate_from_backup.php` - Code corrigé avec SELECT DISTINCT (lignes 1125-1191)
|
||
|
||
**À appliquer sur les environnements** :
|
||
```bash
|
||
# DEV (dva_geo sur maria3/IN3)
|
||
mysql -h 13.23.33.4 -u dva_geo_user -p'CBq9tKHj6PGPZuTmAHV7' dva_geo
|
||
# Puis exécuter les ALTER TABLE ci-dessus
|
||
|
||
# REC (rca_geo sur maria3/IN3)
|
||
mysql -h 13.23.33.4 -u rca_geo_user -p'UPf3C0cQ805LypyM71iW' rca_geo
|
||
# Puis exécuter les ALTER TABLE ci-dessus
|
||
|
||
# PROD (pra_geo sur maria4/IN4)
|
||
mysql -h 13.23.33.4 -u pra_geo_user -p'd2jAAGGWi8fxFrWgXjOA' pra_geo
|
||
# Puis exécuter les ALTER TABLE ci-dessus
|
||
```
|
||
|
||
**Pour re-migrer après correction** :
|
||
```bash
|
||
# Supprimer les données de l'entité avant re-migration
|
||
php scripts/php/migrate_from_backup.php \
|
||
--source-db=geosector_20251008 \
|
||
--mode=entity \
|
||
--entity-id=5 \
|
||
--delete-before
|
||
```
|
||
|
||
---
|
||
|
||
**Dernière mise à jour** : 2025-10-10
|
||
**Auteur de l'analyse** : Claude Code
|
||
**Version** : 1.1
|