On release/v3.1.4: Sauvegarde temporaire pour changement de branche
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-pdo": "*",
|
||||
"phpmailer/phpmailer": "^6.8",
|
||||
"phpoffice/phpspreadsheet": "^2.0"
|
||||
"phpoffice/phpspreadsheet": "^2.0",
|
||||
"setasign/fpdf": "^1.8"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
||||
@@ -141,6 +141,11 @@ $SSH_JUMP_CMD "
|
||||
incus exec ${INCUS_CONTAINER} -- chmod -R 775 ${FINAL_PATH}/uploads || exit 1
|
||||
incus exec ${INCUS_CONTAINER} -- find ${FINAL_PATH}/uploads -type f -exec chmod -R 664 {} \; || exit 1
|
||||
|
||||
echo '📦 Mise à jour des dépendances Composer...'
|
||||
incus exec ${INCUS_CONTAINER} -- bash -c 'cd ${FINAL_PATH} && composer update --no-dev --optimize-autoloader' || {
|
||||
echo '⚠️ Composer non disponible ou échec, poursuite sans mise à jour des dépendances'
|
||||
}
|
||||
|
||||
echo '🧹 Nettoyage...'
|
||||
incus exec ${INCUS_CONTAINER} -- rm -f /tmp/${ARCHIVE_NAME} || exit 1
|
||||
rm -f /tmp/${ARCHIVE_NAME} || exit 1
|
||||
|
||||
@@ -142,6 +142,15 @@ fi
|
||||
|
||||
echo "✅ Propriétaire et permissions appliqués avec succès"
|
||||
|
||||
# Mise à jour des dépendances Composer
|
||||
echo "📦 Mise à jour des dépendances Composer sur $DEST_CONTAINER..."
|
||||
ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- bash -c 'cd $API_PATH && composer update --no-dev --optimize-autoloader'" > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Dépendances Composer mises à jour avec succès"
|
||||
else
|
||||
echo "⚠️ Composer non disponible ou échec, poursuite sans mise à jour des dépendances"
|
||||
fi
|
||||
|
||||
# Vérifier la copie
|
||||
echo "✅ Vérification de la copie..."
|
||||
ssh -i $HOST_KEY -p $HOST_PORT $HOST_USER@$HOST_IP "incus exec $DEST_CONTAINER --project $PROJECT -- test -d $API_PATH"
|
||||
|
||||
@@ -57,8 +57,9 @@ class EntiteController {
|
||||
ville,
|
||||
fk_type,
|
||||
created_at,
|
||||
chk_active
|
||||
) VALUES (?, ?, ?, 1, NOW(), 1)
|
||||
chk_active,
|
||||
chk_user_delete_pass
|
||||
) VALUES (?, ?, ?, 1, NOW(), 1, 0)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
@@ -109,7 +110,7 @@ class EntiteController {
|
||||
public function getEntiteById(int $id): array|false {
|
||||
try {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active, chk_user_delete_pass
|
||||
FROM entites
|
||||
WHERE id = ? AND chk_active = 1
|
||||
');
|
||||
@@ -146,7 +147,7 @@ class EntiteController {
|
||||
public function getEntiteByPostalCode(string $postalCode): array|false {
|
||||
try {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active, chk_user_delete_pass
|
||||
FROM entites
|
||||
WHERE code_postal = ? AND chk_active = 1
|
||||
');
|
||||
@@ -247,7 +248,7 @@ class EntiteController {
|
||||
public function getEntites(): void {
|
||||
try {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active, chk_user_delete_pass
|
||||
FROM entites
|
||||
WHERE chk_active = 1
|
||||
ORDER BY code_postal ASC
|
||||
@@ -587,6 +588,11 @@ class EntiteController {
|
||||
$updateFields[] = 'chk_username_manuel = ?';
|
||||
$params[] = $data['chk_username_manuel'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (isset($data['chk_user_delete_pass'])) {
|
||||
$updateFields[] = 'chk_user_delete_pass = ?';
|
||||
$params[] = $data['chk_user_delete_pass'] ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucun champ à mettre à jour, retourner une erreur
|
||||
@@ -728,7 +734,7 @@ class EntiteController {
|
||||
// Créer le dossier de destination
|
||||
require_once __DIR__ . '/../Services/FileService.php';
|
||||
$fileService = new \FileService();
|
||||
$uploadPath = "/entites/{$entiteId}/logo";
|
||||
$uploadPath = "/{$entiteId}/logo";
|
||||
$fullPath = $fileService->createDirectory($entiteId, $uploadPath);
|
||||
|
||||
// Nom du fichier final
|
||||
|
||||
@@ -533,7 +533,7 @@ class LoginController {
|
||||
e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
|
||||
e.encrypted_email as email, e.gps_lat, e.gps_lng,
|
||||
e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
|
||||
e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe
|
||||
e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass
|
||||
FROM entites e
|
||||
LEFT JOIN x_regions r ON e.fk_region = r.id
|
||||
WHERE e.id = ? AND e.chk_active = 1'
|
||||
@@ -547,7 +547,7 @@ class LoginController {
|
||||
e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
|
||||
e.encrypted_email as email, e.gps_lat, e.gps_lng,
|
||||
e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
|
||||
e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe
|
||||
e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass
|
||||
FROM entites e
|
||||
LEFT JOIN x_regions r ON e.fk_region = r.id
|
||||
WHERE e.id != 1 AND e.chk_active = 1'
|
||||
@@ -600,7 +600,7 @@ class LoginController {
|
||||
e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
|
||||
e.encrypted_email as email, e.gps_lat, e.gps_lng,
|
||||
e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
|
||||
e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe
|
||||
e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass
|
||||
FROM entites e
|
||||
LEFT JOIN x_regions r ON e.fk_region = r.id
|
||||
WHERE e.fk_type = 1 AND e.chk_active = 1'
|
||||
|
||||
@@ -552,8 +552,26 @@ class PassageController {
|
||||
'operationId' => $operationId
|
||||
]);
|
||||
|
||||
// Envoyer la réponse immédiatement pour éviter les timeouts
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Passage créé avec succès',
|
||||
'passage_id' => $passageId,
|
||||
'receipt_generated' => false // On va générer le reçu en arrière-plan
|
||||
], 201);
|
||||
|
||||
// Flush la sortie pour s'assurer que la réponse est envoyée
|
||||
if (ob_get_level()) {
|
||||
ob_end_flush();
|
||||
}
|
||||
flush();
|
||||
|
||||
// Fermer la connexion HTTP mais continuer le traitement
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
fastcgi_finish_request();
|
||||
}
|
||||
|
||||
// Générer automatiquement un reçu si c'est un don (fk_type = 1) avec email valide
|
||||
$receiptGenerated = false;
|
||||
if (isset($data['fk_type']) && (int)$data['fk_type'] === 1) {
|
||||
// Vérifier si un email a été fourni
|
||||
$hasEmail = false;
|
||||
@@ -584,13 +602,8 @@ class PassageController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Passage créé avec succès',
|
||||
'passage_id' => $passageId,
|
||||
'receipt_generated' => $receiptGenerated
|
||||
], 201);
|
||||
|
||||
return; // Fin de la méthode, éviter d'exécuter le code après
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la création du passage', [
|
||||
'level' => 'error',
|
||||
@@ -740,25 +753,41 @@ class PassageController {
|
||||
'passageId' => $passageId
|
||||
]);
|
||||
|
||||
// Vérifier si un reçu doit être généré après la mise à jour
|
||||
$receiptGenerated = false;
|
||||
// Envoyer la réponse immédiatement pour éviter les timeouts
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Passage mis à jour avec succès',
|
||||
'receipt_generated' => false // On va générer le reçu en arrière-plan
|
||||
], 200);
|
||||
|
||||
// Récupérer les données actualisées du passage
|
||||
$stmt = $this->db->prepare('SELECT fk_type, encrypted_email, nom_recu FROM ope_pass WHERE id = ?');
|
||||
$stmt->execute([$passageId]);
|
||||
$updatedPassage = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
// Flush la sortie pour s'assurer que la réponse est envoyée
|
||||
if (ob_get_level()) {
|
||||
ob_end_flush();
|
||||
}
|
||||
flush();
|
||||
|
||||
if ($updatedPassage) {
|
||||
// Générer un reçu si :
|
||||
// - C'est un don (fk_type = 1)
|
||||
// - Il y a un email valide
|
||||
// - Il n'y a pas encore de reçu (nom_recu est vide ou null)
|
||||
if ((int)$updatedPassage['fk_type'] === 1 &&
|
||||
!empty($updatedPassage['encrypted_email']) &&
|
||||
empty($updatedPassage['nom_recu'])) {
|
||||
|
||||
// Vérifier que l'email est valide en le déchiffrant
|
||||
try {
|
||||
// Fermer la connexion HTTP mais continuer le traitement
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
fastcgi_finish_request();
|
||||
}
|
||||
|
||||
// Maintenant générer le reçu en arrière-plan après avoir envoyé la réponse
|
||||
try {
|
||||
// Récupérer les données actualisées du passage
|
||||
$stmt = $this->db->prepare('SELECT fk_type, encrypted_email, nom_recu FROM ope_pass WHERE id = ?');
|
||||
$stmt->execute([$passageId]);
|
||||
$updatedPassage = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($updatedPassage) {
|
||||
// Générer un reçu si :
|
||||
// - C'est un don (fk_type = 1)
|
||||
// - Il y a un email valide
|
||||
// - Il n'y a pas encore de reçu (nom_recu est vide ou null)
|
||||
if ((int)$updatedPassage['fk_type'] === 1 &&
|
||||
!empty($updatedPassage['encrypted_email']) &&
|
||||
empty($updatedPassage['nom_recu'])) {
|
||||
|
||||
// Vérifier que l'email est valide en le déchiffrant
|
||||
$email = ApiService::decryptSearchableData($updatedPassage['encrypted_email']);
|
||||
|
||||
if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
@@ -772,21 +801,17 @@ class PassageController {
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la génération automatique du reçu après mise à jour', [
|
||||
'level' => 'warning',
|
||||
'error' => $e->getMessage(),
|
||||
'passageId' => $passageId
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la génération automatique du reçu après mise à jour', [
|
||||
'level' => 'warning',
|
||||
'error' => $e->getMessage(),
|
||||
'passageId' => $passageId
|
||||
]);
|
||||
}
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Passage mis à jour avec succès',
|
||||
'receipt_generated' => $receiptGenerated
|
||||
], 200);
|
||||
|
||||
return; // Fin de la méthode, éviter d'exécuter le code après
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la mise à jour du passage', [
|
||||
'level' => 'error',
|
||||
@@ -818,8 +843,47 @@ class PassageController {
|
||||
|
||||
$passageId = (int)$id;
|
||||
|
||||
// Récupérer le rôle de l'utilisateur
|
||||
$stmt = $this->db->prepare('SELECT fk_role, fk_entite FROM users WHERE id = ?');
|
||||
$stmt->execute([$userId]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$user) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Utilisateur non trouvé'
|
||||
], 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$userRole = (int)$user['fk_role'];
|
||||
$entiteId = (int)$user['fk_entite'];
|
||||
|
||||
// Si l'utilisateur est un membre (fk_role = 1), vérifier les permissions de l'entité
|
||||
if ($userRole === 1) {
|
||||
$stmt = $this->db->prepare('SELECT chk_user_delete_pass FROM entites WHERE id = ?');
|
||||
$stmt->execute([$entiteId]);
|
||||
$entite = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$entite || (int)$entite['chk_user_delete_pass'] !== 1) {
|
||||
LogService::log('Tentative de suppression de passage non autorisée', [
|
||||
'level' => 'warning',
|
||||
'userId' => $userId,
|
||||
'userRole' => $userRole,
|
||||
'entiteId' => $entiteId,
|
||||
'passageId' => $passageId,
|
||||
'chk_user_delete_pass' => $entite ? $entite['chk_user_delete_pass'] : null
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Vous n\'avez pas l\'autorisation de supprimer des passages'
|
||||
], 403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier que le passage existe et appartient à l'entité de l'utilisateur
|
||||
$entiteId = $this->getUserEntiteId($userId);
|
||||
if (!$entiteId) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
|
||||
@@ -72,12 +72,17 @@ class MonitoredDatabase extends PDO {
|
||||
/**
|
||||
* Query avec monitoring
|
||||
*/
|
||||
public function query($statement, $mode = PDO::ATTR_DEFAULT_FETCH_MODE, ...$args): PDOStatement|false {
|
||||
public function query($statement, $mode = null, ...$args): PDOStatement|false {
|
||||
// Démarrer le chronométrage
|
||||
PerformanceMonitor::startDbQuery($statement);
|
||||
|
||||
try {
|
||||
$result = parent::query($statement, $mode, ...$args);
|
||||
// Si pas de mode spécifié, utiliser query simple
|
||||
if ($mode === null) {
|
||||
$result = parent::query($statement);
|
||||
} else {
|
||||
$result = parent::query($statement, $mode, ...$args);
|
||||
}
|
||||
|
||||
// Terminer le chronométrage
|
||||
PerformanceMonitor::endDbQuery();
|
||||
|
||||
@@ -37,7 +37,7 @@ class ExportService {
|
||||
}
|
||||
|
||||
// Créer le dossier de destination
|
||||
$exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}/exports/excel");
|
||||
$exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}");
|
||||
|
||||
LogService::log('exportDir', [
|
||||
'level' => 'warning',
|
||||
@@ -138,7 +138,7 @@ class ExportService {
|
||||
$exportData = $this->collectOperationData($operationId, $entiteId);
|
||||
|
||||
// Créer le dossier de destination
|
||||
$exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}/exports/json");
|
||||
$exportDir = $this->fileService->createDirectory($entiteId, "/{$entiteId}/operations/{$operationId}");
|
||||
|
||||
// Initialiser le service de chiffrement
|
||||
$backupService = new BackupEncryptionService();
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Services;
|
||||
require_once __DIR__ . '/LogService.php';
|
||||
require_once __DIR__ . '/ApiService.php';
|
||||
require_once __DIR__ . '/FileService.php';
|
||||
require_once __DIR__ . '/ReceiptPDFGenerator.php';
|
||||
|
||||
use PDO;
|
||||
use Database;
|
||||
@@ -24,6 +25,8 @@ class ReceiptService {
|
||||
private PDO $db;
|
||||
private FileService $fileService;
|
||||
private const DEFAULT_LOGO_PATH = __DIR__ . '/../../docs/_logo_recu.png';
|
||||
private const LOGO_WIDTH = 40; // Largeur du logo en mm (80 est trop grand pour un A4)
|
||||
private const LOGO_HEIGHT = 40; // Hauteur du logo en mm
|
||||
|
||||
public function __construct() {
|
||||
$this->db = Database::getInstance();
|
||||
@@ -89,7 +92,7 @@ class ReceiptService {
|
||||
$pdfContent = $this->generateOptimizedPDF($receiptData, $logoPath);
|
||||
|
||||
// Créer le répertoire de stockage
|
||||
$uploadPath = "/entites/{$operationData['fk_entite']}/recus/{$operationData['id']}";
|
||||
$uploadPath = "/{$operationData['fk_entite']}/recus/{$operationData['id']}";
|
||||
$fullPath = $this->fileService->createDirectory($operationData['fk_entite'], $uploadPath);
|
||||
|
||||
// Nom du fichier
|
||||
@@ -141,181 +144,13 @@ class ReceiptService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un PDF ultra-optimisé (< 20KB)
|
||||
* Utilise le format PDF natif pour minimiser la taille
|
||||
* Génère un PDF optimisé avec logo et mise en page épurée
|
||||
*/
|
||||
private function generateOptimizedPDF(array $data, ?string $logoPath): string {
|
||||
// Début du PDF
|
||||
$pdf = "%PDF-1.3\n";
|
||||
$objects = [];
|
||||
$xref = [];
|
||||
|
||||
// Object 1 - Catalog
|
||||
$objects[1] = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n";
|
||||
|
||||
// Object 2 - Pages
|
||||
$objects[2] = "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n";
|
||||
|
||||
// Object 3 - Page
|
||||
$objects[3] = "3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>\nendobj\n";
|
||||
|
||||
// Object 4 - Font (Helvetica)
|
||||
$objects[4] = "4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n";
|
||||
|
||||
// Contenu de la page (texte du reçu)
|
||||
$content = $this->generatePDFContent($data);
|
||||
|
||||
// Object 5 - Content stream
|
||||
$contentLength = strlen($content);
|
||||
$objects[5] = "5 0 obj\n<< /Length $contentLength >>\nstream\n$content\nendstream\nendobj\n";
|
||||
|
||||
// Construction du PDF final
|
||||
$offset = strlen($pdf);
|
||||
foreach ($objects as $obj) {
|
||||
$xref[] = $offset;
|
||||
$pdf .= $obj;
|
||||
$offset += strlen($obj);
|
||||
}
|
||||
|
||||
// Table xref
|
||||
$pdf .= "xref\n";
|
||||
$pdf .= "0 " . (count($objects) + 1) . "\n";
|
||||
$pdf .= "0000000000 65535 f \n";
|
||||
foreach ($xref as $off) {
|
||||
$pdf .= sprintf("%010d 00000 n \n", $off);
|
||||
}
|
||||
|
||||
// Trailer
|
||||
$pdf .= "trailer\n";
|
||||
$pdf .= "<< /Size " . (count($objects) + 1) . " /Root 1 0 R >>\n";
|
||||
$pdf .= "startxref\n";
|
||||
$pdf .= "$offset\n";
|
||||
$pdf .= "%%EOF\n";
|
||||
|
||||
return $pdf;
|
||||
$pdf = new ReceiptPDFGenerator();
|
||||
return $pdf->generateReceipt($data, $logoPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère le contenu textuel du reçu pour le PDF
|
||||
*/
|
||||
private function generatePDFContent(array $data): string {
|
||||
$content = "BT\n";
|
||||
$content .= "/F1 12 Tf\n";
|
||||
$y = 750;
|
||||
|
||||
// En-tête
|
||||
$content .= "50 $y Td\n";
|
||||
$content .= "(" . $this->escapeString($data['entite_name']) . ") Tj\n";
|
||||
$y -= 20;
|
||||
|
||||
if (!empty($data['entite_address'])) {
|
||||
$content .= "0 -20 Td\n";
|
||||
$content .= "(" . $this->escapeString($data['entite_address']) . ") Tj\n";
|
||||
$y -= 20;
|
||||
}
|
||||
|
||||
// Titre du reçu
|
||||
$y -= 40;
|
||||
$content .= "/F1 16 Tf\n";
|
||||
$content .= "0 -40 Td\n";
|
||||
$content .= "(RECU DE DON N° " . $data['receipt_number'] . ") Tj\n";
|
||||
|
||||
$content .= "/F1 10 Tf\n";
|
||||
$content .= "0 -15 Td\n";
|
||||
$content .= "(Article 200 du Code General des Impots) Tj\n";
|
||||
|
||||
// Informations du donateur
|
||||
$y -= 60;
|
||||
$content .= "/F1 12 Tf\n";
|
||||
$content .= "0 -45 Td\n";
|
||||
$content .= "(DONATEUR) Tj\n";
|
||||
|
||||
$content .= "/F1 11 Tf\n";
|
||||
$content .= "0 -20 Td\n";
|
||||
$content .= "(Nom : " . $this->escapeString($data['donor_name']) . ") Tj\n";
|
||||
|
||||
if (!empty($data['donor_address'])) {
|
||||
$content .= "0 -15 Td\n";
|
||||
$content .= "(Adresse : " . $this->escapeString($data['donor_address']) . ") Tj\n";
|
||||
}
|
||||
|
||||
if (!empty($data['donor_email'])) {
|
||||
$content .= "0 -15 Td\n";
|
||||
$content .= "(Email : " . $this->escapeString($data['donor_email']) . ") Tj\n";
|
||||
}
|
||||
|
||||
// Détails du don
|
||||
$content .= "0 -30 Td\n";
|
||||
$content .= "/F1 12 Tf\n";
|
||||
$content .= "(DETAILS DU DON) Tj\n";
|
||||
|
||||
$content .= "/F1 11 Tf\n";
|
||||
$content .= "0 -20 Td\n";
|
||||
$content .= "(Date : " . $data['donation_date'] . ") Tj\n";
|
||||
|
||||
$content .= "0 -15 Td\n";
|
||||
$content .= "(Montant : " . $data['amount'] . " EUR) Tj\n";
|
||||
|
||||
$content .= "0 -15 Td\n";
|
||||
$content .= "(Mode de reglement : " . $this->escapeString($data['payment_method']) . ") Tj\n";
|
||||
|
||||
if (!empty($data['operation_name'])) {
|
||||
$content .= "0 -15 Td\n";
|
||||
$content .= "(Campagne : " . $this->escapeString($data['operation_name']) . ") Tj\n";
|
||||
}
|
||||
|
||||
// Mention légale
|
||||
$content .= "/F1 9 Tf\n";
|
||||
$content .= "0 -40 Td\n";
|
||||
$content .= "(Reduction d'impot egale a 66% du montant verse dans la limite de 20% du revenu imposable) Tj\n";
|
||||
|
||||
// Date et signature
|
||||
$content .= "/F1 11 Tf\n";
|
||||
$content .= "0 -30 Td\n";
|
||||
$content .= "(Fait a " . $this->escapeString($data['entite_city']) . ", le " . $data['signature_date'] . ") Tj\n";
|
||||
|
||||
$content .= "0 -20 Td\n";
|
||||
$content .= "(Le President) Tj\n";
|
||||
|
||||
$content .= "ET\n";
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Échappe les caractères spéciaux pour le PDF
|
||||
*/
|
||||
private function escapeString(string $str): string {
|
||||
// Échapper les caractères spéciaux PDF
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
$str = str_replace('(', '\\(', $str);
|
||||
$str = str_replace(')', '\\)', $str);
|
||||
|
||||
// Remplacer manuellement les caractères accentués les plus courants
|
||||
$accents = [
|
||||
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A',
|
||||
'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a',
|
||||
'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E',
|
||||
'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
|
||||
'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
|
||||
'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
|
||||
'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O',
|
||||
'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o',
|
||||
'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U',
|
||||
'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u',
|
||||
'Ñ' => 'N', 'ñ' => 'n',
|
||||
'Ç' => 'C', 'ç' => 'c',
|
||||
'Œ' => 'OE', 'œ' => 'oe',
|
||||
'Æ' => 'AE', 'æ' => 'ae'
|
||||
];
|
||||
|
||||
$str = strtr($str, $accents);
|
||||
|
||||
// Supprimer tout caractère non-ASCII restant
|
||||
$str = preg_replace('/[^\x20-\x7E]/', '', $str);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les données du passage
|
||||
|
||||
Reference in New Issue
Block a user