db = Database::getInstance(); $this->fileService = new FileService(); } /** * Génère un export Excel complet d'une opération * * @param int $operationId ID de l'opération * @param int $entiteId ID de l'entité * @param int|null $userId Filtrer par utilisateur (optionnel) * @return array Informations du fichier généré */ public function generateExcelExport(int $operationId, int $entiteId, ?int $userId = null): array { try { // Récupérer les données de l'opération $operationData = $this->getOperationData($operationId, $entiteId); if (!$operationData) { throw new Exception('Opération non trouvée'); } // Créer le dossier de destination $exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}/exports/excel"); LogService::log('exportDir', [ 'level' => 'warning', 'exportDir' => $exportDir, ]); // Générer le nom du fichier $timestamp = date('Ymd-His'); $userSuffix = $userId ? "-user{$userId}" : ''; $filename = "geosector-export-{$operationId}{$userSuffix}-{$timestamp}.xlsx"; $filepath = $exportDir . '/' . $filename; // Créer le spreadsheet $spreadsheet = new PhpOffice\PhpSpreadsheet\Spreadsheet(); // Insérer les données $this->createPassagesSheet($spreadsheet, $operationId, $userId); $this->createUsersSheet($spreadsheet, $operationId); $this->createSectorsSheet($spreadsheet, $operationId); $this->createUserSectorsSheet($spreadsheet, $operationId); // Supprimer la feuille par défaut (Worksheet) qui est créée automatiquement $defaultSheet = $spreadsheet->getSheetByName('Worksheet'); if ($defaultSheet) { $spreadsheet->removeSheetByIndex($spreadsheet->getIndex($defaultSheet)); } // Essayer d'abord le writer XLSX, sinon utiliser CSV try { $writer = new Xls($spreadsheet); $writer->save($filepath); } catch (Exception $e) { // Si XLSX échoue, utiliser CSV comme fallback $csvPath = str_replace('.xlsx', '.csv', $filepath); $csvWriter = new Csv($spreadsheet); $csvWriter->setDelimiter(';'); $csvWriter->setEnclosure('"'); $csvWriter->save($csvPath); // Mettre à jour les variables pour le CSV $filepath = $csvPath; $filename = str_replace('.xlsx', '.csv', $filename); LogService::log('Fallback vers CSV car XLSX a échoué', [ 'level' => 'warning', 'error' => $e->getMessage(), 'operationId' => $operationId ]); } // Appliquer les permissions sur le fichier $this->fileService->setFilePermissions($filepath); // Déterminer le type de fichier réellement généré $fileType = str_ends_with($filename, '.csv') ? 'csv' : 'xlsx'; // Enregistrer en base de données $mediaId = $this->fileService->saveToMediasTable($entiteId, $operationId, $filename, $filepath, $fileType, 'Export Excel opération - ' . $operationData['libelle']); LogService::log('Export Excel généré', [ 'level' => 'info', 'operationId' => $operationId, 'entiteId' => $entiteId, 'path' => $exportDir, 'filename' => $filename, 'mediaId' => $mediaId ]); return [ 'id' => $mediaId, 'filename' => $filename, 'path' => str_replace(getcwd() . '/', '', $filepath), 'size' => filesize($filepath), 'type' => 'excel' ]; } catch (Exception $e) { LogService::log('Erreur lors de la génération de l\'export Excel', [ 'level' => 'error', 'error' => $e->getMessage(), 'operationId' => $operationId, 'entiteId' => $entiteId ]); throw $e; } } /** * Génère un export JSON complet d'une opération (chiffré et compressé) * * @param int $operationId ID de l'opération * @param int $entiteId ID de l'entité * @param string $type Type d'export (auto, manual) * @return array Informations du fichier généré */ public function generateJsonExport(int $operationId, int $entiteId, string $type = 'manual'): array { try { // Récupérer toutes les données de l'opération $exportData = $this->collectOperationData($operationId, $entiteId); // Créer le dossier de destination $exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}/exports/json"); // Initialiser le service de chiffrement $backupService = new BackupEncryptionService(); // Générer le JSON original $jsonData = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); // Chiffrer et compresser les données $encryptedData = $backupService->encryptBackup($jsonData); // Générer le nom du fichier avec extension appropriée $timestamp = date('Ymd-His'); $filename = $backupService->generateBackupFilename($operationId, $timestamp, $type); $filepath = $exportDir . '/' . $filename; // Sauvegarder le fichier chiffré file_put_contents($filepath, $encryptedData); // Appliquer les permissions sur le fichier $this->fileService->setFilePermissions($filepath); // Obtenir les statistiques de compression $stats = $backupService->getCompressionStats($jsonData, $encryptedData); // Enregistrer en base de données avec le bon type MIME $mediaId = $this->fileService->saveToMediasTable( $entiteId, $operationId, $filename, $filepath, 'enc', "Sauvegarde chiffrée opération - {$type} - " . $exportData['operation']['libelle'], 'backup' ); LogService::log('Export JSON chiffré généré', [ 'level' => 'info', 'operationId' => $operationId, 'entiteId' => $entiteId, 'filename' => $filename, 'type' => $type, 'mediaId' => $mediaId, 'original_size' => $stats['original_size'], 'final_size' => $stats['final_size'], 'compression_ratio' => $stats['compression_ratio'] . '%', 'is_compressed' => $stats['is_compressed'], 'cipher' => $stats['cipher'] ]); return [ 'id' => $mediaId, 'filename' => $filename, 'path' => str_replace(getcwd() . '/', '', $filepath), 'size' => filesize($filepath), 'type' => 'encrypted_json', 'compression_stats' => $stats ]; } catch (Exception $e) { LogService::log('Erreur lors de la génération de l\'export JSON chiffré', [ 'level' => 'error', 'error' => $e->getMessage(), 'operationId' => $operationId, 'entiteId' => $entiteId ]); throw $e; } } /** * Crée la feuille des passages */ private function createPassagesSheet(Spreadsheet $spreadsheet, int $operationId, ?int $userId = null): void { $sheet = $spreadsheet->createSheet(); $sheet->setTitle('Passages'); // En-têtes $headers = [ 'ID_Passage', 'Date', 'Heure', 'Prénom', 'Nom', 'Tournée', 'Type', 'N°', 'Bis', 'Rue', 'Ville', 'Habitat', 'Donateur', 'Email', 'Tél', 'Montant', 'Règlement', 'Remarque', 'FK_User', 'FK_Sector', 'FK_Operation' ]; // Écrire les en-têtes $sheet->fromArray([$headers], null, 'A1'); // Récupérer les données des passages $sql = ' SELECT p.id, p.passed_at, p.fk_type, p.numero, p.rue_bis, p.rue, p.ville, p.fk_habitat, p.appt, p.niveau, p.encrypted_name, p.encrypted_email, p.encrypted_phone, p.montant, p.fk_type_reglement, p.remarque, p.fk_user, p.fk_sector, p.fk_operation, u.encrypted_name as user_name, u.first_name as user_first_name, u.sect_name, xtr.libelle as reglement_libelle FROM ope_pass p LEFT JOIN users u ON u.id = p.fk_user LEFT JOIN x_types_reglements xtr ON xtr.id = p.fk_type_reglement WHERE p.fk_operation = ? AND p.chk_active = 1 '; $params = [$operationId]; if ($userId) { $sql .= ' AND p.fk_user = ?'; $params[] = $userId; } $sql .= ' ORDER BY p.passed_at DESC'; $stmt = $this->db->prepare($sql); $stmt->execute($params); $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); // Remplir les données $row = 2; foreach ($passages as $passage) { $dateEve = $passage['passed_at'] ? date('d/m/Y', strtotime($passage['passed_at'])) : ''; $heureEve = $passage['passed_at'] ? date('H:i', strtotime($passage['passed_at'])) : ''; // Déchiffrer les données $donateur = ApiService::decryptData($passage['encrypted_name']); $email = !empty($passage['encrypted_email']) ? ApiService::decryptSearchableData($passage['encrypted_email']) : ''; $phone = !empty($passage['encrypted_phone']) ? ApiService::decryptData($passage['encrypted_phone']) : ''; $userName = ApiService::decryptData($passage['user_name']); // Type de passage $typeLabels = [ 1 => 'Effectué', 2 => 'A finaliser', 3 => 'Refusé', 4 => 'Don', 9 => 'Habitat vide' ]; $typeLabel = $typeLabels[$passage['fk_type']] ?? $passage['fk_type']; // Habitat $habitat = $passage['fk_habitat'] == 1 ? 'Individuel' : "Etage {$passage['niveau']} - Appt {$passage['appt']}"; $rowData = [ $passage['id'], $dateEve, $heureEve, $passage['user_first_name'], $userName, $passage['sect_name'], $typeLabel, $passage['numero'], $passage['rue_bis'], $passage['rue'], $passage['ville'], $habitat, $donateur, $email, $phone, $passage['montant'], $passage['reglement_libelle'], $passage['remarque'], $passage['fk_user'], $passage['fk_sector'], $passage['fk_operation'] ]; $sheet->fromArray([$rowData], null, "A{$row}"); $row++; } // Auto-ajuster les colonnes foreach (range('A', 'T') as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } } /** * Crée la feuille des utilisateurs */ private function createUsersSheet(Spreadsheet $spreadsheet, int $operationId): void { $sheet = $spreadsheet->createSheet(); $sheet->setTitle('Utilisateurs'); // En-têtes $headers = [ 'ID_User', 'Nom', 'Prénom', 'Email', 'Téléphone', 'Mobile', 'Rôle', 'Date_création', 'Actif', 'FK_Entite' ]; $sheet->fromArray([$headers], null, 'A1'); // Récupérer les utilisateurs de l'opération $sql = ' SELECT DISTINCT u.id, u.encrypted_name, u.first_name, u.encrypted_email, u.encrypted_phone, u.encrypted_mobile, u.fk_role, u.created_at, u.chk_active, u.fk_entite, r.libelle as role_libelle FROM users u INNER JOIN ope_users ou ON ou.fk_user = u.id LEFT JOIN x_users_roles r ON r.id = u.fk_role WHERE ou.fk_operation = ? AND ou.chk_active = 1 ORDER BY u.encrypted_name '; $stmt = $this->db->prepare($sql); $stmt->execute([$operationId]); $users = $stmt->fetchAll(PDO::FETCH_ASSOC); $row = 2; foreach ($users as $user) { $rowData = [ $user['id'], ApiService::decryptData($user['encrypted_name']), $user['first_name'], !empty($user['encrypted_email']) ? ApiService::decryptSearchableData($user['encrypted_email']) : '', !empty($user['encrypted_phone']) ? ApiService::decryptData($user['encrypted_phone']) : '', !empty($user['encrypted_mobile']) ? ApiService::decryptData($user['encrypted_mobile']) : '', $user['role_libelle'], date('d/m/Y H:i', strtotime($user['created_at'])), $user['chk_active'] ? 'Oui' : 'Non', $user['fk_entite'] ]; $sheet->fromArray([$rowData], null, "A{$row}"); $row++; } foreach (range('A', 'J') as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } } /** * Crée la feuille des secteurs */ private function createSectorsSheet(Spreadsheet $spreadsheet, int $operationId): void { $sheet = $spreadsheet->createSheet(); $sheet->setTitle('Secteurs'); $headers = ['ID_Sector', 'Libellé', 'Couleur', 'Date_création', 'Actif', 'FK_Operation']; $sheet->fromArray([$headers], null, 'A1'); $sql = ' SELECT id, libelle, color, created_at, chk_active, fk_operation FROM ope_sectors WHERE fk_operation = ? AND chk_active = 1 ORDER BY libelle '; $stmt = $this->db->prepare($sql); $stmt->execute([$operationId]); $sectors = $stmt->fetchAll(PDO::FETCH_ASSOC); $row = 2; foreach ($sectors as $sector) { $rowData = [ $sector['id'], $sector['libelle'], $sector['color'], date('d/m/Y H:i', strtotime($sector['created_at'])), $sector['chk_active'] ? 'Oui' : 'Non', $sector['fk_operation'] ]; $sheet->fromArray([$rowData], null, "A{$row}"); $row++; } foreach (range('A', 'F') as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } } /** * Crée la feuille des relations secteurs-utilisateurs */ private function createUserSectorsSheet(Spreadsheet $spreadsheet, int $operationId): void { $sheet = $spreadsheet->createSheet(); $sheet->setTitle('Secteurs-Utilisateurs'); $headers = [ 'ID_Relation', 'FK_Sector', 'Nom_Secteur', 'FK_User', 'Nom_Utilisateur', 'Date_assignation', 'FK_Operation' ]; $sheet->fromArray([$headers], null, 'A1'); $sql = ' SELECT ous.id, ous.fk_sector, ous.fk_user, ous.created_at, ous.fk_operation, s.libelle as sector_name, u.encrypted_name as user_name, u.first_name FROM ope_users_sectors ous INNER JOIN ope_sectors s ON s.id = ous.fk_sector INNER JOIN users u ON u.id = ous.fk_user WHERE ous.fk_operation = ? AND ous.chk_active = 1 ORDER BY s.libelle, u.encrypted_name '; $stmt = $this->db->prepare($sql); $stmt->execute([$operationId]); $userSectors = $stmt->fetchAll(PDO::FETCH_ASSOC); $row = 2; foreach ($userSectors as $us) { $userName = ApiService::decryptData($us['user_name']); $fullUserName = $us['first_name'] ? $us['first_name'] . ' ' . $userName : $userName; $rowData = [ $us['id'], $us['fk_sector'], $us['sector_name'], $us['fk_user'], $fullUserName, date('d/m/Y H:i', strtotime($us['created_at'])), $us['fk_operation'] ]; $sheet->fromArray([$rowData], null, "A{$row}"); $row++; } foreach (range('A', 'G') as $col) { $sheet->getColumnDimension($col)->setAutoSize(true); } } /** * Collecte toutes les données d'une opération pour l'export JSON */ private function collectOperationData(int $operationId, int $entiteId): array { // Métadonnées de l'export $exportData = [ 'export_metadata' => [ 'version' => '1.0', 'export_date' => date('c'), 'source_entite_id' => $entiteId, 'export_type' => 'full_operation' ] ]; // Données de l'opération $exportData['operation'] = $this->getOperationData($operationId, $entiteId); // Utilisateurs de l'opération $exportData['users'] = $this->getOperationUsers($operationId); // Secteurs de l'opération $exportData['sectors'] = $this->getOperationSectors($operationId); // Passages de l'opération $exportData['passages'] = $this->getOperationPassages($operationId); // Relations utilisateurs-secteurs $exportData['user_sectors'] = $this->getOperationUserSectors($operationId); return $exportData; } /** * Récupère les données de l'opération */ private function getOperationData(int $operationId, int $entiteId): ?array { $stmt = $this->db->prepare(' SELECT * FROM operations WHERE id = ? AND fk_entite = ? '); $stmt->execute([$operationId, $entiteId]); return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; } /** * Récupère les utilisateurs de l'opération */ private function getOperationUsers(int $operationId): array { $stmt = $this->db->prepare(' SELECT DISTINCT u.* FROM users u INNER JOIN ope_users ou ON ou.fk_user = u.id WHERE ou.fk_operation = ? AND ou.chk_active = 1 '); $stmt->execute([$operationId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Récupère les secteurs de l'opération */ private function getOperationSectors(int $operationId): array { $stmt = $this->db->prepare(' SELECT * FROM ope_sectors WHERE fk_operation = ? AND chk_active = 1 '); $stmt->execute([$operationId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Récupère les passages de l'opération */ private function getOperationPassages(int $operationId): array { $stmt = $this->db->prepare(' SELECT * FROM ope_pass WHERE fk_operation = ? AND chk_active = 1 '); $stmt->execute([$operationId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Récupère les relations utilisateurs-secteurs */ private function getOperationUserSectors(int $operationId): array { $stmt = $this->db->prepare(' SELECT * FROM ope_users_sectors WHERE fk_operation = ? AND chk_active = 1 '); $stmt->execute([$operationId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Récupère les données des passages dans un format simple pour l'export Excel * (inspiré de l'ancienne version qui fonctionne) */ private function getSimplePassagesData(int $operationId, ?int $userId = null): array { // En-têtes (comme dans l'ancienne version) $aData = []; $aData[] = [ 'Date', 'Heure', 'Prenom', 'Nom', 'Tournee', 'Type', 'N°', 'Rue', 'Ville', 'Habitat', 'Donateur', 'Email', 'Tel', 'Montant', 'Reglement', 'Remarque' ]; // Récupérer les données des passages $sql = ' SELECT p.passed_at, p.fk_type, p.numero, p.rue_bis, p.rue, p.ville, p.fk_habitat, p.appt, p.niveau, p.encrypted_name, p.encrypted_email, p.encrypted_phone, p.montant, p.fk_type_reglement, p.remarque, u.encrypted_name as user_name, u.first_name as user_first_name, u.sect_name, xtr.libelle as reglement_libelle FROM ope_pass p LEFT JOIN users u ON u.id = p.fk_user LEFT JOIN x_types_reglements xtr ON xtr.id = p.fk_type_reglement WHERE p.fk_operation = ? AND p.chk_active = 1 '; $params = [$operationId]; if ($userId) { $sql .= ' AND p.fk_user = ?'; $params[] = $userId; } $sql .= ' ORDER BY p.passed_at DESC'; $stmt = $this->db->prepare($sql); $stmt->execute($params); $passages = $stmt->fetchAll(PDO::FETCH_ASSOC); // Traiter les données comme dans l'ancienne version foreach ($passages as $p) { // Type de passage switch ($p["fk_type"]) { case 1: $ptype = "Effectué"; $preglement = $p["reglement_libelle"]; break; case 2: $ptype = "A finaliser"; $preglement = ""; break; case 3: $ptype = "Refusé"; $preglement = ""; break; case 4: $ptype = "Don"; $preglement = ""; break; case 9: $ptype = "Habitat vide"; $preglement = ""; break; default: $ptype = $p["fk_type"]; $preglement = ""; break; } // Habitat if ($p["fk_habitat"] == 1) { $phabitat = "Individuel"; } else { $phabitat = "Etage " . $p["niveau"] . " - Appt " . $p["appt"]; } // Dates $dateEve = $p["passed_at"] ? date("d/m/Y", strtotime($p["passed_at"])) : ""; $heureEve = $p["passed_at"] ? date("H:i", strtotime($p["passed_at"])) : ""; // Déchiffrer les données $donateur = ApiService::decryptData($p["encrypted_name"]); $email = !empty($p["encrypted_email"]) ? ApiService::decryptSearchableData($p["encrypted_email"]) : ""; $phone = !empty($p["encrypted_phone"]) ? ApiService::decryptData($p["encrypted_phone"]) : ""; $userName = ApiService::decryptData($p["user_name"]); // Nettoyer les données (comme dans l'ancienne version) $nom = str_replace("/", "-", $userName); $tournee = str_replace("/", "-", $p["sect_name"]); $aData[] = [ $dateEve, $heureEve, $p["user_first_name"], $nom, $tournee, $ptype, $p["numero"] . $p["rue_bis"], $p["rue"], $p["ville"], $phabitat, $donateur, $email, $phone, $p["montant"], $preglement, $p["remarque"] ]; } return $aData; } /** * Restaure une opération à partir d'un backup chiffré * * @param string $backupFilePath Chemin vers le fichier de backup * @param int $targetEntiteId ID de l'entité cible (pour restauration cross-entité) * @return array Résultat de la restauration * @throws Exception En cas d'erreur de restauration */ public function restoreFromBackup(string $backupFilePath, int $targetEntiteId): array { try { // Initialiser le service de chiffrement $backupService = new BackupEncryptionService(); // Lire et déchiffrer le backup $backupData = $backupService->readBackupFile($backupFilePath); // Valider la structure du backup if (!isset($backupData['operation']) || !isset($backupData['export_metadata'])) { throw new Exception('Structure de backup invalide'); } $operationData = $backupData['operation']; $originalEntiteId = $backupData['export_metadata']['source_entite_id']; // Commencer la transaction $this->db->beginTransaction(); // Créer la nouvelle opération $newOperationId = $this->restoreOperation($operationData, $targetEntiteId); // Restaurer les utilisateurs (si même entité) if ($targetEntiteId === $originalEntiteId && isset($backupData['users'])) { $this->restoreUsers($backupData['users'], $newOperationId); } // Restaurer les secteurs if (isset($backupData['sectors'])) { $this->restoreSectors($backupData['sectors'], $newOperationId); } // Restaurer les relations utilisateurs-secteurs if (isset($backupData['user_sectors'])) { $this->restoreUserSectors($backupData['user_sectors'], $newOperationId); } // Restaurer les passages if (isset($backupData['passages'])) { $this->restorePassages($backupData['passages'], $newOperationId); } $this->db->commit(); LogService::log('Restauration de backup réussie', [ 'level' => 'info', 'backup_file' => $backupFilePath, 'original_operation_id' => $operationData['id'], 'new_operation_id' => $newOperationId, 'target_entite_id' => $targetEntiteId, 'original_entite_id' => $originalEntiteId ]); return [ 'success' => true, 'new_operation_id' => $newOperationId, 'original_operation_id' => $operationData['id'], 'restored_data' => [ 'operation' => true, 'users' => isset($backupData['users']) && $targetEntiteId === $originalEntiteId, 'sectors' => isset($backupData['sectors']), 'user_sectors' => isset($backupData['user_sectors']), 'passages' => isset($backupData['passages']) ] ]; } catch (Exception $e) { $this->db->rollBack(); LogService::log('Erreur lors de la restauration du backup', [ 'level' => 'error', 'backup_file' => $backupFilePath, 'target_entite_id' => $targetEntiteId, 'error' => $e->getMessage() ]); throw $e; } } /** * Restaure les données de l'opération */ private function restoreOperation(array $operationData, int $targetEntiteId): int { $stmt = $this->db->prepare(' INSERT INTO operations ( fk_entite, libelle, date_deb, date_fin, chk_distinct_sectors, fk_user_creat, chk_active, created_at ) VALUES (?, ?, ?, ?, ?, ?, 0, NOW()) '); $userId = Session::getUserId() ?? 1; $stmt->execute([ $targetEntiteId, $operationData['libelle'] . ' (Restaurée)', $operationData['date_deb'], $operationData['date_fin'], $operationData['chk_distinct_sectors'] ?? 0, $userId ]); return (int)$this->db->lastInsertId(); } /** * Restaure les utilisateurs (uniquement si même entité) */ private function restoreUsers(array $users, int $newOperationId): void { foreach ($users as $user) { // Vérifier si l'utilisateur existe déjà $stmt = $this->db->prepare('SELECT id FROM users WHERE id = ?'); $stmt->execute([$user['id']]); if ($stmt->fetch()) { // Associer l'utilisateur existant à la nouvelle opération $stmt = $this->db->prepare(' INSERT IGNORE INTO ope_users (fk_operation, fk_user, chk_active, created_at) VALUES (?, ?, 1, NOW()) '); $stmt->execute([$newOperationId, $user['id']]); } } } /** * Restaure les secteurs */ private function restoreSectors(array $sectors, int $newOperationId): void { foreach ($sectors as $sector) { $stmt = $this->db->prepare(' INSERT INTO ope_sectors ( fk_operation, libelle, color, chk_active, created_at ) VALUES (?, ?, ?, 1, NOW()) '); $stmt->execute([ $newOperationId, $sector['libelle'], $sector['color'] ]); } } /** * Restaure les relations utilisateurs-secteurs */ private function restoreUserSectors(array $userSectors, int $newOperationId): void { foreach ($userSectors as $us) { // Trouver le nouveau secteur par son libellé $stmt = $this->db->prepare(' SELECT id FROM ope_sectors WHERE fk_operation = ? AND libelle = ? LIMIT 1 '); $stmt->execute([$newOperationId, $us['libelle'] ?? '']); $newSector = $stmt->fetch(); if ($newSector) { $stmt = $this->db->prepare(' INSERT IGNORE INTO ope_users_sectors ( fk_operation, fk_sector, fk_user, chk_active, created_at ) VALUES (?, ?, ?, 1, NOW()) '); $stmt->execute([ $newOperationId, $newSector['id'], $us['fk_user'] ]); } } } /** * Restaure les passages */ private function restorePassages(array $passages, int $newOperationId): void { foreach ($passages as $passage) { $stmt = $this->db->prepare(' INSERT INTO ope_pass ( fk_operation, fk_user, fk_sector, fk_type, passed_at, numero, rue_bis, rue, ville, fk_habitat, appt, niveau, encrypted_name, encrypted_email, encrypted_phone, montant, fk_type_reglement, remarque, chk_active, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW()) '); $stmt->execute([ $newOperationId, $passage['fk_user'], $passage['fk_sector'], $passage['fk_type'], $passage['passed_at'], $passage['numero'], $passage['rue_bis'], $passage['rue'], $passage['ville'], $passage['fk_habitat'], $passage['appt'], $passage['niveau'], $passage['encrypted_name'], $passage['encrypted_email'], $passage['encrypted_phone'], $passage['montant'], $passage['fk_type_reglement'], $passage['remarque'] ]); } } }