feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles

- 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>
This commit is contained in:
pierre
2025-11-09 18:26:27 +01:00
parent 21657a3820
commit 2f5946a184
812 changed files with 142105 additions and 25992 deletions

View File

@@ -0,0 +1,312 @@
<?php
/**
* Migration des opérations complètes
*
* Orchestre la migration d'une opération avec tous ses utilisateurs,
* secteurs, passages et médias. Utilise UserMigrator et SectorMigrator.
*/
class OperationMigrator
{
private PDO $sourceDb;
private PDO $targetDb;
private MigrationLogger $logger;
private UserMigrator $userMigrator;
private SectorMigrator $sectorMigrator;
/**
* Constructeur
*
* @param PDO $sourceDb Connexion source
* @param PDO $targetDb Connexion cible
* @param MigrationLogger $logger Logger
* @param UserMigrator $userMigrator Migrator d'utilisateurs
* @param SectorMigrator $sectorMigrator Migrator de secteurs
*/
public function __construct(
PDO $sourceDb,
PDO $targetDb,
MigrationLogger $logger,
UserMigrator $userMigrator,
SectorMigrator $sectorMigrator
) {
$this->sourceDb = $sourceDb;
$this->targetDb = $targetDb;
$this->logger = $logger;
$this->userMigrator = $userMigrator;
$this->sectorMigrator = $sectorMigrator;
}
/**
* Récupère les opérations à migrer pour une entité
* - 1 opération active
* - 2 dernières opérations inactives avec au moins 10 passages effectués
*
* @param int $entityId ID de l'entité
* @return array Liste des IDs d'opérations à migrer
*/
public function getOperationsToMigrate(int $entityId): array
{
$operationIds = [];
// 1. Récupérer l'opération active (pour vérification)
$stmt = $this->sourceDb->prepare("
SELECT rowid
FROM operations
WHERE fk_entite = :entity_id AND active = 1
LIMIT 1
");
$stmt->execute([':entity_id' => $entityId]);
$activeOp = $stmt->fetch(PDO::FETCH_COLUMN);
// 2. Récupérer les 2 dernières opérations inactives avec >= 10 passages effectués
// ORDER BY DESC pour avoir les plus récentes, puis on inverse
$stmt = $this->sourceDb->prepare("
SELECT o.rowid, COUNT(p.rowid) as nb_passages
FROM operations o
LEFT JOIN ope_pass p ON p.fk_operation = o.rowid AND p.fk_type = 1
WHERE o.fk_entite = :entity_id
AND o.active = 0
" . ($activeOp ? "AND o.rowid != :active_id" : "") . "
GROUP BY o.rowid
HAVING nb_passages >= 10
ORDER BY o.rowid DESC
LIMIT 2
");
$params = [':entity_id' => $entityId];
if ($activeOp) {
$params[':active_id'] = $activeOp;
}
$stmt->execute($params);
$inactiveOps = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Inverser pour avoir l'ordre chronologique (plus ancienne → plus récente)
$inactiveOps = array_reverse($inactiveOps);
foreach ($inactiveOps as $op) {
$operationIds[] = $op['rowid'];
$this->logger->info("✓ Opération inactive trouvée: {$op['rowid']} ({$op['nb_passages']} passages)");
}
// 3. Ajouter l'opération active EN DERNIER
if ($activeOp) {
$operationIds[] = $activeOp;
$this->logger->info("✓ Opération active trouvée: {$activeOp}");
}
$this->logger->info("📊 Total: " . count($operationIds) . " opération(s) à migrer");
return $operationIds;
}
/**
* Migre une opération complète avec tous ses utilisateurs et secteurs
*
* @param int $oldOperationId ID de l'opération dans l'ancienne base
* @return array|null Tableau de statistiques ou null en cas d'erreur
*/
public function migrateOperation(int $oldOperationId): ?array
{
$this->logger->separator();
$this->logger->info("🔄 Migration de l'opération ID: {$oldOperationId}");
try {
// 1. Récupérer l'opération source
$stmt = $this->sourceDb->prepare("
SELECT * FROM operations
WHERE rowid = :id
");
$stmt->execute([':id' => $oldOperationId]);
$operation = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$operation) {
$this->logger->warning("Opération {$oldOperationId} non trouvée");
return null;
}
// 2. Créer l'opération dans la nouvelle base
$newOperationId = $this->createOperation($operation);
if (!$newOperationId) {
return null;
}
$this->logger->success("✓ Opération créée avec ID: {$newOperationId}");
// 3. Migrer les utilisateurs de l'opération
// Pour opération active : tous les users actifs de l'entité
// Pour opération inactive : uniquement ceux dans ope_users_sectors
$entityId = (int)$operation['fk_entite'];
$isActiveOperation = (int)$operation['active'] === 1;
$userResult = $this->userMigrator->migrateOperationUsers(
$oldOperationId,
$newOperationId,
$entityId,
$isActiveOperation
);
$userMapping = $userResult['mapping'];
$usersCount = $userResult['count'];
if (empty($userMapping)) {
$this->logger->warning("Aucun utilisateur migré, abandon de l'opération {$oldOperationId}");
return null;
}
// 4. Récupérer les secteurs DISTINCTS de l'opération
$stmt = $this->sourceDb->prepare("
SELECT DISTINCT fk_sector
FROM ope_users_sectors
WHERE fk_operation = :operation_id AND active = 1
");
$stmt->execute([':operation_id' => $oldOperationId]);
$sectors = $stmt->fetchAll(PDO::FETCH_COLUMN);
$this->logger->info("📍 " . count($sectors) . " secteur(s) distinct(s) à migrer");
// 5. Migrer chaque secteur et collecter les stats
$sectorsDetail = [];
$totalPassages = 0;
foreach ($sectors as $oldSectorId) {
$sectorStats = $this->sectorMigrator->migrateSector(
$oldOperationId,
$newOperationId,
$oldSectorId,
$userMapping
);
if ($sectorStats) {
$sectorsDetail[] = $sectorStats;
$totalPassages += $sectorStats['passages'];
}
}
// 6. Migrer les médias de l'opération (support='operations')
$this->migrateOperationMedias($oldOperationId, $newOperationId);
$this->logger->success("✅ Migration de l'opération {$oldOperationId} terminée");
// 7. Retourner les statistiques
return [
'id' => $newOperationId,
'name' => $operation['libelle'],
'users' => $usersCount,
'sectors' => count($sectorsDetail),
'total_passages' => $totalPassages,
'sectors_detail' => $sectorsDetail
];
} catch (Exception $e) {
$this->logger->error("❌ Erreur migration opération {$oldOperationId}: " . $e->getMessage());
return null;
}
}
/**
* Crée une opération dans la nouvelle base
*
* @param array $operation Données de l'opération
* @return int|null ID de la nouvelle opération ou null en cas d'erreur
*/
private function createOperation(array $operation): ?int
{
try {
$stmt = $this->targetDb->prepare("
INSERT INTO operations (
fk_entite, libelle, date_deb, date_fin,
chk_distinct_sectors,
created_at, fk_user_creat, updated_at, fk_user_modif, chk_active
) VALUES (
:fk_entite, :libelle, :date_deb, :date_fin,
:chk_distinct_sectors,
:created_at, :fk_user_creat, :updated_at, :fk_user_modif, :chk_active
)
");
$stmt->execute([
':fk_entite' => $operation['fk_entite'],
':libelle' => $operation['libelle'],
':date_deb' => $operation['date_deb'],
':date_fin' => $operation['date_fin'],
':chk_distinct_sectors' => $operation['chk_distinct_sectors'],
':created_at' => $operation['date_creat'],
':fk_user_creat' => $operation['fk_user_creat'],
':updated_at' => $operation['date_modif'],
':fk_user_modif' => $operation['fk_user_modif'] ?? 0,
':chk_active' => $operation['active']
]);
return (int)$this->targetDb->lastInsertId();
} catch (Exception $e) {
$this->logger->error("❌ Erreur création opération: " . $e->getMessage());
return null;
}
}
/**
* Migre les médias d'une opération
*
* @param int $oldOperationId ID ancienne opération
* @param int $newOperationId ID nouvelle opération
* @return int Nombre de médias migrés
*/
private function migrateOperationMedias(int $oldOperationId, int $newOperationId): int
{
$stmt = $this->sourceDb->prepare("
SELECT * FROM medias
WHERE support = 'operations' AND support_rowid = :operation_id
");
$stmt->execute([':operation_id' => $oldOperationId]);
$medias = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($medias)) {
return 0;
}
$count = 0;
foreach ($medias as $media) {
$stmt = $this->targetDb->prepare("
INSERT INTO medias (
dir0, dir1, dir2, support, support_rowid,
fichier, type_fichier, description, position,
hauteur, largeur, niveaugris,
created_at, fk_user_creat, updated_at, fk_user_modif
) VALUES (
:dir0, :dir1, :dir2, :support, :support_rowid,
:fichier, :type_fichier, :description, :position,
:hauteur, :largeur, :niveaugris,
:created_at, :fk_user_creat, :updated_at, :fk_user_modif
)
");
$stmt->execute([
':dir0' => $media['dir0'],
':dir1' => $media['dir1'],
':dir2' => $media['dir2'],
':support' => $media['support'],
':support_rowid' => $newOperationId,
':fichier' => $media['fichier'],
':type_fichier' => $media['type_fichier'],
':description' => $media['description'],
':position' => $media['position'],
':hauteur' => $media['hauteur'],
':largeur' => $media['largeur'],
':niveaugris' => $media['niveaugris'],
':created_at' => $media['date_creat'],
':fk_user_creat' => $media['fk_user_creat'],
':updated_at' => $media['date_modif'],
':fk_user_modif' => $media['fk_user_modif']
]);
$count++;
}
$this->logger->success("{$count} média(s) migré(s)");
return $count;
}
}