# 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