19 changed files with 4946 additions and 1223 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -4,13 +4,14 @@ use App\Models\Users; |
|||
use App\Models\Avance; |
|||
use App\Models\AlertMail; |
|||
|
|||
/** |
|||
* Vérifier les deadlines et envoyer des alertes email |
|||
*/ |
|||
function checkDeadlineAlerts() |
|||
{ |
|||
log_message('info', "=== DÉBUT checkDeadlineAlerts ==="); |
|||
|
|||
$cacheFile = WRITEPATH . 'cache/check_deadline_last_run.txt'; |
|||
|
|||
// On enlève la vérification de 24h pour s'assurer que le script tourne quotidiennement |
|||
file_put_contents($cacheFile, time()); |
|||
|
|||
$avanceModel = new Avance(); |
|||
@ -20,15 +21,16 @@ function checkDeadlineAlerts() |
|||
$today = date('Y-m-d'); |
|||
log_message('info', "Date du jour: {$today}"); |
|||
|
|||
// Modification pour vérifier les avances dans 0-3 jours |
|||
// Récupération des avances dans 0-3 jours |
|||
$avances = $avanceModel |
|||
->where('DATE(deadline) >=', $today) // Inclut le jour même |
|||
->where('DATE(deadline) >=', $today) |
|||
->where('DATE(deadline) <=', date('Y-m-d', strtotime('+3 days'))) |
|||
->where('active', 1) |
|||
->findAll(); |
|||
|
|||
log_message('info', "Nombre d'avances trouvées (0-3 jours): " . count($avances)); |
|||
|
|||
// Récupération des utilisateurs DAF |
|||
$users = $usersModel->select('users.email, users.firstname, users.lastname') |
|||
->join('user_group', 'user_group.user_id = users.id') |
|||
->join('groups', 'groups.id = user_group.group_id') |
|||
@ -56,7 +58,7 @@ function checkDeadlineAlerts() |
|||
|
|||
log_message('info', "Avance ID: {$avance['avance_id']}, Deadline: {$deadline}, Jours restants: {$daysLeft}"); |
|||
|
|||
// Modification des types d'alerte pour 0, 1, 2, 3 jours |
|||
// Détermination du type d'alerte |
|||
$alertType = match($daysLeft) { |
|||
3 => 'deadline_3_days', |
|||
2 => 'deadline_2_days', |
|||
@ -83,10 +85,12 @@ function checkDeadlineAlerts() |
|||
continue; |
|||
} |
|||
|
|||
// Message modifié pour inclure le cas du jour même |
|||
// Construction du message |
|||
$urgencyText = $daysLeft === 0 ? "ÉCHÉANCE AUJOURD'HUI" : "{$daysLeft} jour(s) restant(s)"; |
|||
$message = " |
|||
<h3>⚠️ URGENT : Avance approchant de la deadline</h3> |
|||
<html> |
|||
<body style='font-family: Arial, sans-serif; color: #333;'> |
|||
<h3 style='color: #d9534f;'>⚠️ URGENT : Avance approchant de la deadline</h3> |
|||
<p><strong>ID Avance :</strong> {$avance['avance_id']}</p> |
|||
<p><strong>Client :</strong> {$avance['customer_name']}</p> |
|||
<p><strong>Montant avance :</strong> " . number_format($avance['avance_amount'], 0, ',', ' ') . " Ar</p> |
|||
@ -95,19 +99,21 @@ function checkDeadlineAlerts() |
|||
<p><strong>Statut :</strong> <span style='color: red; font-weight: bold;'>{$urgencyText}</span></p> |
|||
<p><strong>Téléphone client :</strong> {$avance['customer_phone']}</p> |
|||
<p><strong>Adresse client :</strong> {$avance['customer_address']}</p> |
|||
<hr> |
|||
<hr style='border: 1px solid #ddd;'> |
|||
<p><em>Cette avance " . ($daysLeft === 0 ? "arrive à échéance aujourd'hui" : "arrivera à échéance dans {$daysLeft} jour(s)") . ". Action requise immédiatement.</em></p> |
|||
</body> |
|||
</html> |
|||
"; |
|||
|
|||
$emailsSent = 0; |
|||
foreach ($emails as $to) { |
|||
log_message('info', "Tentative d'envoi email à: {$to}"); |
|||
|
|||
$subject = $daysLeft === 0 |
|||
? "⚠️ AVANCE URGENTE - ÉCHÉANCE AUJOURD'HUI" |
|||
: "⚠️ AVANCE URGENTE - {$daysLeft} jour(s) restant(s)"; |
|||
|
|||
if (sendEmailInBackground($to, $subject, $message)) { |
|||
foreach ($emails as $to) { |
|||
log_message('info', "Tentative d'envoi email à: {$to}"); |
|||
|
|||
if (sendEmailWithBrevo($to, $subject, $message)) { |
|||
$emailsSent++; |
|||
log_message('info', "Email envoyé avec succès à: {$to}"); |
|||
} else { |
|||
@ -115,6 +121,7 @@ function checkDeadlineAlerts() |
|||
} |
|||
} |
|||
|
|||
// Enregistrement de l'alerte si au moins un email a été envoyé |
|||
if ($emailsSent > 0) { |
|||
log_message('info', "Insertion alerte pour avance_id={$avance['avance_id']} avec type {$alertType}"); |
|||
$alertMailModel->insert([ |
|||
@ -128,49 +135,182 @@ function checkDeadlineAlerts() |
|||
log_message('error', "Aucun email envoyé pour avance_id={$avance['avance_id']} avec type {$alertType}"); |
|||
} |
|||
} |
|||
checkAndConvertCompletedAvances(); |
|||
|
|||
log_message('info', "=== FIN checkDeadlineAlerts ==="); |
|||
|
|||
// ✅ NOUVELLE FONCTIONNALITÉ : Gérer les avances expirées |
|||
handleExpiredAvances(); |
|||
} |
|||
|
|||
function sendEmailInBackground($to, $subject, $message) |
|||
/** |
|||
* ✅ NOUVELLE FONCTION : Gérer automatiquement les avances expirées |
|||
* - Libérer les produits (remettre en stock) |
|||
* - Désactiver les avances |
|||
*/ |
|||
function handleExpiredAvances() |
|||
{ |
|||
log_message('info', "=== DÉBUT handleExpiredAvances ==="); |
|||
|
|||
$avanceModel = new Avance(); |
|||
$productsModel = new \App\Models\Products(); |
|||
|
|||
$today = date('Y-m-d'); |
|||
log_message('info', "Vérification des avances expirées au: {$today}"); |
|||
|
|||
// Récupérer les avances expirées et encore actives |
|||
$expiredAvances = $avanceModel |
|||
->where('DATE(deadline) <', $today) |
|||
->where('active', 1) |
|||
->where('is_order', 0) |
|||
->findAll(); |
|||
|
|||
log_message('info', "Nombre d'avances expirées trouvées: " . count($expiredAvances)); |
|||
|
|||
if (empty($expiredAvances)) { |
|||
log_message('info', "Aucune avance expirée à traiter"); |
|||
log_message('info', "=== FIN handleExpiredAvances ==="); |
|||
return; |
|||
} |
|||
|
|||
$processedCount = 0; |
|||
$errorCount = 0; |
|||
|
|||
foreach ($expiredAvances as $avance) { |
|||
try { |
|||
log_message('info', "Préparation envoi email à: {$to}"); |
|||
log_message('info', "Traitement avance expirée ID: {$avance['avance_id']}, Client: {$avance['customer_name']}, Deadline: {$avance['deadline']}"); |
|||
|
|||
// ✅ Désactiver l'avance |
|||
$updateResult = $avanceModel->update($avance['avance_id'], ['active' => 0]); |
|||
|
|||
if (!$updateResult) { |
|||
log_message('error', "Échec désactivation avance ID: {$avance['avance_id']}"); |
|||
$errorCount++; |
|||
continue; |
|||
} |
|||
|
|||
log_message('info', "Avance ID {$avance['avance_id']} désactivée avec succès"); |
|||
|
|||
// ✅ Libérer le produit UNIQUEMENT pour les avances "sur terre" avec product_id |
|||
if ($avance['type_avance'] === 'terre' && !empty($avance['product_id'])) { |
|||
$productUpdateResult = $productsModel->update($avance['product_id'], ['product_sold' => 0]); |
|||
|
|||
if ($productUpdateResult) { |
|||
log_message('info', "Produit ID {$avance['product_id']} libéré (remis en stock)"); |
|||
} else { |
|||
log_message('warning', "Échec libération produit ID: {$avance['product_id']}"); |
|||
} |
|||
} elseif ($avance['type_avance'] === 'mere') { |
|||
log_message('info', "Avance 'sur mer' - Pas de produit à libérer (product_name: {$avance['product_name']})"); |
|||
} else { |
|||
log_message('info', "Pas de product_id à libérer pour avance ID: {$avance['avance_id']}"); |
|||
} |
|||
|
|||
$processedCount++; |
|||
|
|||
} catch (\Exception $e) { |
|||
log_message('error', "Erreur traitement avance expirée ID {$avance['avance_id']}: " . $e->getMessage()); |
|||
$errorCount++; |
|||
} |
|||
} |
|||
|
|||
log_message('info', "Avances expirées traitées: {$processedCount}, Erreurs: {$errorCount}"); |
|||
log_message('info', "=== FIN handleExpiredAvances ==="); |
|||
} |
|||
|
|||
/** |
|||
* ✅ Vérifier et convertir automatiquement les avances complètes en commandes |
|||
* À appeler dans checkDeadlineAlerts() ou via un cron job |
|||
*/ |
|||
function checkAndConvertCompletedAvances() |
|||
{ |
|||
log_message('info', "=== DÉBUT checkAndConvertCompletedAvances ==="); |
|||
|
|||
$avanceModel = new \App\Models\Avance(); |
|||
|
|||
// Récupérer toutes les avances complètes non encore converties |
|||
$completedAvances = $avanceModel |
|||
->where('amount_due', 0) |
|||
->where('is_order', 0) // Pas encore converties |
|||
->where('active', 1) // Encore actives |
|||
->findAll(); |
|||
|
|||
log_message('info', "Avances complètes trouvées : " . count($completedAvances)); |
|||
|
|||
$convertedCount = 0; |
|||
$errorCount = 0; |
|||
|
|||
foreach ($completedAvances as $avance) { |
|||
log_message('info', "Traitement avance complète ID: {$avance['avance_id']}, Client: {$avance['customer_name']}"); |
|||
|
|||
$order_id = $avanceModel->convertToOrder($avance['avance_id']); |
|||
|
|||
if ($order_id) { |
|||
$convertedCount++; |
|||
log_message('info', "✅ Avance {$avance['avance_id']} convertie en commande {$order_id}"); |
|||
} else { |
|||
$errorCount++; |
|||
log_message('error', "❌ Échec conversion avance {$avance['avance_id']}"); |
|||
} |
|||
} |
|||
|
|||
log_message('info', "Avances converties : {$convertedCount}, Erreurs : {$errorCount}"); |
|||
log_message('info', "=== FIN checkAndConvertCompletedAvances ==="); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Envoyer un email via Brevo |
|||
*/ |
|||
function sendEmailWithBrevo($to, $subject, $message) |
|||
{ |
|||
try { |
|||
log_message('info', "Préparation envoi email via Brevo à: {$to}"); |
|||
|
|||
$email = \Config\Services::email(); |
|||
|
|||
// Configuration Brevo depuis le fichier .env |
|||
$config = [ |
|||
'protocol' => 'smtp', |
|||
'SMTPHost' => 'smtp.gmail.com', |
|||
'SMTPUser' => '[email protected]', |
|||
'SMTPPass' => 'loirqovmfuxnasrm', |
|||
'SMTPPort' => 587, |
|||
'SMTPCrypto' => 'tls', |
|||
'protocol' => env('email.protocol', 'smtp'), |
|||
'SMTPHost' => env('email.SMTPHost', 'smtp-relay.brevo.com'), |
|||
'SMTPUser' => env('email.SMTPUser'), |
|||
'SMTPPass' => env('email.SMTPPass'), |
|||
'SMTPPort' => env('email.SMTPPort', 587), |
|||
'SMTPCrypto' => env('email.SMTPCrypto', 'tls'), |
|||
'mailType' => 'html', |
|||
'charset' => 'utf-8', |
|||
'newline' => "\r\n" |
|||
'newline' => "\r\n", |
|||
'wordWrap' => true, |
|||
'validation' => true |
|||
]; |
|||
|
|||
log_message('info', "Configuration Brevo - Host: {$config['SMTPHost']}, Port: {$config['SMTPPort']}, User: {$config['SMTPUser']}"); |
|||
|
|||
$email->initialize($config); |
|||
|
|||
$email->setFrom('[email protected]', 'Système Motorbike - Alertes Avances'); |
|||
// Utilisation de l'email configuré dans .env |
|||
$fromEmail = env('email.fromEmail', '[email protected]'); |
|||
$fromName = env('email.fromName', 'Système Motorbike - Alertes'); |
|||
|
|||
$email->setFrom($fromEmail, $fromName); |
|||
$email->setTo($to); |
|||
$email->setSubject($subject); |
|||
$email->setMessage($message); |
|||
|
|||
log_message('info', "Configuration email terminée, tentative d'envoi..."); |
|||
log_message('info', "Configuration email Brevo terminée, tentative d'envoi..."); |
|||
|
|||
if (!$email->send()) { |
|||
$debugInfo = $email->printDebugger(['headers']); |
|||
log_message('error', "Erreur email à {$to}: " . print_r($debugInfo, true)); |
|||
$debugInfo = $email->printDebugger(['headers', 'subject', 'body']); |
|||
log_message('error', "Erreur email Brevo à {$to}: " . print_r($debugInfo, true)); |
|||
return false; |
|||
} |
|||
|
|||
log_message('info', "Email envoyé avec succès à: {$to}"); |
|||
log_message('info', "Email envoyé avec succès via Brevo à: {$to}"); |
|||
return true; |
|||
|
|||
} catch (\Exception $e) { |
|||
log_message('error', "Exception email à {$to}: " . $e->getMessage()); |
|||
log_message('error', "Exception email Brevo à {$to}: " . $e->getMessage()); |
|||
log_message('error', "Stack trace: " . $e->getTraceAsString()); |
|||
return false; |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue