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') ->where('groups.group_name', 'DAF') ->findAll(); log_message('info', "Utilisateurs DAF trouvés: " . json_encode($users)); $emails = array_column($users, 'email'); log_message('info', "Emails extraits: " . json_encode($emails)); if (empty($emails)) { log_message('error', "Aucun email DAF trouvé"); $db = \Config\Database::connect(); $allGroups = $db->query("SELECT DISTINCT group_name FROM groups")->getResult(); log_message('info', "Groupes disponibles: " . json_encode($allGroups)); return; } foreach ($avances as $avance) { $deadline = date('Y-m-d', strtotime($avance['deadline'])); $daysLeft = (int) ceil((strtotime($deadline) - strtotime($today)) / 86400); log_message('info', "Avance ID: {$avance['avance_id']}, Deadline: {$deadline}, Jours restants: {$daysLeft}"); // Détermination du type d'alerte $alertType = match($daysLeft) { 3 => 'deadline_3_days', 2 => 'deadline_2_days', 1 => 'deadline_1_day', 0 => 'deadline_today', default => null, }; if ($alertType === null) { log_message('info', "Pas d'alerte nécessaire pour {$daysLeft} jours restants"); continue; } log_message('info', "Type d'alerte: {$alertType}"); // Vérification si l'alerte a déjà été envoyée $alreadySent = $alertMailModel ->where('avance_id', $avance['avance_id']) ->where('alert_type', $alertType) ->first(); if ($alreadySent) { log_message('info', "Alerte déjà envoyée pour avance_id={$avance['avance_id']} type={$alertType}"); continue; } // Construction du message $urgencyText = $daysLeft === 0 ? "ÉCHÉANCE AUJOURD'HUI" : "{$daysLeft} jour(s) restant(s)"; $message = "

⚠️ URGENT : Avance approchant de la deadline

ID Avance : {$avance['avance_id']}

Client : {$avance['customer_name']}

Montant avance : " . number_format($avance['avance_amount'], 0, ',', ' ') . " Ar

Montant dû : " . number_format($avance['amount_due'], 0, ',', ' ') . " Ar

Deadline : {$deadline}

Statut : {$urgencyText}

Téléphone client : {$avance['customer_phone']}

Adresse client : {$avance['customer_address']}


Cette avance " . ($daysLeft === 0 ? "arrive à échéance aujourd'hui" : "arrivera à échéance dans {$daysLeft} jour(s)") . ". Action requise immédiatement.

"; $emailsSent = 0; $subject = $daysLeft === 0 ? "⚠️ AVANCE URGENTE - ÉCHÉANCE AUJOURD'HUI" : "⚠️ AVANCE URGENTE - {$daysLeft} jour(s) restant(s)"; 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 { log_message('error', "Échec envoi email à: {$to}"); } } // 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([ 'avance_id' => $avance['avance_id'], 'alert_type' => $alertType, 'sent_date' => date('Y-m-d H:i:s'), 'status' => 'sent', 'created_at' => date('Y-m-d H:i:s'), ]); } else { 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(); } /** * ✅ 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', "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 (configuration hardcodée pour éviter dépendance .env) */ function sendEmailWithBrevo($to, $subject, $message) { try { log_message('info', "Préparation envoi email via Brevo à: {$to}"); $email = \Config\Services::email(); // ✅ Configuration Brevo hardcodée (pas besoin du .env) $config = [ 'protocol' => 'smtp', 'SMTPHost' => 'smtp-relay.brevo.com', 'SMTPUser' => '8356e1002@smtp-brevo.com', 'SMTPPass' => '2JT0KhZOfgSVQk9D', 'SMTPPort' => 587, 'SMTPCrypto' => 'tls', 'mailType' => 'html', 'charset' => 'utf-8', 'newline' => "\r\n", 'wordWrap' => true, 'validation' => true ]; log_message('info', "Configuration Brevo - Host: {$config['SMTPHost']}, Port: {$config['SMTPPort']}, User: {$config['SMTPUser']}"); $email->initialize($config); // ✅ Configuration expéditeur hardcodée $email->setFrom('noreply@motorbike.mg', 'Système Motorbike - Alertes'); $email->setTo($to); $email->setSubject($subject); $email->setMessage($message); log_message('info', "Configuration email Brevo terminée, tentative d'envoi..."); if (!$email->send()) { $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 via Brevo à: {$to}"); return true; } catch (\Exception $e) { log_message('error', "❌ Exception email Brevo à {$to}: " . $e->getMessage()); log_message('error', "Stack trace: " . $e->getTraceAsString()); return false; } }