You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

316 lines
12 KiB

<?php
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';
file_put_contents($cacheFile, time());
$avanceModel = new Avance();
$alertMailModel = new AlertMail();
$usersModel = new Users();
$today = date('Y-m-d');
log_message('info', "Date du jour: {$today}");
// Récupération des avances dans 0-3 jours
$avances = $avanceModel
->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 = "
<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>
<p><strong>Montant dû :</strong> " . number_format($avance['amount_due'], 0, ',', ' ') . " Ar</p>
<p><strong>Deadline :</strong> {$deadline}</p>
<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 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;
$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
*/
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' => 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",
'wordWrap' => true,
'validation' => true
];
log_message('info', "Configuration Brevo - Host: {$config['SMTPHost']}, Port: {$config['SMTPPort']}, User: {$config['SMTPUser']}");
$email->initialize($config);
// Utilisation de l'email configuré dans .env
$fromEmail = env('email.fromEmail', 'noreply@motorbike.mg');
$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 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;
}
}