voyage garage
Deploy HouseHub / deploy (push) Successful in 2s

This commit is contained in:
2026-06-20 12:50:40 +02:00
parent 03a97df8f1
commit 557a1451de
10 changed files with 537 additions and 565 deletions
+29 -162
View File
@@ -1,70 +1,11 @@
<?php
/**
* Script de migration global HouseHub OS (Multi-tenant)
* Auto-Sync dynamique basé sur schema_family.sql + Migration des données
* À exécuter via le navigateur : http://localhost:8083/migrate.php
*/
require_once __DIR__ . '/includes/meta_db.php';
$db_host = getenv('DB_HOST') ?: '127.0.0.1';
$db_user = getenv('DB_USER') ?: 'househub';
$db_pass = getenv('DB_PASS') ?: 'househub_dev';
echo "<h1>🚀 Début de la migration Multi-Tenant (Auto-Sync)</h1>";
// ---------------------------------------------------------
// 1. LECTURE ET PARSING DU FICHIER SCHEMA_FAMILY.SQL
// ---------------------------------------------------------
$schemaPath = __DIR__ . '/schema_family.sql'; // Modifie si rangé dans /docker/
if (!file_exists($schemaPath)) {
$schemaPath = __DIR__ . '/docker/schema_family.sql';
}
if (!file_exists($schemaPath)) {
die("❌ Impossible de trouver le fichier schema_family.sql");
}
$sqlContent = file_get_contents($schemaPath);
/**
* Analyse le code SQL pour en extraire la structure [Table => [Colonnes]]
*/
function parseExpectedSchema($sql) {
$schema = [];
// Récupère tout ce qui se trouve entre CREATE TABLE (...) ENGINE
preg_match_all('/CREATE TABLE (?:IF NOT EXISTS )?`?([a-zA-Z0-9_]+)`?\s*\((.*?)\)\s*ENGINE/si', $sql, $tableMatches, PREG_SET_ORDER);
foreach($tableMatches as $match) {
$tableName = $match[1];
$body = $match[2];
// Sépare les lignes par virgule, en ignorant les virgules entre parenthèses (ex: DECIMAL(10,2) ou ENUM('a','b'))
$lines = preg_split('/,(?![^\(]*\))/', $body);
$columns = [];
foreach($lines as $line) {
$line = trim($line);
if (empty($line)) continue;
// On ignore la déclaration des clés, contraintes et index
if (preg_match('/^(PRIMARY KEY|UNIQUE KEY|FOREIGN KEY|KEY|INDEX|FULLTEXT KEY|CONSTRAINT)\b/i', $line)) {
continue;
}
// Extrait le nom de la colonne et sa définition SQL
if (preg_match('/^`?([a-zA-Z0-9_]+)`?\s+(.*)$/i', $line, $colMatch)) {
$colName = $colMatch[1];
$colDef = rtrim($colMatch[2], ',');
$columns[$colName] = $colDef;
}
}
$schema[$tableName] = $columns;
}
return $schema;
}
$expectedSchema = parseExpectedSchema($sqlContent);
echo "️ Modèle SQL chargé et analysé : <b>" . count($expectedSchema) . " tables</b> détectées.<hr>";
echo "<h1>🗺️ Migration : Refonte Modèle Voyages</h1><ul>";
try {
$stmt = $meta_pdo->query("SELECT db_name, name FROM families WHERE is_active = 1");
@@ -72,111 +13,37 @@ try {
foreach ($families as $family) {
$dbName = $family['db_name'];
echo "<h2>🏡 Famille : {$family['name']} ($dbName)</h2>";
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$dbName;charset=utf8mb4",
$db_user, $db_pass,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// ---------------------------------------------------------
// 2. EXÉCUTION DU SQL (CRÉATION TABLES MANQUANTES + DONNÉES PAR DÉFAUT)
// ---------------------------------------------------------
try {
$pdo->exec($sqlContent);
echo "✅ Structure de base validée (les tables manquantes ont été créées).<br>";
} catch (PDOException $e) {
echo "⚠️ Avertissement SQL brut : " . $e->getMessage() . "<br>";
}
// ---------------------------------------------------------
// 3. DIFFING (AJOUT DYNAMIQUE DES COLONNES MANQUANTES)
// ---------------------------------------------------------
$colsAdded = 0;
foreach ($expectedSchema as $table => $expectedCols) {
$stmtCol = $pdo->query("SHOW COLUMNS FROM `$table`");
$existingCols = $stmtCol->fetchAll(PDO::FETCH_COLUMN);
foreach ($expectedCols as $colName => $colDef) {
if (!in_array($colName, $existingCols)) {
$pdo->exec("ALTER TABLE `$table` ADD COLUMN `$colName` $colDef");
echo " Nouvelle colonne ajoutée : <b>$table.$colName</b><br>";
$colsAdded++;
}
}
}
if ($colsAdded === 0) {
echo "✅ Toutes les colonnes de toutes les tables sont déjà à jour.<br>";
}
// ---------------------------------------------------------
// 4. MIGRATION DES DONNÉES (TRANSFORMATIONS HISTORIQUES)
// ---------------------------------------------------------
echo "<i>🔄 Exécution des transformations de données héritées...</i><br>";
$pdo->exec("ALTER TABLE pf_events MODIFY id INT NOT NULL AUTO_INCREMENT");
$pdo->exec("ALTER TABLE pf_leaves MODIFY id INT NOT NULL AUTO_INCREMENT");
$pdo = new PDO("mysql:host=$db_host;dbname=$dbName;charset=utf8mb4", $db_user, $db_pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
// A. Changement de types spécifiques (Car le parseur n'ajoute que les colonnes manquantes)
$pdo->exec("ALTER TABLE pf_budget_items MODIFY category VARCHAR(100) DEFAULT NULL");
// B. Migration de l'historique calendrier (Enfants & Helpers)
$helperId = $pdo->query("SELECT id FROM pf_people WHERE role = 'helper' LIMIT 1")->fetchColumn();
if ($helperId) {
$pdo->exec("UPDATE pf_events SET event_type = 'HELPER_OFF', person_id = $helperId WHERE event_type = 'OFF_CAROLE'");
$pdo->exec("UPDATE pf_events SET event_type = 'HELPER_EXTRA', person_id = $helperId WHERE event_type = 'EXTRA_OFF_CAROLE'");
}
$kidId = $pdo->query("SELECT id FROM pf_people WHERE role IN ('child', 'enfant') ORDER BY id ASC LIMIT 1")->fetchColumn();
if ($kidId) {
$pdo->exec("UPDATE pf_events SET event_type = 'CHILD_SICK', person_id = $kidId WHERE event_type = 'PEP_SICK'");
}
// C. Mapping Intelligent du Budget (is_estimate)
$itemsMigres = 0;
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'FIXED' WHERE category = 'expense' AND is_estimate = 0");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'FUEL' WHERE category = 'expense' AND is_estimate = 1 AND (mapping_keywords LIKE '%ESSENCE%' OR name LIKE '%gasolina%')");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'SCHOOL' WHERE category = 'expense' AND is_estimate = 1 AND (mapping_keywords LIKE '%ESCOLA%' OR mapping_keywords LIKE '%PARASCOL%' OR name LIKE '%escola%')");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'FMCG' WHERE category = 'expense' AND is_estimate = 1 AND (mapping_keywords LIKE '%FMCG%' OR name LIKE '%F&B%')");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'AUTRES' WHERE category = 'expense'");
$itemsMigres += $pdo->exec("UPDATE pf_budget_items SET category = 'INCOME' WHERE category = 'income'");
// D. Nettoyage des virgules orphelines dans mapping_keywords
$budgetCodes = ['INCOME', 'FMCG', 'FUEL', 'SCHOOL', 'HEALTH', 'FIXED', 'SAVINGS', 'AUTRES'];
foreach ($budgetCodes as $code) {
$pdo->exec("UPDATE pf_budget_items SET mapping_keywords = REPLACE(mapping_keywords, '$code', '') WHERE mapping_keywords LIKE '%$code%'");
}
$pdo->exec("UPDATE pf_budget_items SET mapping_keywords = TRIM(BOTH ',' FROM REPLACE(REPLACE(mapping_keywords, ' ', ''), ',,', ','))");
// E. Migration de l'historique des dépenses et des règles d'import
$budgetMapping = [
'Income' => 'INCOME',
'FMCG' => 'FMCG',
'Essence' => 'FUEL',
'School' => 'SCHOOL',
'Frais' => 'FIXED',
'LivretA' => 'SAVINGS',
'Autres' => 'AUTRES'
];
$stmtExp = $pdo->prepare("UPDATE pf_expenses SET category = ? WHERE category = ?");
$stmtRules = $pdo->prepare("UPDATE pf_import_rules SET category = ? WHERE category = ?");
foreach ($budgetMapping as $old => $newCode) {
$stmtExp->execute([$newCode, $old]);
$stmtRules->execute([$newCode, $old]);
}
echo "✅ Transformations Data terminées ($itemsMigres prévisions remappées).<hr>";
} catch (PDOException $e) {
echo "❌ Erreur sur la base $dbName : " . $e->getMessage() . "<hr>";
// 1. Mise à jour pf_holidays
$pdo->exec("ALTER TABLE pf_holidays ADD COLUMN return_step_id INT DEFAULT NULL");
// 2. Mise à jour pf_holidays_items
$pdo->exec("ALTER TABLE pf_holidays_items ADD COLUMN step_type VARCHAR(20) DEFAULT 'stop'");
$pdo->exec("ALTER TABLE pf_holidays_items ADD COLUMN expense_context VARCHAR(20) DEFAULT NULL");
// 3. Migration des données (is_return -> return_step_id)
// On cherche la première étape cochée "is_return" et on l'assigne au voyage
$pdo->exec("
UPDATE pf_holidays h
SET return_step_id = (
SELECT id FROM pf_holidays_items i
WHERE i.holiday_id = h.id AND i.is_return = 1 AND i.location_name IS NOT NULL
LIMIT 1
)
");
// 4. Nettoyage de l'ancienne colonne
$pdo->exec("ALTER TABLE pf_holidays_items DROP COLUMN is_return");
echo "<li>✅ {$family['name']} : Schéma Voyages mis à jour !</li>";
} catch (\PDOException $e) {
echo "<li>⚠️ {$family['name']} : " . $e->getMessage() . "</li>";
}
} // FIN DE LA BOUCLE FOREACH FAMILLES
echo "<h1>🎉 Synchronisation et Migration terminées avec succès !</h1>";
}
echo "</ul><h2>🎉 Migration terminée !</h2>";
} catch (Exception $e) {
die("Erreur fatale Meta DB : " . $e->getMessage());
die("Erreur fatale : " . $e->getMessage());
}
?>