Compare commits
No commits in common. '17803c1acb38e9fd8d87e1aa840f3a2a5158b227' and '0ce9cb608d1a236dc7238b2e096547fc7c624372' have entirely different histories.
17803c1acb
...
0ce9cb608d
@ -1,121 +1,53 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
namespace Config; |
namespace Config; |
||||
|
|
||||
use CodeIgniter\Config\BaseConfig; |
use CodeIgniter\Config\BaseConfig; |
||||
|
|
||||
class Email extends BaseConfig |
class Email extends BaseConfig |
||||
{ |
{ |
||||
public string $fromEmail = ''; |
public string $fromEmail = '[email protected]'; |
||||
public string $fromName = ''; |
public string $fromName = 'motorbike'; |
||||
public string $recipients = ''; |
public string $recipients = ''; |
||||
|
|
||||
/** |
|
||||
* The "user agent" |
|
||||
*/ |
|
||||
public string $userAgent = 'CodeIgniter'; |
public string $userAgent = 'CodeIgniter'; |
||||
|
|
||||
/** |
public string $protocol = 'smtp'; |
||||
* The mail sending protocol: mail, sendmail, smtp |
|
||||
*/ |
|
||||
public string $protocol = 'mail'; |
|
||||
|
|
||||
/** |
|
||||
* The server path to Sendmail. |
|
||||
*/ |
|
||||
public string $mailPath = '/usr/sbin/sendmail'; |
public string $mailPath = '/usr/sbin/sendmail'; |
||||
|
|
||||
/** |
public string $SMTPHost = 'smtp.gmail.com'; |
||||
* SMTP Server Hostname |
|
||||
*/ |
public string $SMTPUser = '[email protected]'; |
||||
public string $SMTPHost = ''; |
|
||||
|
public string $SMTPPass = 'loirqovmfuxnasrm'; // Mot de passe d’application (App Password) Gmail |
||||
/** |
|
||||
* SMTP Username |
public int $SMTPPort = 587; |
||||
*/ |
|
||||
public string $SMTPUser = ''; |
public int $SMTPTimeout = 30; |
||||
|
|
||||
/** |
|
||||
* SMTP Password |
|
||||
*/ |
|
||||
public string $SMTPPass = ''; |
|
||||
|
|
||||
/** |
|
||||
* SMTP Port |
|
||||
*/ |
|
||||
public int $SMTPPort = 25; |
|
||||
|
|
||||
/** |
|
||||
* SMTP Timeout (in seconds) |
|
||||
*/ |
|
||||
public int $SMTPTimeout = 5; |
|
||||
|
|
||||
/** |
|
||||
* Enable persistent SMTP connections |
|
||||
*/ |
|
||||
public bool $SMTPKeepAlive = false; |
public bool $SMTPKeepAlive = false; |
||||
|
|
||||
/** |
|
||||
* SMTP Encryption. |
|
||||
* |
|
||||
* @var string '', 'tls' or 'ssl'. 'tls' will issue a STARTTLS command |
|
||||
* to the server. 'ssl' means implicit SSL. Connection on port |
|
||||
* 465 should set this to ''. |
|
||||
*/ |
|
||||
public string $SMTPCrypto = 'tls'; |
public string $SMTPCrypto = 'tls'; |
||||
|
|
||||
/** |
|
||||
* Enable word-wrap |
|
||||
*/ |
|
||||
public bool $wordWrap = true; |
public bool $wordWrap = true; |
||||
|
|
||||
/** |
|
||||
* Character count to wrap at |
|
||||
*/ |
|
||||
public int $wrapChars = 76; |
public int $wrapChars = 76; |
||||
|
|
||||
/** |
public string $mailType = 'html'; |
||||
* Type of mail, either 'text' or 'html' |
|
||||
*/ |
|
||||
public string $mailType = 'text'; |
|
||||
|
|
||||
/** |
|
||||
* Character set (utf-8, iso-8859-1, etc.) |
|
||||
*/ |
|
||||
public string $charset = 'UTF-8'; |
public string $charset = 'UTF-8'; |
||||
|
|
||||
/** |
public bool $validate = true; |
||||
* Whether to validate the email address |
|
||||
*/ |
|
||||
public bool $validate = false; |
|
||||
|
|
||||
/** |
|
||||
* Email Priority. 1 = highest. 5 = lowest. 3 = normal |
|
||||
*/ |
|
||||
public int $priority = 3; |
public int $priority = 3; |
||||
|
|
||||
/** |
|
||||
* Newline character. (Use “\r\n” to comply with RFC 822) |
|
||||
*/ |
|
||||
public string $CRLF = "\r\n"; |
public string $CRLF = "\r\n"; |
||||
|
|
||||
/** |
|
||||
* Newline character. (Use “\r\n” to comply with RFC 822) |
|
||||
*/ |
|
||||
public string $newline = "\r\n"; |
public string $newline = "\r\n"; |
||||
|
|
||||
/** |
|
||||
* Enable BCC Batch Mode. |
|
||||
*/ |
|
||||
public bool $BCCBatchMode = false; |
public bool $BCCBatchMode = false; |
||||
|
|
||||
/** |
|
||||
* Number of emails in each BCC batch |
|
||||
*/ |
|
||||
public int $BCCBatchSize = 200; |
public int $BCCBatchSize = 200; |
||||
|
|
||||
/** |
|
||||
* Enable notify message from server |
|
||||
*/ |
|
||||
public bool $DSN = false; |
public bool $DSN = false; |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
namespace App\Controllers; |
||||
|
|
||||
|
use App\Controllers\BaseController; |
||||
|
|
||||
|
class AlertsController extends BaseController |
||||
|
{ |
||||
|
public function check() |
||||
|
{ |
||||
|
helper('alerts'); |
||||
|
checkDeadlineAlerts(); |
||||
|
return "Vérification des alertes effectuée."; |
||||
|
} |
||||
|
} |
||||
@ -6,6 +6,7 @@ use App\Models\Company; |
|||||
use App\Models\Orders; |
use App\Models\Orders; |
||||
use App\Models\Products; |
use App\Models\Products; |
||||
use App\Models\Avance; |
use App\Models\Avance; |
||||
|
use App\Models\User; // Ajout pour récupérer les emails DAF/Directrice |
||||
|
|
||||
class AvanceController extends AdminController |
class AvanceController extends AdminController |
||||
{ |
{ |
||||
@ -28,354 +29,634 @@ class AvanceController extends AdminController |
|||||
return $this->render_template('avances/avance', $data); |
return $this->render_template('avances/avance', $data); |
||||
} |
} |
||||
|
|
||||
public function fetchAvanceData() |
private function isAdmin($user) |
||||
|
{ |
||||
|
return in_array($user['group_name'], ['Conseil', 'Direction']); |
||||
|
} |
||||
|
|
||||
|
private function isCommerciale($user) |
||||
|
{ |
||||
|
return in_array($user['group_name'], ['COMMERCIALE']); |
||||
|
} |
||||
|
|
||||
|
private function isCaissier($user) |
||||
|
{ |
||||
|
return in_array($user['group_name'], ['Caissier']); |
||||
|
} |
||||
|
|
||||
|
private function buildActionButtons($value, $isAdmin, $isOwner) |
||||
|
{ |
||||
|
$buttons = ''; |
||||
|
|
||||
|
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) { |
||||
|
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc(' . $value['avance_id'] . ')" title="Modifier">' |
||||
|
. '<i class="fa fa-pencil"></i></button> '; |
||||
|
} |
||||
|
|
||||
|
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) { |
||||
|
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')" title="Supprimer">' |
||||
|
. '<i class="fa fa-trash"></i></button> '; |
||||
|
} |
||||
|
|
||||
|
if (in_array('viewAvance', $this->permission) && !$isAdmin) { |
||||
|
$buttons .= '<a href="#" data-order-id="' . $value['avance_id'] . '" class="btn btn-default btn-view" title="Voir">' |
||||
|
. '<i class="fa fa-eye"></i></a>'; |
||||
|
} |
||||
|
|
||||
|
return $buttons; |
||||
|
} |
||||
|
|
||||
|
private function buildDataRow($value, $product, $isAdmin, $isCommerciale, $isCaissier, $buttons) |
||||
|
{ |
||||
|
$date_time = date('d-m-Y h:i a', strtotime($value['avance_date'])); |
||||
|
|
||||
|
if ($isAdmin) { |
||||
|
return [ |
||||
|
$value['customer_name'], |
||||
|
$value['customer_phone'], |
||||
|
$value['customer_address'], |
||||
|
$product->getProductNameById($value['product_id']), |
||||
|
number_format((int)$value['gross_amount'], 0, ',', ' '), |
||||
|
number_format((int)$value['avance_amount'], 0, ',', ' '), |
||||
|
number_format((int)$value['amount_due'], 0, ',', ' '), |
||||
|
$date_time, |
||||
|
$buttons, |
||||
|
]; |
||||
|
} elseif ($isCommerciale || $isCaissier) { |
||||
|
return [ |
||||
|
$value['avance_id'], |
||||
|
$product->getProductNameById($value['product_id']), |
||||
|
number_format((int)$value['avance_amount'], 0, ',', ' '), |
||||
|
number_format((int)$value['amount_due'], 0, ',', ' '), |
||||
|
$date_time, |
||||
|
$buttons, |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
return []; |
||||
|
} |
||||
|
|
||||
|
private function fetchAvanceDataGeneric($methodName = 'getAllAvanceData') |
||||
{ |
{ |
||||
helper(['url', 'form']); |
helper(['url', 'form']); |
||||
$Avance = new Avance(); |
$Avance = new Avance(); |
||||
$product = new Products(); |
$product = new Products(); |
||||
$result = ['data' => []]; |
$result = ['data' => []]; |
||||
$data = $Avance->getAllAvanceData(); |
|
||||
|
$data = $Avance->$methodName(); |
||||
$session = session(); |
$session = session(); |
||||
$users = $session->get('user'); |
$users = $session->get('user'); |
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); |
|
||||
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']); |
$isAdmin = $this->isAdmin($users); |
||||
$isCaissier = in_array($users['group_name'], ['Caissier']); |
$isCommerciale = $this->isCommerciale($users); |
||||
|
$isCaissier = $this->isCaissier($users); |
||||
|
|
||||
foreach ($data as $key => $value) { |
foreach ($data as $key => $value) { |
||||
$isOwner = $users['id'] === $value['user_id']; |
$isOwner = $users['id'] === $value['user_id']; |
||||
$date_time = date('d-m-Y h:i a', strtotime($value['avance_date'])); |
|
||||
|
$buttons = $this->buildActionButtons($value, $isAdmin, $isOwner); |
||||
// Boutons d’action |
|
||||
$buttons = ''; |
$row = $this->buildDataRow($value, $product, $isAdmin, $isCommerciale, $isCaissier, $buttons); |
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) { |
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc('. $value['avance_id'] .')">' |
if (!empty($row)) { |
||||
. '<i class="fa fa-pencil"></i></button>'; |
$result['data'][] = $row; |
||||
} |
} |
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) { |
} |
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')"><i class="fa fa-trash"></i></button>'; |
|
||||
|
return $this->response->setJSON($result); |
||||
|
} |
||||
|
|
||||
|
public function fetchAvanceData() |
||||
|
{ |
||||
|
return $this->fetchAvanceDataGeneric('getAllAvanceData'); |
||||
|
} |
||||
|
|
||||
|
public function fetchAvanceBecameOrder() |
||||
|
{ |
||||
|
return $this->fetchAvanceDataGeneric('getAllAvanceData1'); |
||||
|
} |
||||
|
|
||||
|
public function fetcheExpiredAvance() |
||||
|
{ |
||||
|
return $this->fetchAvanceDataGeneric('getAllAvanceData2'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Méthode pour vérifier et envoyer des emails d'alerte 3 jours avant deadline |
||||
|
* À exécuter via CRON job quotidiennement |
||||
|
*/ |
||||
|
public function checkDeadlineAlerts() |
||||
|
{ |
||||
|
try { |
||||
|
$Avance = new Avance(); |
||||
|
$Products = new Products(); |
||||
|
|
||||
|
// Récupérer toutes les avances actives non converties en commandes |
||||
|
$avances = $Avance->getAvancesNearDeadline(3); // 3 jours avant deadline |
||||
|
|
||||
|
if (!empty($avances)) { |
||||
|
foreach ($avances as $avance) { |
||||
|
// Vérifier si l'email n'a pas déjà été envoyé pour cette avance |
||||
|
if (!$this->hasEmailBeenSent($avance['avance_id'])) { |
||||
|
$this->sendDeadlineAlert($avance, $Products); |
||||
|
$this->markEmailAsSent($avance['avance_id']); |
||||
|
} |
||||
|
} |
||||
} |
} |
||||
if (in_array('viewAvance', $this->permission) && !$isAdmin) { |
|
||||
$buttons .= ' <a href="#" data-order-id="'.$value['id'].'" class="btn btn-default btn-view" title="Voir"><i class="fa fa-eye"></i></a>'; |
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'messages' => 'Vérification des alertes terminée', |
||||
|
'alerts_sent' => count($avances) |
||||
|
]); |
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Erreur vérification deadline: " . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur lors de la vérification des deadlines' |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Envoyer un email d'alerte au DAF et à la Directrice |
||||
|
*/ |
||||
|
private function sendDeadlineAlert($avance, $Products) |
||||
|
{ |
||||
|
try { |
||||
|
$email = \Config\Services::email(); |
||||
|
|
||||
|
// Configuration email (à adapter selon votre config) |
||||
|
$email->setFrom('[email protected]', 'Système de Gestion des Avances'); |
||||
|
|
||||
|
// Récupérer les emails du DAF et de la Directrice |
||||
|
$recipients = $this->getDAFAndDirectriceEmails($avance['store_id']); |
||||
|
$email->setTo($recipients); |
||||
|
|
||||
|
$email->setSubject('⚠️ ALERTE: Avance arrive à échéance dans 3 jours'); |
||||
|
|
||||
|
// Récupérer le nom du produit |
||||
|
$productName = $Products->getProductNameById($avance['product_id']); |
||||
|
|
||||
|
// Calcul des jours restants |
||||
|
$deadline = new \DateTime($avance['deadline']); |
||||
|
$today = new \DateTime(); |
||||
|
$daysRemaining = $today->diff($deadline)->days; |
||||
|
|
||||
|
// Corps de l'email |
||||
|
$message = $this->buildEmailMessage($avance, $productName, $daysRemaining); |
||||
|
$email->setMessage($message); |
||||
|
|
||||
|
// Envoyer l'email |
||||
|
if ($email->send()) { |
||||
|
log_message('info', "Email d'alerte envoyé pour l'avance ID: " . $avance['avance_id']); |
||||
|
return true; |
||||
|
} else { |
||||
|
log_message('error', "Échec envoi email pour avance ID: " . $avance['avance_id'] . " - " . $email->printDebugger()); |
||||
|
return false; |
||||
} |
} |
||||
if ($isAdmin) { |
} catch (\Exception $e) { |
||||
$row = [ |
log_message('error', "Erreur envoi email alerte: " . $e->getMessage()); |
||||
$value['customer_name'], |
return false; |
||||
$value['customer_phone'], |
} |
||||
$value['customer_address'], |
} |
||||
$product->getProductNameById($value['product_id']), |
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '), |
/** |
||||
number_format((int)$value['avance_amount'], 0, ',', ' '), |
* Récupérer les emails du DAF et de la Directrice |
||||
number_format((int)$value['amount_due'], 0, ',', ' '), |
*/ |
||||
$date_time, |
private function getDAFAndDirectriceEmails($store_id) |
||||
$buttons, |
{ |
||||
]; |
$User = new User(); |
||||
// dd($row);die; |
$emails = []; |
||||
$result['data'][] = $row; |
|
||||
|
// Récupérer les utilisateurs avec les rôles DAF et Direction pour le store donné |
||||
|
$dafUsers = $User->getUsersByRole('DAF', $store_id); |
||||
|
$directionUsers = $User->getUsersByRole('Direction', $store_id); |
||||
|
|
||||
|
// Extraire les emails |
||||
|
foreach ($dafUsers as $user) { |
||||
|
if (!empty($user['email'])) { |
||||
|
$emails[] = $user['email']; |
||||
} |
} |
||||
if ($isCommerciale || $isCaissier) { |
} |
||||
$row = [ |
|
||||
$value['avance_id'], |
foreach ($directionUsers as $user) { |
||||
number_format((int)$value['avance_amount'], 0, ',', ' '), |
if (!empty($user['email'])) { |
||||
number_format((int)$value['amount_due'], 0, ',', ' '), |
$emails[] = $user['email']; |
||||
$date_time, |
|
||||
$buttons, |
|
||||
]; |
|
||||
$result['data'][] = $row; |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
// Si aucun email trouvé, utiliser des emails par défaut (à configurer) |
||||
|
if (empty($emails)) { |
||||
|
$emails = [ |
||||
|
'[email protected]', |
||||
|
'[email protected]' |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
return array_unique($emails); // Éviter les doublons |
||||
|
} |
||||
|
|
||||
return $this->response->setJSON($result); |
/** |
||||
|
* Construire le message de l'email |
||||
|
*/ |
||||
|
private function buildEmailMessage($avance, $productName, $daysRemaining) |
||||
|
{ |
||||
|
$typeAvance = strtoupper($avance['type_avance']); |
||||
|
$deadlineFormatted = date('d/m/Y', strtotime($avance['deadline'])); |
||||
|
$avanceDateFormatted = date('d/m/Y à H:i', strtotime($avance['avance_date'])); |
||||
|
$amountDueFormatted = number_format($avance['amount_due'], 0, ',', ' ') . ' FCFA'; |
||||
|
|
||||
|
$urgencyClass = $daysRemaining <= 1 ? 'style="color: red; font-weight: bold;"' : ''; |
||||
|
|
||||
|
return " |
||||
|
<html> |
||||
|
<head> |
||||
|
<style> |
||||
|
.container { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; } |
||||
|
.header { background-color: #f8f9fa; padding: 20px; text-align: center; border-radius: 5px; } |
||||
|
.alert { background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; margin: 20px 0; border-radius: 5px; } |
||||
|
.details { background-color: #f8f9fa; padding: 15px; margin: 10px 0; border-radius: 5px; } |
||||
|
.urgent { color: #dc3545; font-weight: bold; } |
||||
|
.footer { margin-top: 30px; padding: 15px; background-color: #e9ecef; border-radius: 5px; font-size: 12px; } |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class='container'> |
||||
|
<div class='header'> |
||||
|
<h2>⚠️ ALERTE DEADLINE AVANCE</h2> |
||||
|
</div> |
||||
|
|
||||
|
<div class='alert'> |
||||
|
<p><strong " . $urgencyClass . ">Une avance arrive à échéance dans {$daysRemaining} jour(s) !</strong></p> |
||||
|
</div> |
||||
|
|
||||
|
<div class='details'> |
||||
|
<h3>Détails de l'avance :</h3> |
||||
|
<ul> |
||||
|
<li><strong>ID Avance :</strong> #{$avance['avance_id']}</li> |
||||
|
<li><strong>Type d'avance :</strong> {$typeAvance}</li> |
||||
|
<li><strong>Client :</strong> {$avance['customer_name']}</li> |
||||
|
<li><strong>Téléphone :</strong> {$avance['customer_phone']}</li> |
||||
|
<li><strong>Adresse :</strong> {$avance['customer_address']}</li> |
||||
|
<li><strong>CIN :</strong> {$avance['customer_cin']}</li> |
||||
|
<li><strong>Produit :</strong> {$productName}</li> |
||||
|
<li><strong>Montant restant dû :</strong> <span class='urgent'>{$amountDueFormatted}</span></li> |
||||
|
<li><strong>Date avance :</strong> {$avanceDateFormatted}</li> |
||||
|
<li><strong>Date limite :</strong> <span class='urgent'>{$deadlineFormatted}</span></li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
|
||||
|
<div class='alert'> |
||||
|
<p><strong>Action requise :</strong></p> |
||||
|
<p>Veuillez contacter le client pour régulariser le paiement avant l'échéance ou prendre les mesures appropriées.</p> |
||||
|
</div> |
||||
|
|
||||
|
<div class='footer'> |
||||
|
<p>Cet email a été généré automatiquement par le système de gestion des avances.</p> |
||||
|
<p>Date d'envoi : " . date('d/m/Y à H:i') . "</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
||||
|
"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Vérifier si un email a déjà été envoyé pour cette avance |
||||
|
*/ |
||||
|
private function hasEmailBeenSent($avance_id) |
||||
|
{ |
||||
|
$db = \Config\Database::connect(); |
||||
|
$query = $db->query("SELECT id FROM email_alerts WHERE avance_id = ? AND alert_type = 'deadline_3days'", [$avance_id]); |
||||
|
return $query->getNumRows() > 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Marquer l'email comme envoyé |
||||
|
*/ |
||||
|
private function markEmailAsSent($avance_id) |
||||
|
{ |
||||
|
$db = \Config\Database::connect(); |
||||
|
$data = [ |
||||
|
'avance_id' => $avance_id, |
||||
|
'alert_type' => 'deadline_3days', |
||||
|
'sent_date' => date('Y-m-d H:i:s'), |
||||
|
'status' => 'sent' |
||||
|
]; |
||||
|
|
||||
|
$db->table('email_alerts')->insert($data); |
||||
} |
} |
||||
|
|
||||
public function createAvance() |
public function createAvance() |
||||
{ |
{ |
||||
// $this->verifyRole('createAvance'); |
$this->verifyRole('createAvance'); |
||||
$data['page_title'] = $this->pageTitle; |
|
||||
|
|
||||
$Avance = new Avance(); |
if ($this->request->getMethod() !== 'post') { |
||||
$Products = new Products(); |
return $this->response->setJSON([ |
||||
$Notification = New NotificationController(); |
'success' => false, |
||||
|
'messages' => 'Méthode non autorisée' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
if ($this->request->getMethod() === 'post') { |
try { |
||||
$session = session(); |
$session = session(); |
||||
$users = $session->get('user'); |
$users = $session->get('user'); |
||||
|
|
||||
|
$Avance = new Avance(); |
||||
|
$Products = new Products(); |
||||
|
$Notification = new NotificationController(); |
||||
|
|
||||
|
$validation = \Config\Services::validation(); |
||||
|
$validation->setRules([ |
||||
|
'customer_name_avance' => 'required|min_length[2]', |
||||
|
'customer_phone_avance' => 'required', |
||||
|
'customer_address_avance' => 'required', |
||||
|
'customer_cin_avance' => 'required', |
||||
|
'id_product' => 'required|numeric', |
||||
|
'avance_amount' => 'required|numeric|greater_than[0]', |
||||
|
'type_avance' => 'required|in_list[terre,mere]' |
||||
|
]); |
||||
|
|
||||
|
if (!$validation->withRequest($this->request)->run()) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Données invalides: ' . implode(', ', $validation->getErrors()) |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
$avance_date = date('Y-m-d H:i:s'); |
||||
|
|
||||
|
// Calcul automatique de la deadline selon le type d'avance |
||||
|
$type_avance = $this->request->getPost('type_avance'); |
||||
|
if ($type_avance === 'terre') { |
||||
|
$deadline = date('Y-m-d', strtotime($avance_date . ' +15 days')); |
||||
|
} elseif ($type_avance === 'mere') { |
||||
|
$deadline = date('Y-m-d', strtotime($avance_date . ' +2 months')); |
||||
|
} else { |
||||
|
$deadline = null; // fallback si jamais |
||||
|
} |
||||
|
|
||||
$data = [ |
$data = [ |
||||
|
'type_avance' => $type_avance, |
||||
'customer_name' => $this->request->getPost('customer_name_avance'), |
'customer_name' => $this->request->getPost('customer_name_avance'), |
||||
'customer_address' => $this->request->getPost('customer_address_avance'), |
'customer_address' => $this->request->getPost('customer_address_avance'), |
||||
'customer_phone' => $this->request->getPost('customer_phone_avance'), |
'customer_phone' => $this->request->getPost('customer_phone_avance'), |
||||
'customer_cin' => $this->request->getPost('customer_cin_avance'), |
'customer_cin' => $this->request->getPost('customer_cin_avance'), |
||||
'avance_date' => date('Y-m-d'), |
'avance_date' => $avance_date, |
||||
'user_id' => $users['id'], |
'deadline' => $deadline, |
||||
|
'user_id' => $users['id'], |
||||
'store_id' => $users['store_id'], |
'store_id' => $users['store_id'], |
||||
'product_id' => $this->request->getPost('id_product'), |
'product_id' => (int)$this->request->getPost('id_product'), |
||||
'gross_amount' => (float)$this->request->getPost('gross_amount'), |
'gross_amount' => (float)$this->request->getPost('gross_amount'), |
||||
'avance_amount' => (float)$this->request->getPost('avance_amount'), |
'avance_amount' => (float)$this->request->getPost('avance_amount'), |
||||
'amount_due' => (float)$this->request->getPost('amount_due'), |
'amount_due' => (float)$this->request->getPost('amount_due'), |
||||
'is_order' => (float)0, |
'is_order' => 0, |
||||
'active' => 1, |
'active' => 1, |
||||
]; |
]; |
||||
|
|
||||
if($avance_id = $Avance->createAvance($data)){ |
if ($avance_id = $Avance->createAvance($data)) { |
||||
$product = new Products(); |
$Products->update($data['product_id'], ['product_sold' => 1]); |
||||
$product->update((int)$this->request->getPost('id_product'), ['product_sold' => 1]); |
|
||||
$Notification->createNotification('Une avance a été créé', "Conseil",(int)$users['store_id'], 'avances'); |
$Notification->createNotification( |
||||
return $this->response->setJSON([ |
'Une nouvelle avance a été créée', |
||||
'success' => true, |
"Conseil", |
||||
'messages' => 'Avance créé avec succès !' |
(int)$users['store_id'], |
||||
]); |
'avances' |
||||
} |
); |
||||
else{ |
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'messages' => 'Avance créée avec succès !', |
||||
|
'avance_id' => $avance_id |
||||
|
]); |
||||
|
} else { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur lors de la création de l\'avance' |
||||
|
]); |
||||
|
} |
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Erreur création avance: " . $e->getMessage()); |
||||
return $this->response->setJSON([ |
return $this->response->setJSON([ |
||||
'success' => false, |
'success' => false, |
||||
'messages' => 'Une erreur est survenue lors de la création d\une avance !' |
'messages' => 'Une erreur interne est survenue' |
||||
]); |
]); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
public function updateAvance(int $id) |
public function updateAvance(int $id) |
||||
{ |
{ |
||||
$this->verifyRole('updateAvance'); |
$this->verifyRole('updateAvance'); |
||||
$data['page_title'] = $this->pageTitle; |
|
||||
|
|
||||
$Products = new Products(); |
if ($this->request->getMethod() !== 'post') { |
||||
$Avance = new Avance(); |
return $this->response->setJSON([ |
||||
$session = session(); |
'success' => false, |
||||
$users = $session->get('user'); |
'messages' => 'Méthode non autorisée' |
||||
if ($this->request->getMethod() === 'post') { |
]); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
$session = session(); |
||||
|
$users = $session->get('user'); |
||||
|
|
||||
|
$Avance = new Avance(); |
||||
|
$Products = new Products(); |
||||
|
$Orders = new Orders(); |
||||
|
$Company = new Company(); |
||||
|
$Notification = new NotificationController(); |
||||
|
|
||||
|
$validation = \Config\Services::validation(); |
||||
|
$validation->setRules([ |
||||
|
'customer_name_avance_edit' => 'required|min_length[2]', |
||||
|
'customer_phone_avance_edit' => 'required', |
||||
|
'customer_address_avance_edit' => 'required', |
||||
|
'customer_cin_avance_edit' => 'required', |
||||
|
'id_product_edit' => 'required|numeric', |
||||
|
'avance_amount_edit' => 'required|numeric|greater_than[0]', |
||||
|
'type_avance_edit' => 'required|in_list[terre,mere]' |
||||
|
]); |
||||
|
|
||||
|
if (!$validation->withRequest($this->request)->run()) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Données invalides: ' . implode(', ', $validation->getErrors()) |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
// Récupérer la date de création actuelle de l'avance pour recalculer deadline |
||||
|
$currentAvance = $Avance->find($id); |
||||
|
if (!$currentAvance) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Avance introuvable.' |
||||
|
]); |
||||
|
} |
||||
|
$avance_date = $currentAvance['avance_date']; |
||||
|
|
||||
|
// Calcul automatique deadline selon le type d'avance |
||||
|
$type_avance = $this->request->getPost('type_avance_edit'); |
||||
|
if ($type_avance === 'terre') { |
||||
|
$deadline = date('Y-m-d', strtotime($avance_date . ' +15 days')); |
||||
|
} elseif ($type_avance === 'mere') { |
||||
|
$deadline = date('Y-m-d', strtotime($avance_date . ' +2 months')); |
||||
|
} else { |
||||
|
$deadline = null; |
||||
|
} |
||||
|
|
||||
$data = [ |
$data = [ |
||||
'customer_name' => $this->request->getPost('customer_name_avance_edit'), |
'type_avance' => $type_avance, |
||||
'customer_address'=> $this->request->getPost('customer_address_avance_edit'), |
'customer_name' => $this->request->getPost('customer_name_avance_edit'), |
||||
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'), |
'customer_address' => $this->request->getPost('customer_address_avance_edit'), |
||||
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'), |
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'), |
||||
'gross_amout' => $this->request->getPost('gros_amount_edit'), |
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'), |
||||
'avance_amount' => (int)$this->request->getPost('avance_amount_edit'), |
'gross_amount' => (float)$this->request->getPost('gross_amount_edit'), |
||||
'amount_due' => (int)$this->request->getPost('amount_due_edit'), |
'avance_amount' => (float)$this->request->getPost('avance_amount_edit'), |
||||
'product_id' => $this->request->getPost('id_product_edit'), |
'amount_due' => (float)$this->request->getPost('amount_due_edit'), |
||||
|
'product_id' => (int)$this->request->getPost('id_product_edit'), |
||||
|
'deadline' => $deadline, |
||||
]; |
]; |
||||
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4)); |
|
||||
$Company = new Company(); |
$amount_due = $data['amount_due']; |
||||
$company = $Company->getCompanyData(1); |
|
||||
$company['vat_charge_value'] > 0; |
|
||||
$service_charge_rate = $company['service_charge_value']; |
|
||||
$vat_charge_rate = $company['vat_charge_value']; |
|
||||
$gross_amount = $this->request->getPost('gross_amount_edit'); |
|
||||
$vat_charge = ($gross_amount / 100) * $vat_charge_rate; |
|
||||
$amount_due = (int)$this->request->getPost('amount_due_edit'); |
|
||||
$product_id = (array)$this->request->getPost('id_product_edit'); |
|
||||
|
|
||||
if ($amount_due <= 0) { |
if ($amount_due <= 0) { |
||||
$Orders = new Orders(); |
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4)); |
||||
|
$company = $Company->getCompanyData(1); |
||||
$data = [ |
|
||||
'bill_no' => $bill_no, |
$service_charge_rate = $company['service_charge_value'] ?? 0; |
||||
'customer_name' => $this->request->getPost('customer_name_avance_edit'), |
$vat_charge_rate = $company['vat_charge_value'] ?? 0; |
||||
'customer_address'=> $this->request->getPost('customer_address_avance_edit'), |
$gross_amount = $data['gross_amount']; |
||||
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'), |
$vat_charge = ($gross_amount / 100) * $vat_charge_rate; |
||||
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'), |
|
||||
'gross_amout' => $gross_amount, |
$order_data = [ |
||||
'net_amount' => $gross_amount, |
'bill_no' => $bill_no, |
||||
'date_time' => date('Y-m-d H:i:s'), |
'customer_name' => $data['customer_name'], |
||||
|
'customer_address' => $data['customer_address'], |
||||
|
'customer_phone' => $data['customer_phone'], |
||||
|
'customer_cin' => $data['customer_cin'], |
||||
|
'gross_amount' => $gross_amount, |
||||
|
'net_amount' => $gross_amount, |
||||
|
'date_time' => date('Y-m-d H:i:s'), |
||||
'service_charge_rate' => $service_charge_rate, |
'service_charge_rate' => $service_charge_rate, |
||||
'vat_charge_rate' => $vat_charge_rate, |
'vat_charge_rate' => $vat_charge_rate, |
||||
'vat_charge' => $vat_charge, |
'vat_charge' => $vat_charge, |
||||
'discount' => (int) 0, |
'discount' => 0, |
||||
'paid_status' => 1, |
'paid_status' => 1, |
||||
'user_id' => $users['id'], |
'user_id' => $users['id'], |
||||
'store_id' => $users['store_id'], |
'store_id' => $users['store_id'], |
||||
'amount_value' => $gross_amount, |
'amount_value' => $gross_amount, |
||||
'rate_value' => $gross_amount, |
'rate_value' => $gross_amount, |
||||
]; |
]; |
||||
$data1 = ['is_order' => 1]; |
|
||||
if($Orders->create($data,$product_id)){ |
$product_id = [$data['product_id']]; |
||||
$Avance->updateAvance($id,$data1); |
|
||||
$Notification = New NotificationController(); |
if ($Orders->create($order_data, $product_id)) { |
||||
$Notification->createNotification('Une commande a été créé', "Conseil",(int)$users['store_id'], 'orders'); |
$Avance->updateAvance($id, ['is_order' => 1]); |
||||
|
$Notification->createNotification( |
||||
|
'Une avance a été convertie en commande', |
||||
|
"Conseil", |
||||
|
(int)$users['store_id'], |
||||
|
'orders' |
||||
|
); |
||||
|
|
||||
return $this->response->setJSON([ |
return $this->response->setJSON([ |
||||
'success' => true, |
'success' => true, |
||||
'messages' => 'success. Avance convertie en commande avec succès.' |
'messages' => 'Avance convertie en commande avec succès.' |
||||
]); |
]); |
||||
} |
} else { |
||||
else{ |
|
||||
return $this->response->setJSON([ |
return $this->response->setJSON([ |
||||
'success' => false, |
'success' => false, |
||||
'messages' => 'Erreur lors de la convertion de l\'avance' |
'messages' => 'Erreur lors de la conversion de l\'avance en commande' |
||||
]); |
]); |
||||
} |
} |
||||
} |
} else { |
||||
else{ |
|
||||
if ($Avance->updateAvance($id, $data)) { |
if ($Avance->updateAvance($id, $data)) { |
||||
return $this->response->setJSON([ |
return $this->response->setJSON([ |
||||
'success' => true, |
'success' => true, |
||||
'messages' => 'success', 'Avance mise à jour avec succès.' |
'messages' => 'Avance mise à jour avec succès.' |
||||
|
|
||||
]); |
]); |
||||
} else { |
} else { |
||||
return $this->response->setJSON([ |
return $this->response->setJSON([ |
||||
'success' => true, |
'success' => false, |
||||
'messages' => 'Errors', 'Une erreur est survenue lors de la mise à jour.' |
'messages' => 'Erreur lors de la mise à jour de l\'avance.' |
||||
]); |
]); |
||||
} |
} |
||||
} |
} |
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Erreur mise à jour avance: " . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Une erreur interne est survenue' |
||||
|
]); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
public function removeAvance() |
public function removeAvance() |
||||
{ |
{ |
||||
$this->verifyRole('deleteAvance'); |
$this->verifyRole('deleteAvance'); |
||||
$avance_id = $this->request->getPost('avance_id'); |
|
||||
$product_id = $this->request->getPost('product_id'); |
try { |
||||
$response = []; |
$avance_id = $this->request->getPost('avance_id'); |
||||
|
$product_id = $this->request->getPost('product_id'); |
||||
|
|
||||
$Avance = new Avance(); |
if (!$avance_id || !$product_id) { |
||||
if ($Avance->removeAvance($avance_id)) { |
return $this->response->setJSON([ |
||||
$product = new Products(); |
'success' => false, |
||||
$product->update($product_id, ['product_sold' => 0]); |
'messages' => 'Données manquantes pour la suppression' |
||||
$response['success'] = true; |
]); |
||||
$response['messages'] = "Avance supprimée avec succès. Ce produit peut désormais être réservé à nouveau."; |
} |
||||
} else { |
|
||||
$response['success'] = false; |
|
||||
$response['messages'] = "une erreur est survenue lors de la suppression d'une avance"; |
|
||||
} |
|
||||
return $this->response->setJSON($response); |
|
||||
} |
|
||||
|
|
||||
public function fetchSingleAvance($avance_id) |
|
||||
{ |
|
||||
$this->verifyRole('updateAvance'); |
|
||||
|
|
||||
try { |
|
||||
$avanceModel = new Avance(); |
|
||||
|
|
||||
$data = $avanceModel->fetchSingleAvance($avance_id); |
|
||||
|
|
||||
return $this->response->setJSON($data); |
|
||||
} |
|
||||
catch (\Throwable $th) { |
|
||||
log_message('error', "Erreur lors de la récupération d'une avance: " . $th->getMessage()); |
|
||||
|
|
||||
return $this->response |
$Avance = new Avance(); |
||||
->setStatusCode(500) |
$Products = new Products(); |
||||
->setJSON(['error' => 'Une erreur interne est survenue. Lors de la création d\'une avance']); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public function fetchAvanceBecameOrder() |
if ($Avance->removeAvance($avance_id)) { |
||||
{ |
$Products->update($product_id, ['product_sold' => 0]); |
||||
helper(['url', 'form']); |
|
||||
$Avance = new Avance(); |
return $this->response->setJSON([ |
||||
$product = new Products(); |
'success' => true, |
||||
$result = ['data' => []]; |
'messages' => "Avance supprimée avec succès. Le produit peut être réservé à nouveau." |
||||
$data = $Avance->getAllAvanceData1(); |
]); |
||||
$session = session(); |
} else { |
||||
$users = $session->get('user'); |
return $this->response->setJSON([ |
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); |
'success' => false, |
||||
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']); |
'messages' => "Erreur lors de la suppression de l'avance" |
||||
$isCaissier = in_array($users['group_name'], ['Caissier']); |
]); |
||||
foreach ($data as $key => $value) { |
|
||||
$isOwner = $users['id'] === $value['user_id']; |
|
||||
$date_time = date('d-m-Y h:i a', strtotime($value['avance_date'])); |
|
||||
|
|
||||
// Boutons d’action |
|
||||
$buttons = ''; |
|
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) { |
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc('. $value['avance_id'] .')">' |
|
||||
. '<i class="fa fa-pencil"></i></button>'; |
|
||||
} |
|
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) { |
|
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')"><i class="fa fa-trash"></i></button>'; |
|
||||
} |
|
||||
if (in_array('viewAvance', $this->permission) && !$isAdmin) { |
|
||||
$buttons .= ' <a href="#" data-order-id="'.$value['id'].'" class="btn btn-default btn-view" title="Voir"><i class="fa fa-eye"></i></a>'; |
|
||||
} |
|
||||
if ($isAdmin) { |
|
||||
$row = [ |
|
||||
$value['customer_name'], |
|
||||
$value['customer_phone'], |
|
||||
$value['customer_address'], |
|
||||
$product->getProductNameById($value['product_id']), |
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '), |
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '), |
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '), |
|
||||
$date_time, |
|
||||
$buttons, |
|
||||
]; |
|
||||
// dd($row);die; |
|
||||
$result['data'][] = $row; |
|
||||
} |
|
||||
if ($isCommerciale || $isCaissier) { |
|
||||
$row = [ |
|
||||
$value['avance_id'], |
|
||||
$product->getProductNameById($value['product_id']), |
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '), |
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '), |
|
||||
$date_time, |
|
||||
$buttons, |
|
||||
]; |
|
||||
$result['data'][] = $row; |
|
||||
} |
} |
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Erreur suppression avance: " . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Une erreur interne est survenue' |
||||
|
]); |
||||
} |
} |
||||
|
|
||||
return $this->response->setJSON($result); |
|
||||
} |
} |
||||
|
|
||||
public function fetcheExpiredAvance() |
public function fetchSingleAvance($avance_id) |
||||
{ |
{ |
||||
helper(['url', 'form']); |
$this->verifyRole('updateAvance'); |
||||
$Avance = new Avance(); |
|
||||
$product = new Products(); |
try { |
||||
$result = ['data' => []]; |
if (!$avance_id || !is_numeric($avance_id)) { |
||||
$data = $Avance->getAllAvanceData2(); |
return $this->response->setStatusCode(400)->setJSON([ |
||||
$session = session(); |
'error' => 'ID d\'avance invalide' |
||||
$users = $session->get('user'); |
]); |
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); |
|
||||
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']); |
|
||||
$isCaissier = in_array($users['group_name'], ['Caissier']); |
|
||||
foreach ($data as $key => $value) { |
|
||||
$isOwner = $users['id'] === $value['user_id']; |
|
||||
$date_time = date('d-m-Y h:i a', strtotime($value['avance_date'])); |
|
||||
|
|
||||
// Boutons d’action |
|
||||
$buttons = ''; |
|
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) { |
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc('. $value['avance_id'] .')">' |
|
||||
. '<i class="fa fa-pencil"></i></button>'; |
|
||||
} |
|
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) { |
|
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')"><i class="fa fa-trash"></i></button>'; |
|
||||
} |
|
||||
if (in_array('viewAvance', $this->permission) && !$isAdmin) { |
|
||||
$buttons .= ' <a href="#" data-order-id="'.$value['id'].'" class="btn btn-default btn-view" title="Voir"><i class="fa fa-eye"></i></a>'; |
|
||||
} |
|
||||
if ($isAdmin) { |
|
||||
$row = [ |
|
||||
$value['customer_name'], |
|
||||
$value['customer_phone'], |
|
||||
$value['customer_address'], |
|
||||
$product->getProductNameById($value['product_id']), |
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '), |
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '), |
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '), |
|
||||
$date_time, |
|
||||
$buttons, |
|
||||
]; |
|
||||
// dd($row);die; |
|
||||
$result['data'][] = $row; |
|
||||
} |
} |
||||
if ($isCommerciale || $isCaissier) { |
|
||||
$row = [ |
$avanceModel = new Avance(); |
||||
$value['avance_id'], |
$data = $avanceModel->fetchSingleAvance($avance_id); |
||||
$product->getProductNameById($value['product_id']), |
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '), |
if (!$data) { |
||||
number_format((int)$value['amount_due'], 0, ',', ' '), |
return $this->response->setStatusCode(404)->setJSON([ |
||||
$date_time, |
'error' => 'Avance non trouvée' |
||||
$buttons, |
]); |
||||
]; |
|
||||
$result['data'][] = $row; |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
return $this->response->setJSON($result); |
return $this->response->setJSON($data); |
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Erreur récupération avance: " . $e->getMessage()); |
||||
|
return $this->response->setStatusCode(500)->setJSON([ |
||||
|
'error' => 'Erreur interne lors de la récupération de l\'avance' |
||||
|
]); |
||||
|
} |
||||
} |
} |
||||
|
} |
||||
} |
|
||||
@ -1,58 +1,22 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
namespace App\Controllers; |
namespace App\Controllers; |
||||
|
|
||||
use CodeIgniter\Controller; |
use CodeIgniter\Controller; |
||||
use CodeIgniter\HTTP\CLIRequest; |
|
||||
use CodeIgniter\HTTP\IncomingRequest; |
|
||||
use CodeIgniter\HTTP\RequestInterface; |
|
||||
use CodeIgniter\HTTP\ResponseInterface; |
|
||||
use Psr\Log\LoggerInterface; |
|
||||
|
|
||||
/** |
class BaseController extends Controller |
||||
* Class BaseController |
|
||||
* |
|
||||
* BaseController provides a convenient place for loading components |
|
||||
* and performing functions that are needed by all your controllers. |
|
||||
* Extend this class in any new controllers: |
|
||||
* class Home extends BaseController |
|
||||
* |
|
||||
* For security be sure to declare any new methods as protected or private. |
|
||||
*/ |
|
||||
abstract class BaseController extends Controller |
|
||||
{ |
{ |
||||
/** |
|
||||
* Instance of the main Request object. |
|
||||
* |
|
||||
* @var CLIRequest|IncomingRequest |
|
||||
*/ |
|
||||
protected $request; |
|
||||
|
|
||||
/** |
|
||||
* An array of helpers to be loaded automatically upon |
|
||||
* class instantiation. These helpers will be available |
|
||||
* to all other controllers that extend BaseController. |
|
||||
* |
|
||||
* @var list<string> |
|
||||
*/ |
|
||||
protected $helpers = []; |
protected $helpers = []; |
||||
|
|
||||
/** |
public function initController(\CodeIgniter\HTTP\RequestInterface $request, |
||||
* Be sure to declare properties for any property fetch you initialized. |
\CodeIgniter\HTTP\ResponseInterface $response, |
||||
* The creation of dynamic property is deprecated in PHP 8.2. |
\Psr\Log\LoggerInterface $logger) |
||||
*/ |
|
||||
// protected $session; |
|
||||
|
|
||||
/** |
|
||||
* @return void |
|
||||
*/ |
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) |
|
||||
{ |
{ |
||||
// Do Not Edit This Line |
|
||||
parent::initController($request, $response, $logger); |
parent::initController($request, $response, $logger); |
||||
|
|
||||
// Preload any models, libraries, etc, here. |
helper('alerts'); |
||||
|
|
||||
// E.g.: $this->session = \Config\Services::session(); |
if (function_exists('checkDeadlineAlerts')) { |
||||
|
checkDeadlineAlerts(); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,25 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Controllers; |
||||
|
|
||||
|
use CodeIgniter\Controller; |
||||
|
|
||||
|
class TestDeadline extends Controller |
||||
|
{ |
||||
|
public function index() |
||||
|
{ |
||||
|
// Charger le helper qui contient ta fonction |
||||
|
helper('alerts'); // si ton fichier s'appelle alerts_helper.php |
||||
|
|
||||
|
// 🔹 Supprimer le cache 24h pour forcer l'exécution |
||||
|
$cacheFile = WRITEPATH . 'cache/check_deadline_last_run.txt'; |
||||
|
if (file_exists($cacheFile)) { |
||||
|
unlink($cacheFile); |
||||
|
} |
||||
|
|
||||
|
// Lancer la vérification |
||||
|
checkDeadlineAlerts(); |
||||
|
|
||||
|
echo "✅ Test de l'envoi d'alertes terminé."; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,145 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="fr"> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
|
<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '"> |
||||
|
<style> |
||||
|
body { font-size: 14px; font-family: Arial, sans-serif; } |
||||
|
.invoice-container { |
||||
|
max-width: 350px; /* Réduire la largeur du cadre */ |
||||
|
margin: 20px auto; |
||||
|
padding: 20px; |
||||
|
border: 2px solid #007bff; /* Bordure plus visible */ |
||||
|
border-radius: 10px; |
||||
|
background: #f0f8ff; /* Couleur de fond plus douce */ |
||||
|
} |
||||
|
.invoice-header { |
||||
|
background: #007bff; |
||||
|
color: white; |
||||
|
text-align: center; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
padding: 10px; |
||||
|
border-radius: 10px 10px 0 0; |
||||
|
} |
||||
|
.invoice-footer { |
||||
|
background: #343a40; |
||||
|
color: white; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
padding: 10px; |
||||
|
border-radius: 0 0 10px 10px; |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
table { width: 100%; border-collapse: collapse; } |
||||
|
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; } |
||||
|
th { background: #e9ecef; } |
||||
|
p, strong { color: #333; } |
||||
|
</style> |
||||
|
</head> |
||||
|
<body onload="window.print();"> |
||||
|
<div class="invoice-container"> |
||||
|
<div class="invoice-header"> |
||||
|
' . esc($company_info['company_name']) . ' |
||||
|
</div> |
||||
|
<p><strong>Facture ID:</strong> ' . esc($order_data['bill_no']) . '</p> |
||||
|
<p><strong>Nom:</strong> ' . esc($order_data['customer_name']) . '</p> |
||||
|
<p><strong>Adresse:</strong> ' . esc($order_data['customer_address']) . '</p> |
||||
|
<p><strong>Téléphone:</strong> ' . esc($order_data['customer_phone']) . '</p> |
||||
|
|
||||
|
<div style="display: flex;align-items: center;justify-content: space-around;margin-bottom: 3%;"> |
||||
|
<div> |
||||
|
<p>Signature du client</p> |
||||
|
</div> |
||||
|
<div> |
||||
|
<p>Signature du commercial</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
<table class="table table-bordered"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Produit</th> |
||||
|
<th>Qté</th> |
||||
|
<th>Prix</th> |
||||
|
<th>Total</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody>'; |
||||
|
|
||||
|
foreach ($orders_items as $item) { |
||||
|
$product_data = $Products->getProductData($item['product_id']); |
||||
|
$html .= '<tr> |
||||
|
<td>' . esc($product_data['name']) . '</td> |
||||
|
<td>' . esc($item['qty']) . '</td> |
||||
|
<td>' . number_format((float)$item['rate'], 2, '.', ' ') . '</td> |
||||
|
<td>' . number_format((float)$item['amount'], 2, '.', ' ') . '</td> |
||||
|
</tr>'; |
||||
|
} |
||||
|
|
||||
|
$html .= ' </tbody> |
||||
|
</table> |
||||
|
<table class="table"> |
||||
|
<tr> |
||||
|
<th>Total:</th> |
||||
|
<td>' . number_format((float)$order_data['gross_amount'], 2, '.', ' ') . '</td> |
||||
|
</tr>'; |
||||
|
|
||||
|
if (!empty($order_data['service_charge']) && (float)$order_data['service_charge'] > 0) { |
||||
|
$html .= '<tr> |
||||
|
<th>Frais de service:</th> |
||||
|
<td>' . number_format((float)$order_data['service_charge'], 2, '.', ' ') . '</td> |
||||
|
</tr>'; |
||||
|
} |
||||
|
|
||||
|
if (!empty($order_data['vat_charge']) && (float)$order_data['vat_charge'] > 0) { |
||||
|
$html .= '<tr> |
||||
|
<th>TVA:</th> |
||||
|
<td>' . number_format((float)$order_data['vat_charge'], 2, '.', ' ') . '</td> |
||||
|
</tr>'; |
||||
|
} |
||||
|
|
||||
|
$html .= '<tr> |
||||
|
<th>Réduction:</th> |
||||
|
<td>' . number_format((float)$order_data['discount'], 2, '.', ' ') . '</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>Total à payer:</th> |
||||
|
<td><strong>' . number_format((float)$order_data['net_amount'], 2, '.', ' ') . '</strong></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th>Statut:</th> |
||||
|
<td>' . $paid_status . '</td> |
||||
|
</tr>'; |
||||
|
|
||||
|
// Vérification et ajout des informations de paiement |
||||
|
if (!empty($order_data['order_payment_mode'])) { |
||||
|
$html .= '<tr> |
||||
|
<th>Mode de paiement:</th> |
||||
|
<td><strong>' . esc($order_data['order_payment_mode']) . '</strong></td> |
||||
|
</tr>'; |
||||
|
} |
||||
|
|
||||
|
if (!empty($order_data['tranche_1'])) { |
||||
|
$html .= '<tr> |
||||
|
<th>Tranche 1:</th> |
||||
|
<td><strong>' . number_format((float)$order_data['tranche_1'], 2, '.', ' ') . '</strong></td> |
||||
|
</tr>'; |
||||
|
} |
||||
|
|
||||
|
if (!empty($order_data['tranche_2'])) { |
||||
|
$html .= '<tr> |
||||
|
<th>Tranche 2:</th> |
||||
|
<td><strong>' . number_format((float)$order_data['tranche_2'], 2, '.', ' ') . '</strong></td> |
||||
|
</tr>'; |
||||
|
} |
||||
|
|
||||
|
$html .= '</table> |
||||
|
|
||||
|
<div class="invoice-footer"> |
||||
|
Merci pour votre achat !<br> |
||||
|
<strong>' . esc($company_info['company_name']) . '</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,176 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use App\Models\Users; |
||||
|
use App\Models\Avance; |
||||
|
use App\Models\AlertMail; |
||||
|
|
||||
|
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(); |
||||
|
$alertMailModel = new AlertMail(); |
||||
|
$usersModel = new Users(); |
||||
|
|
||||
|
$today = date('Y-m-d'); |
||||
|
log_message('info', "Date du jour: {$today}"); |
||||
|
|
||||
|
// Modification pour vérifier les avances dans 0-3 jours |
||||
|
$avances = $avanceModel |
||||
|
->where('DATE(deadline) >=', $today) // Inclut le jour même |
||||
|
->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)); |
||||
|
|
||||
|
$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}"); |
||||
|
|
||||
|
// Modification des types d'alerte pour 0, 1, 2, 3 jours |
||||
|
$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; |
||||
|
} |
||||
|
|
||||
|
// Message modifié pour inclure le cas du jour même |
||||
|
$urgencyText = $daysLeft === 0 ? "ÉCHÉANCE AUJOURD'HUI" : "{$daysLeft} jour(s) restant(s)"; |
||||
|
$message = " |
||||
|
<h3>⚠️ 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> |
||||
|
<p><em>Cette avance " . ($daysLeft === 0 ? "arrive à échéance aujourd'hui" : "arrivera à échéance dans {$daysLeft} jour(s)") . ". Action requise immédiatement.</em></p> |
||||
|
"; |
||||
|
|
||||
|
$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)) { |
||||
|
$emailsSent++; |
||||
|
log_message('info', "Email envoyé avec succès à: {$to}"); |
||||
|
} else { |
||||
|
log_message('error', "Échec envoi email à: {$to}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
log_message('info', "=== FIN checkDeadlineAlerts ==="); |
||||
|
} |
||||
|
|
||||
|
function sendEmailInBackground($to, $subject, $message) |
||||
|
{ |
||||
|
try { |
||||
|
log_message('info', "Préparation envoi email à: {$to}"); |
||||
|
|
||||
|
$email = \Config\Services::email(); |
||||
|
|
||||
|
$config = [ |
||||
|
'protocol' => 'smtp', |
||||
|
'SMTPHost' => 'smtp.gmail.com', |
||||
|
'SMTPUser' => '[email protected]', |
||||
|
'SMTPPass' => 'loirqovmfuxnasrm', |
||||
|
'SMTPPort' => 587, |
||||
|
'SMTPCrypto' => 'tls', |
||||
|
'mailType' => 'html', |
||||
|
'charset' => 'utf-8', |
||||
|
'newline' => "\r\n" |
||||
|
]; |
||||
|
|
||||
|
$email->initialize($config); |
||||
|
|
||||
|
$email->setFrom('[email protected]', 'Système Motorbike - Alertes Avances'); |
||||
|
$email->setTo($to); |
||||
|
$email->setSubject($subject); |
||||
|
$email->setMessage($message); |
||||
|
|
||||
|
log_message('info', "Configuration email terminée, tentative d'envoi..."); |
||||
|
|
||||
|
if (!$email->send()) { |
||||
|
$debugInfo = $email->printDebugger(['headers']); |
||||
|
log_message('error', "Erreur email à {$to}: " . print_r($debugInfo, true)); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
log_message('info', "Email envoyé avec succès à: {$to}"); |
||||
|
return true; |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Exception email à {$to}: " . $e->getMessage()); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Models; |
||||
|
|
||||
|
use CodeIgniter\Model; |
||||
|
|
||||
|
class AlertMail extends Model |
||||
|
{ |
||||
|
protected $table = 'email_alerts'; |
||||
|
protected $primaryKey = 'id'; |
||||
|
|
||||
|
protected $allowedFields = [ |
||||
|
'avance_id', |
||||
|
'alert_type', |
||||
|
'sent_date', |
||||
|
'status', |
||||
|
'created_at', |
||||
|
]; |
||||
|
|
||||
|
// Pas de fonction checkDeadlineAlerts ici ! |
||||
|
} |
||||
@ -1 +0,0 @@ |
|||||
icon |
|
||||
@ -0,0 +1 @@ |
|||||
|
icon |
||||
@ -1 +0,0 @@ |
|||||
icon |
|
||||
@ -0,0 +1 @@ |
|||||
|
icon |
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 0 B |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 848 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 657 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 196 KiB |
@ -0,0 +1 @@ |
|||||
|
1755601005 |
||||