Compare commits

...

No commits in common. '17803c1acb38e9fd8d87e1aa840f3a2a5158b227' and '0ce9cb608d1a236dc7238b2e096547fc7c624372' have entirely different histories.

  1. 2
      .vscode/sftp.json
  2. 98
      app/Config/Email.php
  3. 6
      app/Config/Kint.php
  4. 11
      app/Config/Routes.php
  5. 14
      app/Controllers/AlertsController.php
  6. 827
      app/Controllers/AvanceController.php
  7. 52
      app/Controllers/BaseController.php
  8. 428
      app/Controllers/ProductCOntroller.php
  9. 58
      app/Controllers/ReportController.php
  10. 25
      app/Controllers/TestDeadline.php
  11. 41
      app/Controllers/UserController.php
  12. 145
      app/Controllers/test.html
  13. 176
      app/Helpers/alerts_helper.php
  14. 21
      app/Models/AlertMail.php
  15. 326
      app/Models/Avance.php
  16. 89
      app/Views/attributes/addvalue.php
  17. 831
      app/Views/avances/avance.php
  18. 25
      app/Views/brands/index.php
  19. 25
      app/Views/category/index.php
  20. 94
      app/Views/commercial/addImage.php
  21. 25
      app/Views/commercial/index.php
  22. 16
      app/Views/commercial/single.php
  23. 26
      app/Views/dashboard.php
  24. 26
      app/Views/demande/index.php
  25. 8
      app/Views/groups/create.php
  26. 5
      app/Views/groups/edit.php
  27. 25
      app/Views/groups/index.php
  28. 32
      app/Views/mecanicien/index.php
  29. 27
      app/Views/orders/avance.php
  30. 4
      app/Views/orders/create.php
  31. 2
      app/Views/orders/createbyid.php
  32. 26
      app/Views/orders/index.php
  33. 25
      app/Views/performance/index.php
  34. 3
      app/Views/products/create.php
  35. 62
      app/Views/products/index.php
  36. 25
      app/Views/recouvrement/index.php
  37. 25
      app/Views/recouvrement/recouvrement.php
  38. 1
      app/Views/reports/performance.php
  39. 2
      app/Views/reports/stockDetail.php
  40. 1
      app/Views/reports/venteStore.php
  41. 24
      app/Views/securite/index.php
  42. 52
      app/Views/sortieCaisse/index.php
  43. 25
      app/Views/stores/index.php
  44. 49
      app/Views/templates/side_menubar.php
  45. 28
      app/Views/users/index.php
  46. 1
      awstats-icon
  47. 1
      awstats-icon
  48. 1
      awstatsicons
  49. 1
      awstatsicons
  50. 9
      composer.json
  51. 1
      icon/mime/conf.png
  52. BIN
      icon/mime/conf.png
  53. 1
      icon/mime/csv.png
  54. BIN
      icon/mime/csv.png
  55. 1
      icon/mime/document.png
  56. BIN
      icon/mime/document.png
  57. 1
      icon/mime/dtd.png
  58. BIN
      icon/mime/dtd.png
  59. 1
      icon/mime/flv.png
  60. BIN
      icon/mime/flv.png
  61. 1
      icon/mime/fon.png
  62. BIN
      icon/mime/fon.png
  63. 1
      icon/mime/package.png
  64. BIN
      icon/mime/package.png
  65. 1
      icon/mime/runtime.png
  66. BIN
      icon/mime/runtime.png
  67. 1
      icon/mime/swf.png
  68. BIN
      icon/mime/swf.png
  69. 1
      icon/mime/vbs.png
  70. BIN
      icon/mime/vbs.png
  71. 1
      icon/mime/xsl.png
  72. BIN
      icon/mime/xsl.png
  73. BIN
      public/assets/bower_components/jquery-ui/themes/humanity/images/animated-overlay.gif
  74. BIN
      public/assets/bower_components/jquery-ui/themes/humanity/images/ui-bg_glass_100_f5f0e5_1x400.png
  75. BIN
      public/assets/images/product_image/686cdaac4dad4.jpg
  76. BIN
      public/assets/images/product_image/686cdaac5f43e.jpg
  77. BIN
      public/assets/images/product_image/686cdaac72635.jpg
  78. BIN
      public/assets/images/product_image/6894a44cd6b5f.jpg
  79. BIN
      public/assets/images/product_image/689ca160619a9.jpg
  80. BIN
      public/assets/images/product_image/689eba65cf5a8.jpg
  81. BIN
      public/assets/images/product_image/689ebcb98f420.jpg
  82. BIN
      public/assets/images/product_image/689ebceb0d802.jpg
  83. BIN
      public/assets/images/product_image/689ebd3b59c5b.jpg
  84. 1
      writable/cache/check_deadline_last_run.txt
  85. 1
      writable/debugbar/debugbar_1751886377.352834.json
  86. 1
      writable/debugbar/debugbar_1751886387.359498.json
  87. 1
      writable/debugbar/debugbar_1751886393.226242.json
  88. 1
      writable/debugbar/debugbar_1751886396.580956.json
  89. 1
      writable/debugbar/debugbar_1751886397.779369.json
  90. 1
      writable/debugbar/debugbar_1751886406.431574.json
  91. 1
      writable/debugbar/debugbar_1751886416.347097.json
  92. 1
      writable/debugbar/debugbar_1751886426.829140.json
  93. 1
      writable/debugbar/debugbar_1751886438.478916.json
  94. 1
      writable/debugbar/debugbar_1751886439.130326.json
  95. 1
      writable/debugbar/debugbar_1751886443.397712.json
  96. 1
      writable/debugbar/debugbar_1751886445.279811.json
  97. 1
      writable/debugbar/debugbar_1751886453.171091.json
  98. 1
      writable/debugbar/debugbar_1751886454.621281.json
  99. 1
      writable/debugbar/debugbar_1751886456.946542.json
  100. 1
      writable/debugbar/debugbar_1751886458.223834.json

2
.vscode/sftp.json

@ -6,7 +6,7 @@
"username": "motorbike", "username": "motorbike",
"remotePath": "/home/motorbike/public_html/", "remotePath": "/home/motorbike/public_html/",
"password": "IVrMDogT3XiBcrY", "password": "IVrMDogT3XiBcrY",
"uploadOnSave": true, "uploadOnSave": false,
"useTempFile": false, "useTempFile": false,
"openSsh": false "openSsh": false
} }

98
app/Config/Email.php

@ -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 = 'rey342505@gmail.com';
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 = 'rey342505@gmail.com';
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;
} }

6
app/Config/Kint.php

@ -4,7 +4,7 @@ namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use Kint\Parser\ConstructablePluginInterface; use Kint\Parser\ConstructablePluginInterface;
use Kint\Renderer\AbstractRenderer; //use Kint\Renderer\AbstractRenderer;
use Kint\Renderer\Rich\TabPluginInterface; use Kint\Renderer\Rich\TabPluginInterface;
use Kint\Renderer\Rich\ValuePluginInterface; use Kint\Renderer\Rich\ValuePluginInterface;
@ -41,8 +41,10 @@ class Kint extends BaseConfig
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
public string $richTheme = 'aante-light.css'; public string $richTheme = 'aante-light.css';
public $richSort = null;
public bool $richFolder = false; public bool $richFolder = false;
public int $richSort = AbstractRenderer::SORT_FULL; //public int $richSort = AbstractRenderer::SORT_FULL;
/** /**
* @var array<string, class-string<ValuePluginInterface>>|null * @var array<string, class-string<ValuePluginInterface>>|null

11
app/Config/Routes.php

@ -30,6 +30,14 @@ use App\Controllers\PerformanceController;
*/ */
$routes->get('/login', [Auth::class, 'login'], ['filter' => 'loggedIn']); $routes->get('/login', [Auth::class, 'login'], ['filter' => 'loggedIn']);
$routes->post('/login', [Auth::class, 'loginPost'], ['filter' => 'loggedIn']); $routes->post('/login', [Auth::class, 'loginPost'], ['filter' => 'loggedIn']);
$routes->get('test-email', 'TestEmail::index');
$routes->get('alerts/check', 'AlertsController::check');
$routes->get('check-deadline', 'TestDeadline::index');
/** /**
* route to all the rest of web app * route to all the rest of web app
@ -53,6 +61,8 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
* route to logout * route to logout
*/ */
$routes->get('/logout', [Auth::class, 'logout']); $routes->get('/logout', [Auth::class, 'logout']);
// Route pour tester les alertes manuellement (à supprimer en production)
$routes->get('test-deadline-alerts', 'AvanceController::checkDeadlineAlerts');
/** /**
* route for the users * route for the users
@ -242,6 +252,7 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
$routes->post('delete', [MecanicienController::class, 'delete']); $routes->post('delete', [MecanicienController::class, 'delete']);
$routes->post('update/(:num)', [MecanicienController::class, 'update']); $routes->post('update/(:num)', [MecanicienController::class, 'update']);
$routes->get('fetchMecanicienPerformances', [MecanicienController::class, 'fetchMecanicienPerformances']); $routes->get('fetchMecanicienPerformances', [MecanicienController::class, 'fetchMecanicienPerformances']);
// $routes->put('update/(:num)', 'MecanicienController::update/$1'); // $routes->put('update/(:num)', 'MecanicienController::update/$1');
}); });

14
app/Controllers/AlertsController.php

@ -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.";
}
}

827
app/Controllers/AvanceController.php

@ -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('noreply@yourcompany.com', '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 = [
'daf@yourcompany.com',
'direction@yourcompany.com'
];
}
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'
]);
}
} }
}
}

52
app/Controllers/BaseController.php

@ -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();
}
} }
} }

428
app/Controllers/ProductCOntroller.php

@ -74,81 +74,79 @@ class ProductCOntroller extends AdminController
public function fetchProductData() public function fetchProductData()
{ {
// Initialize the response array
$result = ['data' => []]; $result = ['data' => []];
$Products = new Products(); $Products = new Products();
$Stores = new Stores(); $Stores = new Stores();
function convertString($name) function convertString($name)
{ {
return "$name"; return "$name";
} }
// Fetch product data from the model
$data = $Products->getProductData(); // Ensure this method exists in your ProductModel $data = $Products->getProductData();
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
// Fetch store data // Gestion du nom du magasin
$store_data = $Stores->getStoresData($value['store_id']); // Ensure this method exists in your StoreModel if ($value['store_id'] == 0) {
$store_data['name'] = $value['store_id'] == 0 ? "TOUS" : $Stores->getStoresData($value['store_id'])["name"]; $store_name = "TOUS";
// Construct buttons } else {
$store_info = $Stores->getStoresData($value['store_id']);
$store_name = $store_info && isset($store_info['name']) ? $store_info['name'] : "Inconnu";
}
// Disponibilité
$availability = ($value['qty'] > 0) ? '<span class="label label-success">En stock</span>' : '<span class="label label-danger">Rupture</span>';
// Construction des boutons
$buttons = ''; $buttons = '';
if (in_array('updateProduct', $this->permission ?? [])) { if (in_array('updateProduct', $this->permission ?? [])) {
$buttons .= '<a href="' . base_url('products/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>'; $buttons .= '<a href="' . base_url('products/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>';
} }
if (in_array('deleteProduct', $this->permission ?? [])) { if (in_array('deleteProduct', $this->permission ?? [])) {
$buttons .= ' <button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['id'] . ')" data-toggle="modal" data-target="#removeModal"><i class="fa fa-trash"></i></button>'; $buttons .= ' <button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['id'] . ')" data-toggle="modal" data-target="#removeModal"><i class="fa fa-trash"></i></button>';
} }
if (in_array('updateProduct', $this->permission ?? [])) { if (in_array('updateProduct', $this->permission ?? [])) {
$buttons .= ' <a href="ventes/' . $value['id'] . '" class="btn btn-default"><i class="fa fa-image"></i></a>'; $buttons .= ' <a href="ventes/' . $value['id'] . '" class="btn btn-default"><i class="fa fa-image"></i></a>';
} }
if (in_array('updateProduct', $this->permission ?? [])) { if (in_array('updateProduct', $this->permission ?? [])) {
$buttons .= ' <button class="btn btn-default" onclick="generateQrPdf(' . $value["id"] . ')"><i class="fa fa-qrcode"></i></button>'; $buttons .= ' <button class="btn btn-default" onclick="generateQrPdf(' . $value["id"] . ')"><i class="fa fa-qrcode"></i></button>';
} }
if (in_array('viewProduct', $this->permission ?? [])) { if (in_array('viewProduct', $this->permission ?? [])) {
$buttons .= " <a href='/ventes/show/" . $value['id'] . "' class='btn btn-default'><i class='fa fa-eye'></i></a>"; $buttons .= " <a href='/ventes/show/" . $value['id'] . "' class='btn btn-default'><i class='fa fa-eye'></i></a>";
} }
if (in_array('assignStore', $this->permission ?? [])) { if (in_array('assignStore', $this->permission ?? [])) {
$buttons .= $buttons .=
'<button type="button" class="btn btn-info assignbtn" title="Assigner sur un magasin" data-magasin="' . $store_data['name'] . '" data-products-id="' . $value["id"] . '" data-toggle="modal" data-target="#assignStoreModal"> '<button type="button" class="btn btn-info assignbtn" title="Assigner sur un magasin" data-magasin="' . $store_name . '" data-products-id="' . $value["id"] . '" data-toggle="modal" data-target="#assignStoreModal">
<i class="fa fa-forward"></i> <i class="fa fa-forward"></i>
</button>'; </button>';
} }
$imagePath = 'assets/images/product_image/' . $value['image'];
$imageHtml = $value['image'] ?
// Image HTML '<img src="' . base_url($imagePath) . '" width="50" height="50" class="img-thumbnail">' :
$img = '<img src="' . base_url('assets/images/product_image/' . $value['image']) . '" alt="' . $value['name'] . '" class="img-circle" width="50" height="50" />'; '<div class="no-image">Aucune image</div>';
// Availability Status // Préparer les données pour DataTables (7 colonnes)
$availability = ($value['availability'] == 1) ? '<span class="label label-success">Disponible</span>' : '<span class="label label-warning">Indisponible</span>'; $result['data'][$key] = [
$value['image'],
// Quantity Status convertString($value['sku']),
$qty_status = '';
if ($value['qty'] <= 10 && $value['qty'] > 0) {
$qty_status = '<span class="label label-warning">Low!</span>';
} elseif ($value['product_sold'] == false) {
$qty_status = '<span class="label label-danger">Rupture de stock!</span>';
}
// Populate the result data
$result['data'][] = [
$img,
$value['sku'],
$value['name'], $value['name'],
number_format($value['prix_vente'], 0, ',', ' '), $value['price'],
$store_data['name'] ?? 'Unknown Store', $store_name,
$availability, $availability, // <-- ici la disponibilité ajoutée
$buttons $buttons
]; ];
} }
// Return JSON response
return $this->response->setJSON($result); return $this->response->setJSON($result);
} }
public function create() public function create()
{ {
@ -356,233 +354,147 @@ class ProductCOntroller extends AdminController
return $this->response->setJSON($response); return $this->response->setJSON($response);
} }
public function createByExcel() public function createByExcel()
{ {
$this->verifyRole("createProduct"); $this->verifyRole("createProduct");
// 1) Récupération et validation du fichier try {
$file = $this->request->getFile('excel_product'); $file = $this->request->getFile('excel_product');
if (!$file || !$file->isValid() || $file->hasMoved()) { if (!$file || !$file->isValid()) {
return $this->response->setJSON([ return $this->response->setJSON([
'success' => false, 'success' => false,
'messages' => "Aucun fichier valide reçu" 'messages' => "Fichier invalide ou non reçu"
]); ]);
}
$ext = strtolower($file->getClientExtension());
if (! in_array($ext, ['xls', 'xlsx'])) {
return $this->response->setJSON([
'success' => false,
'messages' => "Seuls les fichiers xls/xlsx sont autorisés"
]);
}
try {
// 2) Chargement du fichier Excel
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file->getTempName());
$sheet = $spreadsheet->getActiveSheet();
// 3) Lecture des données brutes et mapping des en-têtes
$allRows = $sheet->toArray(null, true, true, true);
if (count($allRows) < 2) {
return $this->response->setJSON([
'success' => false,
'messages' => "Le fichier ne contient aucune donnée"
]);
}
$headerRow = array_shift($allRows);
$map = [];
foreach ($headerRow as $col => $heading) {
$h = mb_strtolower(trim($heading));
$h = str_replace(['’', '‘', '“', '”'], '\'', $h);
$h = iconv('UTF-8', 'ASCII//TRANSLIT', $h);
$h = preg_replace('/[^a-z0-9_]/', '_', $h);
$h = preg_replace('/_+/', '_', $h);
$h = trim($h, '_');
switch ($h) {
case 'designation':
case 'nom':
$map[$col] = 'name'; break;
case 'n_serie':
$map[$col] = 'sku'; break;
case 'prix_ar':
$map[$col] = 'prix_vente'; break;
case 'prix_d_achat':
case 'prix_dachat':
case 'prixd_achat':
$map[$col] = 'price'; break;
case 'marque':
$map[$col] = 'marque'; break;
case 'description':
$map[$col] = 'description'; break;
case 'code_moteur':
case 'n_moteur':
$map[$col] = 'numero_de_moteur'; break;
case 'chassis':
case 'chasis':
$map[$col] = 'chasis'; break;
case 'date_arrivage':
case 'date_d_arivage':
$map[$col] = 'date_arrivage'; break;
case 'puissance':
$map[$col] = 'puissance'; break;
case 'availability':
case 'disponibilite':
$map[$col] = 'availability'; break;
case 'piece':
case 'piece_manquant':
$map[$col] = 'is_piece'; break;
case 'cle':
$map[$col] = 'cler'; break;
case 'categories':
case 'categorie_id':
$map[$col] = 'categorie_id'; break;
case 'etat':
case 'etats':
$map[$col] = 'etats'; break;
case 'magasin':
$map[$col] = 'store_id'; break;
case 'info_manquekit':
case 'infomanquekit':
$map[$col] = 'infoManquekit'; break;
case 'info':
case 'info_piece':
$map[$col] = 'info'; break;
case 'info_manque':
$map[$col] = 'infoManque'; break;
case 'image':
case 'image_s':
$map[$col] = 'image'; break;
default:
// Non mappé
break;
}
}
// 4) Extraction des images intégrées, si présent
$imagesMap = [];
foreach ($sheet->getDrawingCollection() as $drawing) {
if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
$coord = $drawing->getCoordinates();
$extImg = pathinfo($drawing->getPath(), PATHINFO_EXTENSION);
$name = uniqid('img_') . ".$extImg";
$dir = FCPATH . 'assets/images/product_image/';
if (! is_dir($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($dir . $name, file_get_contents($drawing->getPath()));
$imagesMap[$coord] = $name;
}
}
// 5) Chargement des modèles
$ProductsModel = new \App\Models\Products();
$ProductsModel->skipValidation(true);
$BrandsModel = new \App\Models\Brands();
$CatModel = new \App\Models\Category();
$countInserted = 0;
// 6) Boucle sur chaque ligne de données
foreach ($allRows as $rowIndex => $row) {
$data = [];
// Lecture des cellules formatées pour chaque champ mappé
foreach ($map as $col => $field) {
$cellValue = $sheet
->getCell($col . ($rowIndex + 2))
->getFormattedValue();
$data[$field] = trim((string)$cellValue);
} }
if (empty($data['name'])) { $ext = strtolower($file->getClientExtension());
continue; // champ désignation vide if (!in_array($ext, ['xls', 'xlsx'])) {
return $this->response->setJSON([
'success' => false,
'messages' => "Seuls les fichiers Excel (.xls, .xlsx) sont acceptés"
]);
} }
// Conversion du prix AR : capture tous les groupes de chiffres $spreadsheet = IOFactory::load($file->getTempName());
if (! empty($data['prix_vente'])) { $sheet = $spreadsheet->getActiveSheet();
preg_match_all('/\d+/', $data['prix_vente'], $matches); $rows = $sheet->toArray();
$digits = implode('', $matches[0]); // ex. ["2","000","000"] => "2000000"
$data['prix_vente'] = intval($digits); if (count($rows) <= 1) {
} else { return $this->response->setJSON([
$data['prix_vente'] = 0; 'success' => false,
'messages' => "Le fichier ne contient pas de données"
]);
} }
// Valeurs par défaut // Récupérer les en-têtes
$data['qty'] = 1; $headers = array_shift($rows);
$data['product_sold'] = 0; $headers = array_map('strtolower', $headers);
$data['availability'] = isset($data['availability'])
? (strtolower($data['availability']) === 'oui' ? 1 : 0) // Mapping des colonnes Excel vers les champs de la base
: 0; $columnMapping = [
$data['is_piece'] = isset($data['is_piece']) 'n° série' => 'sku',
? (strtolower($data['is_piece']) === 'oui' ? 1 : 0) 'marque' => 'marque',
: 0; 'désignation' => 'name',
$data['cler'] = isset($data['cler']) 'fournisseur' => 'info', // À adapter selon votre besoin
? (strtolower($data['cler']) === 'oui' ? 1 : 0) 'date d\'arrivage' => 'date_arivage',
: 1; 'n° moteur' => 'numero_de_moteur',
$data['etats'] = isset($data['etats']) 'châssis' => 'chasis',
? (strtolower($data['etats']) === 'kit' ? 1 : 0) 'puissance' => 'puissance',
: 1; 'clé' => 'cler',
'prix d\'achat' => 'prix_vente',
// Association d’image si présente 'prix ar' => 'price',
foreach ($map as $col => $field) { 'catégories' => 'categorie_id',
if ($field === 'image') { 'magasin' => 'store_id',
$coordImg = $col . ($rowIndex + 2); 'disponibilité' => 'availability',
if (isset($imagesMap[$coordImg])) { 'état' => 'etats',
$data['image'] = $imagesMap[$coordImg]; 'pièce manquant' => 'infoManque'
];
$ProductsModel = new Products();
$BrandsModel = new Brands();
$StoresModel = new Stores();
$CategoryModel = new Category();
$countInserted = 0;
foreach ($rows as $row) {
if (empty(array_filter($row))) continue; // Ignore les lignes vides
$data = [
'is_piece' => 0,
'product_sold' => 0,
'qty' => 1
];
// Mapper chaque colonne
foreach ($headers as $index => $header) {
$header = trim($header);
if (isset($columnMapping[$header]) && isset($row[$index])) {
$field = $columnMapping[$header];
$value = trim($row[$index]);
// Traitements spécifiques pour certains champs
switch ($field) {
case 'marque':
// Chercher ou créer la marque
$brand = $BrandsModel->where('name', $value)->first();
if (!$brand) {
$brandId = $BrandsModel->insert(['name' => $value, 'active' => 1]);
} else {
$brandId = $brand['id'];
}
$data[$field] = $brandId;
break;
case 'store_id':
// Gestion du magasin
if ($value == 'TOUS') {
$data[$field] = 0;
} else {
$store = $StoresModel->where('name', $value)->first();
$data[$field] = $store ? $store['id'] : 0;
}
break;
case 'date_arivage':
// Convertir la date Excel en format MySQL
if (is_numeric($value)) {
$data[$field] = date('Y-m-d', \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($value));
} else {
$data[$field] = date('Y-m-d', strtotime($value));
}
break;
case 'price':
// Nettoyer "1 900 000 Ar" → 1900000.00
$cleanedValue = str_replace(['Ar', ' ', ','], '', $value);
$data[$field] = (float)$cleanedValue;
break;
default:
$data[$field] = $value;
}
} }
break;
} }
}
// Insertion
// Gestion des clés étrangères if (!empty($data['name'])) {
if (! empty($data['marque'])) { if ($ProductsModel->insert($data)) {
$data['marque'] = $BrandsModel->getOrCreateIdByName($data['marque']); $countInserted++;
}
if (! empty($data['categorie_id'])) {
$labels = array_map('trim', explode(',', $data['categorie_id']));
$catIds = [];
foreach ($labels as $label) {
if ($label !== '') {
$catIds[] = $CatModel->getOrCreateIdByName($label);
} }
} }
$data['categorie_id'] = $catIds;
}
if (! empty($data['store_id'])) {
// store_id depuis la session
$Store = new Stores();
$store = $Store->getIdStoreByName($data['store_id']);
$data['store_id'] = $store;
}
// Insertion
$id = $ProductsModel->insert($data);
if ($id !== false) {
$countInserted++;
} }
return $this->response->setJSON([
'success' => true,
'messages' => "$countInserted produits importés avec succès"
]);
} catch (\Exception $e) {
return $this->response->setJSON([
'success' => false,
'messages' => "Erreur lors de l'import: " . $e->getMessage()
]);
} }
// 7) Notification et réponse
$Notification = new \App\Controllers\NotificationController();
$user = session()->get('user');
$Notification->createNotification(
"$countInserted produits ajoutés",
"COMMERCIALE",
(int)$user['store_id'],
"avances"
);
return $this->response->setJSON([
'success' => true,
'messages' => "Produits importés avec succès ($countInserted)"
]);
} catch (\Exception $e) {
log_message('error', $e->getMessage());
return $this->response->setJSON([
'success' => false,
'messages' => "Erreur pendant l’import : " . $e->getMessage()
]);
} }
}
} }

58
app/Controllers/ReportController.php

@ -22,39 +22,35 @@ class ReportController extends AdminController
{ {
$this->verifyRole('viewReports'); $this->verifyRole('viewReports');
$data['page_title'] = $this->pageTitle; $data['page_title'] = $this->pageTitle;
// Get the current year or the selected year from the form // Get the current year or the selected year from the form
$today_year = date('Y'); $today_year = date('Y');
if ($this->request->getPost('select_year')) { if ($this->request->getPost('select_year')) {
$today_year = $this->request->getPost('select_year'); $today_year = $this->request->getPost('select_year');
} }
// // Fetch order data and years // Fetch order data and years
$Reports = new Reports(); $Reports = new Reports();
$Orders = new Orders(); $Orders = new Orders();
$Store = new Stores(); $Store = new Stores();
$parking_data = $Reports->getOrderData($today_year); $parking_data = $Reports->getOrderData($today_year);
$data['report_years'] = $Reports->getOrderYear(); $data['report_years'] = $Reports->getOrderYear();
// // Process the parking data and calculate total amounts // Process the parking data and calculate total amounts
$final_parking_data = []; $final_parking_data = [];
foreach ($parking_data as $month => $orders) { foreach ($parking_data as $month => $orders) {
$total_amount_earned = 0; // Initialize the total amount for the month $total_amount_earned = 0; // Initialize the total amount for the month
// If there are orders for this month, sum the gross_amount
if (!empty($orders)) { if (!empty($orders)) {
foreach ($orders as $order) { foreach ($orders as $order) {
// Cast gross_amount to float and add to the total
$total_amount_earned += (float) $order['net_amount']; $total_amount_earned += (float) $order['net_amount'];
} }
} }
// Assign the total amount for the month to the final array
$final_parking_data[$month] = $total_amount_earned; $final_parking_data[$month] = $total_amount_earned;
} }
// //data for the camembert // Data for the camembert (pie chart)
$paymentModes = $Orders->getPaymentModes(); $paymentModes = $Orders->getPaymentModes();
$total_mvola1 = $paymentModes->total_mvola1; $total_mvola1 = $paymentModes->total_mvola1;
$total_mvola2 = $paymentModes->total_mvola2; $total_mvola2 = $paymentModes->total_mvola2;
@ -68,57 +64,59 @@ class ReportController extends AdminController
$totalOrders = $Orders->getTotalOrders(); $totalOrders = $Orders->getTotalOrders();
$totalAmountPerPaymentModes = ["MVOLA" => $total_mvola, "Espece" => $total_espece, "Virement Bancaire" => $total_banque]; $totalAmountPerPaymentModes = ["MVOLA" => $total_mvola, "Espece" => $total_espece, "Virement Bancaire" => $total_banque];
$totalOrdersCount = (int) $totalOrders->total_orders; $totalOrdersCount = (int) $totalOrders->total_orders;
// // dd($paymentModes);
$labels = []; $labels = [];
$totals = []; $totals = [];
if ($totalOrdersCount > 0) { if ($totalOrdersCount > 0) {
foreach ($totalAmountPerPaymentModes as $mode => $total) { foreach ($totalAmountPerPaymentModes as $mode => $total) {
$labels[] = $mode; $labels[] = $mode;
$totals[] = $total; $totals[] = $total;
} }
} }
$data['labels'] = json_encode($labels); $data['labels'] = json_encode($labels);
$data['totals'] = json_encode($totals); $data['totals'] = json_encode($totals);
// // prepare data for product chart // Prepare data for product chart
$OrderItem = new OrderItems(); $OrderItem = new OrderItems();
$productTable = $OrderItem->getAllSoldProductToday(); $productTable = $OrderItem->getAllSoldProductToday();
$product_sold = (int) $productTable->total_product_sold; $product_sold = (int) $productTable->total_product_sold;
$unsold_product = (int) $productTable->total_unsold_product; $unsold_product = (int) $productTable->total_unsold_product;
// Définir les labels et les valeurs pour le Pie Chart
$labels1 = ["Produits vendus", "Produits non vendus"]; $labels1 = ["Produits vendus", "Produits non vendus"];
$totals2 = [$product_sold, $unsold_product]; $totals2 = [$product_sold, $unsold_product];
// Encoder les données en JSON pour le Pie Chart
$data['labels_product'] = json_encode($labels1); $data['labels_product'] = json_encode($labels1);
$data['totals_product'] = json_encode($totals2); $data['totals_product'] = json_encode($totals2);
// // Prepare data for the view // Prepare data for the view
$data['selected_year'] = $today_year; $data['selected_year'] = $today_year;
$data['company_currency'] = $this->companycurrency(); $data['company_currency'] = $this->companycurrency();
$data['results'] = $final_parking_data; $data['results'] = $final_parking_data;
// //data for the camember in dashboard // Data for the camembert in dashboard
$totalStoreOrder = $Orders->getTotalOrderPerStore(); $totalStoreOrder = $Orders->getTotalOrderPerStore();
$totalOrders = $Orders->getTotalOrders(); $totalOrders = $Orders->getTotalOrders();
$totalOrdersCount = (int) $totalOrders->total_orders; $totalOrdersCount = (int) $totalOrders->total_orders;
// Initialisation des variables pour éviter l'erreur "Undefined variable"
$labelStore = [];
$totalPerStore = [];
foreach ($totalStoreOrder as $totalOrdersInStore) { foreach ($totalStoreOrder as $totalOrdersInStore) {
$storeList = $Store->getStoreById($totalOrdersInStore->store_id); $storeList = $Store->getStoreById($totalOrdersInStore->store_id);
$labelStore[] = $storeList->name ?? 'Inconnu'; $labelStore[] = $storeList->name ?? 'Inconnu';
$totalPerStore[] = ((int) $totalOrdersInStore->total / $totalOrdersCount) * 100; $totalPerStore[] = ((int) $totalOrdersInStore->total / $totalOrdersCount) * 100;
} }
$data['labelStore'] = json_encode($labelStore); $data['labelStore'] = json_encode($labelStore);
$data['totalPerStore'] = json_encode($totalPerStore); $data['totalPerStore'] = json_encode($totalPerStore);
// Load the view // Load the view
// return view('reports/index', $this->data);
return $this->render_template('reports/index', $data); return $this->render_template('reports/index', $data);
} }
private function companycurrency() private function companycurrency()
{ {
return 'AR'; // Replace with your actual logic for company currency return 'AR'; // Replace with your actual logic for company currency

25
app/Controllers/TestDeadline.php

@ -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é.";
}
}

41
app/Controllers/UserController.php

@ -350,34 +350,25 @@ class UserController extends AdminController
return redirect()->to('/users'); return redirect()->to('/users');
} }
public function delete($id)
{
$this->verifyRole('deleteUser');
if ($id) { // supression utilisateur
// Check if the form has been submitted with confirmation
if ($this->request->getPost('confirm')) {
$usersModel = new Users(); // Ensure Users model is loaded
$delete = $usersModel->delete($id);
$data['page_title'] = $this->pageTitle;
if ($delete) { public function delete($id)
session()->setFlashdata('success', 'Supprimé avec succès'); {
return redirect()->to('/users'); $this->verifyRole('deleteUser');
} else {
session()->setFlashdata('error', 'Une erreur est survenue !!'); if (!$id) {
return redirect()->to("/users/delete/{$id}"); return $this->response->setJSON(['success' => false, 'message' => 'ID manquant']);
} }
} else {
// If no confirmation yet, load the delete confirmation view $usersModel = new Users();
$data = [ $delete = $usersModel->delete($id);
'id' => $id,
'page_title' => $this->pageTitle if ($delete) {
]; return $this->response->setJSON(['success' => true, 'message' => 'Supprimé avec succès']);
// die(var_dump($data)); } else {
return $this->render_template('users/delete', $data); // Use CodeIgniter 4's view function return $this->response->setJSON(['success' => false, 'message' => 'Échec de la suppression']);
}
} }
} }

145
app/Controllers/test.html

@ -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>

176
app/Helpers/alerts_helper.php

@ -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' => 'rey342505@gmail.com',
'SMTPPass' => 'loirqovmfuxnasrm',
'SMTPPort' => 587,
'SMTPCrypto' => 'tls',
'mailType' => 'html',
'charset' => 'utf-8',
'newline' => "\r\n"
];
$email->initialize($config);
$email->setFrom('rey342505@gmail.com', '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;
}
}

21
app/Models/AlertMail.php

@ -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 !
}

326
app/Models/Avance.php

@ -2,104 +2,120 @@
namespace App\Models; namespace App\Models;
use CodeIgniter\Model; use CodeIgniter\Model;
class Avance extends Model{
/** class Avance extends Model {
* table name
* @var string
*/
protected $table = 'avances'; protected $table = 'avances';
protected $primaryKey = 'avance_id'; protected $primaryKey = 'avance_id';
protected $allowedFields = [ protected $allowedFields = [
'avance_amount', 'avance_date','user_id', 'avance_amount', 'avance_date','user_id',
'customer_name', 'customer_name', 'customer_address', 'customer_phone', 'customer_cin',
'customer_address', 'gross_amount','amount_due','product_id','is_order','active','store_id',
'customer_phone', 'type_avance', 'deadline' // Ajout du champ type et deadline
'customer_cin','gross_amount','amount_due','product_id','is_order','active','store_id']; ];
public function createAvance( array $data) { public function createAvance(array $data) {
try { try {
return $this->insert($data); // Si la date de création n'est pas définie, on prend aujourd'hui
} catch (\Exception $e) { if (empty($data['avance_date'])) {
log_message('error', 'Erreur lors de l\'ajout de l\'avance : ' . $e->getMessage()); $data['avance_date'] = date('Y-m-d');
return false; }
}
}
public function updateAvance(int $id, array $data) { // Calcul de la deadline en fonction du type
if (!empty($data['type'])) {
if ($id <= 0) { if (strtolower($data['type']) === 'avance sur terre') {
log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id); $data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +15 days'));
return false; } elseif (strtolower($data['type']) === 'avance sur mer') {
$data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +2 months'));
}
} }
try { return $this->insert($data);
} catch (\Exception $e) {
return $this->update($id, $data); log_message('error', 'Erreur lors de l\'ajout de l\'avance : ' . $e->getMessage());
} catch (\Exception $e) { return false;
log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage()); }
return false; }
public function updateAvance(int $id, array $data) {
if ($id <= 0) {
log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id);
return false;
}
try {
// Recalcul de la deadline si le type change
if (!empty($data['type']) && !empty($data['avance_date'])) {
if (strtolower($data['type']) === 'avance sur terre') {
$data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +15 days'));
} elseif (strtolower($data['type']) === 'avance sur mer') {
$data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +2 months'));
}
} }
return $this->update($id, $data);
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage());
return false;
} }
}
public function getAllAvanceData(int $id=null) {
$session = session(); // 📌 Le reste de tes fonctions restent inchangées
$users = $session->get('user'); public function getAllAvanceData(int $id=null) {
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); $session = session();
if($isAdmin) { $users = $session->get('user');
if($id){ $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
try { if($isAdmin) {
return $this->where('user_id',$id) if($id){
try {
return $this->where('user_id',$id)
->where('is_order',0) ->where('is_order',0)
->where('active',1) ->where('active',1)
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC')
->findAll(); ->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
try {
return $this
->where('is_order',0)
->where('active',1)
->orderBy('avance_date', 'DESC') ->findAll();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage()); log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false; return false;
} }
} }
else{ try {
if($id){ return $this
try { ->where('is_order',0)
return $this->where('user_id',$id) ->where('active',1)
->orderBy('avance_date', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
} else {
if($id){
try {
return $this->where('user_id',$id)
->where('is_order',0) ->where('is_order',0)
->where('active',1) ->where('active',1)
->where('store_id',$users['store_id']) ->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC')
->findAll(); ->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
try {
return $this
->where('is_order',0)
->where('active',1)
->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC') ->findAll();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage()); log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false; return false;
} }
} }
try {
return $this
->where('is_order',0)
->where('active',1)
->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
} }
}
public function fetchSingleAvance(int $avance_id){ public function fetchSingleAvance(int $avance_id){
return $this->where('avance_id',$avance_id) return $this->where('avance_id',$avance_id)->first();
->first();
} }
public function removeAvance(int $avance_id){ public function removeAvance(int $avance_id){
@ -112,159 +128,165 @@ class Avance extends Model{
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
if($isAdmin) { if($isAdmin) {
try { try {
return $this->select(' return $this->select('SUM(avance_amount) AS ta')
SUM(avance_amount) AS ta, ->where('is_order', 0)
->get()
') ->getRowObject();
->where('is_order', 0)
->get()
->getRowObject();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage()); log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage());
return false; return false;
} }
} } else {
else{
try { try {
return $this->select(' return $this->select('SUM(avance_amount) AS ta')
SUM(avance_amount) AS ta, ->where('is_order', 0)
->where('store_id',$users['store_id'])
') ->get()
->where('is_order', 0) ->getRowObject();
->where('store_id',$users['store_id'])
->get()
->getRowObject();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage()); log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage());
return false; return false;
} }
} }
} }
public function getAllAvanceData1(int $id=null) { public function getAllAvanceData1(int $id=null) {
$session = session(); $session = session();
$users = $session->get('user'); $users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
if($isAdmin) { if($isAdmin) {
if($id){ if($id){
try { try {
return $this->where('user_id',$id) return $this->where('user_id',$id)
->where('is_order',1) ->where('is_order',1)
->where('active',1) ->where('active',1)
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC')
->findAll(); ->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
try {
return $this
->where('is_order',1)
->where('active',1)
->orderBy('avance_date', 'DESC') ->findAll();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage()); log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false; return false;
} }
} }
else{ try {
if($id){ return $this
try { ->where('is_order',1)
return $this->where('user_id',$id) ->where('active',1)
->orderBy('avance_date', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
} else {
if($id){
try {
return $this->where('user_id',$id)
->where('is_order',1) ->where('is_order',1)
->where('active',1) ->where('active',1)
->where('store_id',$users['store_id']) ->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC')
->findAll(); ->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
try {
return $this
->where('is_order',0)
->orderBy('avance_date', 'DESC') ->findAll();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage()); log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false; return false;
} }
} }
try {
return $this
->where('is_order',0)
->orderBy('avance_date', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
} }
public function getAllAvanceData2(int $id=null) { public function getAllAvanceData2(int $id=null) {
$session = session(); $session = session();
$users = $session->get('user'); $users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
if($isAdmin) { if($isAdmin) {
if($id){ if($id){
try { try {
return $this->where('user_id',$id) return $this->where('user_id',$id)
->where('is_order',0) ->where('is_order',0)
->where('active',0) ->where('active',0)
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC')
->findAll(); ->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
try {
return $this
->where('is_order',0)
->where('active',0)
->orderBy('avance_date', 'DESC') ->findAll();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage()); log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false; return false;
} }
} }
else{ try {
if($id){ return $this
try { ->where('is_order',0)
return $this->where('user_id',$id) ->where('active',0)
->orderBy('avance_date', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
} else {
if($id){
try {
return $this->where('user_id',$id)
->where('is_order',0) ->where('is_order',0)
->where('active',0) ->where('active',0)
->where('store_id',$users['store_id']) ->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC')
->findAll(); ->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
try {
return $this
->where('is_order',0)
->where('active',0)
->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC') ->findAll();
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage()); log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false; return false;
} }
} }
try {
return $this
->where('is_order',0)
->where('active',0)
->where('store_id',$users['store_id'])
->orderBy('avance_date', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
return false;
}
}
} }
public function checkExpiredAvance() public function checkExpiredAvance() {
{
$now = date('Y-m-d'); $now = date('Y-m-d');
$avances = $this->where('active', '1') $avances = $this->where('active', '1')
->where('DATE_ADD(avance_date, INTERVAL 15 DAY) <', $now) ->where('deadline <', $now)
->findAll(); ->findAll();
if (!empty($avances)) { if (!empty($avances)) {
$productModel = new Products(); $productModel = new Products();
foreach ($avances as $avance) { foreach ($avances as $avance) {
// Mettre l'avance à expirée $this->update($avance['avance_id'], ['active' => '0']);
$this->update($avance['id'], ['active' => '0']);
// Remettre le produit disponible
$productModel->update($avance['product_id'], ['product_sold' => 0]); $productModel->update($avance['product_id'], ['product_sold' => 0]);
} }
} }
} }
} /**
* Récupérer les avances qui arrivent à échéance dans X jours
*/
public function getAvancesNearDeadline($days = 3)
{
$alertDate = date('Y-m-d', strtotime("+{$days} days"));
return $this->select('avances.*, users.store_id')
->join('users', 'users.id = avances.user_id')
->where('avances.is_order', 0)
->where('avances.active', 1)
->where('avances.amount_due >', 0)
->where('DATE(avances.deadline)', $alertDate)
->findAll();
}
}

89
app/Views/attributes/addvalue.php

@ -173,27 +173,46 @@
$(document).ready(function() { $(document).ready(function() {
$("#attributeNav").addClass('active'); $("#attributeNav").addClass('active');
// initialize the datatable // initialisation de la DataTable en français
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': base_url + 'attributes/fetchAttributeValueData/' + <?php echo $attribute_data['id']; ?>, 'ajax': base_url + 'attributes/fetchAttributeValueData/' + <?php echo $attribute_data['id']; ?>,
'order': [] 'order': [],
"language": {
"sProcessing": "Traitement en cours...",
"sSearch": "Rechercher&nbsp;:",
"sLengthMenu": "Afficher _MENU_ &eacute;l&eacute;ments",
"sInfo": "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
"sInfoEmpty": "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
"sInfoFiltered": "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
"sInfoPostFix": "",
"sLoadingRecords": "Chargement en cours...",
"sZeroRecords": "Aucun &eacute;l&eacute;ment &agrave; afficher",
"sEmptyTable": "Aucune donn&eacute;e disponible dans le tableau",
"oPaginate": {
"sFirst": "Premier",
"sPrevious": "Pr&eacute;c&eacute;dent",
"sNext": "Suivant",
"sLast": "Dernier"
},
"oAria": {
"sSortAscending": ": activer pour trier la colonne par ordre croissant",
"sSortDescending": ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
}); });
// submit the create from // soumission du formulaire de création
$("#createForm").unbind('submit').on('submit', function() { $("#createForm").unbind('submit').on('submit', function() {
var form = $(this); var form = $(this);
// remove the text-danger
$(".text-danger").remove(); $(".text-danger").remove();
$.ajax({ $.ajax({
url: form.attr('action'), url: form.attr('action'),
type: form.attr('method'), type: form.attr('method'),
data: form.serialize(), // /converting the form data into array and sending it to server data: form.serialize(),
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
@ -201,20 +220,15 @@
if (response.success === true) { if (response.success === true) {
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' + $("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + '<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">&times;</span></button>' +
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages + '<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
'</div>'); '</div>');
// hide the modal
$("#addModal").modal('hide'); $("#addModal").modal('hide');
// reset the form
$("#createForm")[0].reset(); $("#createForm")[0].reset();
$("#createForm .form-group").removeClass('has-error').removeClass('has-success'); $("#createForm .form-group").removeClass('has-error').removeClass('has-success');
} else { } else {
if (response.messages instanceof Object) { if (response.messages instanceof Object) {
$.each(response.messages, function(index, value) { $.each(response.messages, function(index, value) {
var id = $("#" + index); var id = $("#" + index);
@ -229,8 +243,8 @@
}); });
} else { } else {
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' + $("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + '<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">&times;</span></button>' +
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages + '<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
'</div>'); '</div>');
} }
} }
@ -242,31 +256,24 @@
}); });
// edit function // fonction de modification
// id => attribute value id
function editFunc(id) { function editFunc(id) {
$.ajax({ $.ajax({
url: base_url + 'attributes/fetchAttributeValueById/' + id, url: base_url + 'attributes/fetchAttributeValueById/' + id,
type: 'post', type: 'post',
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
console.log(response);
$("#edit_attribute_value_name").val(response.value); $("#edit_attribute_value_name").val(response.value);
// submit the edit from
$("#updateForm").unbind('submit').bind('submit', function() { $("#updateForm").unbind('submit').bind('submit', function() {
var form = $(this); var form = $(this);
// remove the text-danger
$(".text-danger").remove(); $(".text-danger").remove();
$.ajax({ $.ajax({
url: form.attr('action') + '/' + id, url: form.attr('action') + '/' + id,
type: form.attr('method'), type: form.attr('method'),
data: form.serialize(), // /converting the form data into array and sending it to server data: form.serialize(),
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
@ -274,18 +281,14 @@
if (response.success === true) { if (response.success === true) {
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' + $("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + '<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">&times;</span></button>' +
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages + '<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
'</div>'); '</div>');
// hide the modal
$("#editModal").modal('hide'); $("#editModal").modal('hide');
// reset the form
$("#updateForm .form-group").removeClass('has-error').removeClass('has-success'); $("#updateForm .form-group").removeClass('has-error').removeClass('has-success');
} else { } else {
if (response.messages instanceof Object) { if (response.messages instanceof Object) {
$.each(response.messages, function(index, value) { $.each(response.messages, function(index, value) {
var id = $("#" + index); var id = $("#" + index);
@ -300,8 +303,8 @@
}); });
} else { } else {
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' + $("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + '<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">&times;</span></button>' +
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages + '<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
'</div>'); '</div>');
} }
} }
@ -315,22 +318,18 @@
}); });
} }
// remove functions // fonction de suppression
function removeFunc(id) { function removeFunc(id) {
if (id) { if (id) {
$("#removeForm").on('submit', function() { $("#removeForm").on('submit', function() {
var form = $(this); var form = $(this);
// remove the text-danger
$(".text-danger").remove(); $(".text-danger").remove();
$.ajax({ $.ajax({
url: form.attr('action'), url: form.attr('action'),
type: form.attr('method'), type: form.attr('method'),
data: { data: { attribute_value_id: id },
attribute_value_id: id
},
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
@ -338,18 +337,16 @@
if (response.success === true) { if (response.success === true) {
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' + $("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + '<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">&times;</span></button>' +
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages + '<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
'</div>'); '</div>');
// hide the modal
$("#removeModal").modal('hide'); $("#removeModal").modal('hide');
} else { } else {
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' + $("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + '<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">&times;</span></button>' +
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages + '<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
'</div>'); '</div>');
} }
} }
@ -359,4 +356,4 @@
}); });
} }
} }
</script> </script>

831
app/Views/avances/avance.php

@ -96,11 +96,29 @@
<div class="modal-content"> <div class="modal-content">
<form id="create_avance_form"> <form id="create_avance_form">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Ajouter une avance</h4> <h4 class="modal-title"> Ajouter une avance </h4>
<button type="button" class="close" data-dismiss="modal">&times;</button> <button type="button" class="close" data-dismiss="modal">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="row"> <div class="row">
<!-- type d'avance -->
<!-- <div class="form-group col-md-6">
<label for="id_product" class="form-label">Type d'avance</label>
<select name="id_product" id="id_product" class="form-control " required>
<option value="" disabled selected>Sélectionnez un type d'avance </option>
<option value="terre">Avance sur terre</option>
<option value="mere">Avance sur mère</option>
</select>
</div> -->
<div class="form-group col-md-6">
<label for="type_avance" class="form-label">Type d'avance</label>
<select name="type_avance" id="type_avance" class="form-control" required>
<option value="" disabled selected>Sélectionnez un type d'avance</option>
<option value="terre">Avance sur terre</option>
<option value="mere">Avance sur mère</option>
</select>
</div>
<!-- Nom client --> <!-- Nom client -->
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label>Nom du client</label> <label>Nom du client</label>
@ -129,15 +147,15 @@
<div class="row"> <div class="row">
<!-- Produit --> <!-- Produit -->
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label for="id_product" class="form-label">Produit</label> <label for="id_product" class="form-label">Produit</label>
<select name="id_product" id="id_product" class="form-control " onchange="getProductDataCreate()" required> <select name="id_product" id="id_product" class="form-control" onchange="getProductDataCreate()" required>
<option value="">Sélectionnez un produit</option> <option value="">Sélectionnez un produit</option>
<?php foreach($products as $p): ?> <?php foreach($products as $p): ?>
<option value="<?= $p['id'] ?>" <?= $p['product_sold'] ? 'disabled' : '' ?>> <option value="<?= $p['id'] ?>" <?= $p['product_sold'] ? 'disabled' : '' ?>>
<?= esc($p['name']) ?> <?= $p['product_sold'] ? '(Rupture)' : '' ?> <?= esc($p['name']) ?> <?= $p['product_sold'] ? '(Rupture)' : '' ?>
</option> </option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<!-- Prix brut --> <!-- Prix brut -->
<div class="form-group col-md-6"> <div class="form-group col-md-6">
@ -182,6 +200,16 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="row"> <div class="row">
<!-- Type d'avance -->
<div class="form-group col-md-6">
<label for="type_avance_edit" class="form-label">Type d'avance</label>
<select name="type_avance_edit" id="type_avance_edit" class="form-control" required>
<option value="" disabled>Sélectionnez un type d'avance</option>
<option value="terre">Avance sur terre</option>
<option value="mere">Avance sur mère</option>
</select>
</div>
<!-- Nom client --> <!-- Nom client -->
<div class="form-group col-md-6"> <div class="form-group col-md-6">
<label>Nom du client</label> <label>Nom du client</label>
@ -249,362 +277,493 @@
</div> </div>
<?php endif;?> <?php endif;?>
<?php if (in_array('deleteAvance', $user_permission)): ?> <?php if (in_array('deleteAvance', $user_permission)): ?>
<!-- remove brand modal --> <!-- remove brand modal -->
<div class="modal fade" tabindex="-1" role="dialog" id="removeModal"> <div class="modal fade" tabindex="-1" role="dialog" id="removeModal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<h4 class="modal-title">Supprimer cette avance</h4> <span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Supprimer cette avance</h4>
</div>
<form role="form" action="<?php echo base_url('avances/deleteAvance') ?>" method="post" id="removeForm">
<input type="hidden" name="avance_id" value="">
<input type="hidden" name="product_id" value="">
<div class="modal-body">
<p>Voulez-vous vraiment supprimer ?</p>
</div> </div>
<form role="form" action="<?php echo base_url('avances/deleteAvance') ?>" method="post" id="removeForm"> <div class="modal-footer">
<div class="modal-body"> <button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
<p>Voulez-vous vraiment supprimer ?</p> <button type="submit" class="btn btn-primary">Oui</button>
</div> </div>
<div class="modal-footer"> </form>
<button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button> </div><!-- /.modal-content -->
<button type="submit" class="btn btn-primary">Oui</button> </div><!-- /.modal-dialog -->
</div> </div><!-- /.modal -->
</form> <?php endif; ?>
<script>
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0;
$(document).ready(function() {
$('#avance_menu').addClass("active");
$('.select2').select2();
// 📌 Configuration langue FR
var datatableLangFr = {
lengthMenu: "Afficher _MENU_ enregistrements par page",
zeroRecords: "Aucun résultat trouvé",
info: "Affichage de _START_ à _END_ sur _TOTAL_ enregistrements",
infoEmpty: "Aucun enregistrement disponible",
infoFiltered: "(filtré depuis _MAX_ enregistrements au total)",
search: "Rechercher :",
paginate: {
first: "Premier",
last: "Dernier",
next: "Suivant",
previous: "Précédent"
}
};
</div><!-- /.modal-content --> // 📌 Fonction pour initialiser la DataTable
</div><!-- /.modal-dialog --> function initAvanceTable(url, columns) {
</div><!-- /.modal --> if ($.fn.DataTable.isDataTable('#avanceTable')) {
<?php endif; ?> $('#avanceTable').DataTable().destroy();
}
return $('#avanceTable').DataTable({
ajax: url,
columns: columns,
language: datatableLangFr
});
}
<script> // 🔄 FONCTION DE MISE À JOUR DYNAMIQUE DE LA DATATABLE
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0; function refreshDataTable() {
$(document).ready(function() { if (typeof manageTable !== 'undefined' && manageTable) {
$('#avance_menu').addClass("active"); manageTable.ajax.reload(null, false); // Recharger seulement les données de la table
$('.select2').select2(); }
<?php if ($isAdmin):?> }
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceData', <?php if ($isAdmin): ?>
columns: [ var adminColumns = [
{ title: "Client" }, { title: "Client" },
{ title: "Téléphone" }, { title: "Téléphone" },
{ title: "Adresse" }, { title: "Adresse" },
{ title: "Produit" }, { title: "Produit" },
{ title: "Prix" }, { title: "Prix" },
{ title: "Avance" }, { title: "Avance" },
{ title: "Reste à payer" }, { title: "Reste à payer" },
{ title: "Date" } { title: "Date" }
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?> <?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
,{ title: "Action", orderable: false, searchable: false } ,{ title: "Action", orderable: false, searchable: false }
<?php endif; ?> <?php endif; ?>
] ];
});
$('#avance_order').on('click', function () {
$('#avanceTable').DataTable().destroy();
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceBecameOrder',
columns: [
{ title: "Client" },
{ title: "Téléphone" },
{ title: "Adresse" },
{ title: "Produit" },
{ title: "Prix" },
{ title: "Avance" },
{ title: "Reste à payer" },
{ title: "Date" }
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
,{ title: "Action", orderable: false, searchable: false }
<?php endif; ?>
]
});
});
$('#avance_expired').on('click', function () { var manageTable = initAvanceTable('fetchAvanceData', adminColumns);
$('#avanceTable').DataTable().destroy();
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchExpiredAvance',
columns: [
{ title: "Client" },
{ title: "Téléphone" },
{ title: "Adresse" },
{ title: "Produit" },
{ title: "Prix" },
{ title: "Avance" },
{ title: "Reste à payer" },
{ title: "Date" }
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
,{ title: "Action", orderable: false, searchable: false }
<?php endif; ?>
]
});
});
$('#avance_order').on('click', function () {
manageTable = initAvanceTable('fetchAvanceBecameOrder', adminColumns);
});
$('#avance_no_order').on('click', function () { $('#avance_expired').on('click', function () {
$('#avanceTable').DataTable().destroy(); manageTable = initAvanceTable('fetchExpiredAvance', adminColumns);
manageTable = $('#avanceTable').DataTable({ });
ajax: 'fetchAvanceData',
columns: [ $('#avance_no_order').on('click', function () {
{ title: "Client" }, manageTable = initAvanceTable('fetchAvanceData', adminColumns);
{ title: "Téléphone" }, });
{ title: "Adresse" }, <?php endif; ?>
{ title: "Produit" },
{ title: "Prix" }, <?php if ($isCaissier || $isCommerciale): ?>
{ title: "Avance" }, var userColumns = [
{ title: "Reste à payer" }, { title: "#" },
{ title: "Date" } { title: "Produit" },
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?> { title: "Avance" },
,{ title: "Action", orderable: false, searchable: false } { title: "Reste à payer" },
<?php endif; ?> { title: "Date" }
] <?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
});
});
<?php endif; ?>
<?php if ($isCaissier || $isCommerciale):?>
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceData',
columns: [
{ title: "#" },
{ title: "Produit" },
{ title: "Avance" },
{ title: "Reste à payer" },
{ title: "Date" }
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
,{ title: "Action", orderable: false, searchable: false } ,{ title: "Action", orderable: false, searchable: false }
<?php endif; ?> <?php endif; ?>
] ];
});
$('#avance_order').on('click', function () {
$('#avanceTable').DataTable().destroy();
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceBecameOrder',
columns: [
{ title: "#" },
{ title: "Produit" },
{ title: "Avance" },
{ title: "Reste à payer" },
{ title: "Date" }
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
,{ title: "Action", orderable: false, searchable: false }
<?php endif; ?>
]
});
});
$('#avance_no_order').on('click', function () { var manageTable = initAvanceTable('fetchAvanceData', userColumns);
$('#avanceTable').DataTable().destroy();
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceData',
columns: [
{ title: "#" },
{ title: "Produit" },
{ title: "Avance" },
{ title: "Reste à payer" },
{ title: "Date" }
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
,{ title: "Action", orderable: false, searchable: false }
<?php endif; ?>
]
});
});
<?php endif; ?>
$('#avance_order').on('click', function () {
manageTable = initAvanceTable('fetchAvanceBecameOrder', userColumns);
});
$('#avance_no_order').on('click', function () {
manageTable = initAvanceTable('fetchAvanceData', userColumns);
});
<?php endif; ?>
// Création AJAX // ✅ CRÉATION avec actualisation automatique
$('#create_avance_form').on('submit', function(e) { $('#create_avance_form').on('submit', function(e) {
e.preventDefault(); e.preventDefault();
const $form = $(this); const $form = $(this);
$.post('/avances/createAvance', $form.serialize(), function(res) { var brut = parseFloat($('#gross_amount').val()) || 0;
if (res.success === true) { var avance = parseFloat($('#avance_amount').val()) || 0;
var minAvance = brut * 0.25;
manageTable.ajax.reload(null, false);
if (avance < minAvance) {
$("#messages").html( $("#messages").html(`
`<div class="alert alert-success alert-dismissible" role="alert"> <div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages} <strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong>
</div>` L'avance doit être au minimum de 25% du prix du produit (${minAvance.toFixed(2)} Ar).
); </div>
`);
// Cacher le modal de création return;
$("#createModal").modal('hide');
$form[0].reset();
} else {
$("#createModal").modal('hide');
$("#messages").html(
`<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${res.messages}
</div>`
);
} }
}, 'json');
// Désactiver le bouton de soumission
$form.find('button[type="submit"]').prop('disabled', true).text('Enregistrement...');
$.post('/avances/createAvance', $form.serialize(), function(res) {
if (res.success === true) {
$("#messages").html(`
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages}
</div>
`);
$("#createModal").modal('hide');
$form[0].reset(); // Reset le formulaire
// 🔄 MISE À JOUR DYNAMIQUE après ajout
refreshDataTable();
// Auto-masquer le message après 3 secondes
setTimeout(function() {
$("#messages .alert").fadeOut();
}, 3000);
} else {
$("#createModal").modal('hide');
$("#messages").html(`
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${res.messages}
</div>
`);
// Réactiver le bouton en cas d'erreur
$form.find('button[type="submit"]').prop('disabled', false).text('Enregistrer');
}
}, 'json');
}); });
}); // 🔥 SUPPRESSION avec actualisation automatique
window.removeFunc = function(id, product_id) {
$('#removeModal').modal('show');
$('#removeForm input[name="avance_id"]').val(id);
$('#removeForm input[name="product_id"]').val(product_id);
};
$('#removeForm').on('submit', function(e) {
e.preventDefault();
var form = $(this);
var submitButton = form.find('button[type="submit"]');
// Désactiver le bouton
submitButton.prop('disabled', true).text('Suppression...');
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
data: form.serialize(),
dataType: 'json',
success: function(response) {
if (response.success === true) {
$('#removeModal').modal('hide');
$("#messages").html(`
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${response.messages}
</div>
`);
// 🔄 MISE À JOUR DYNAMIQUE après suppression
refreshDataTable();
// Auto-masquer le message après 3 secondes
setTimeout(function() {
$("#messages .alert").fadeOut();
}, 3000);
} else {
$('#removeModal').modal('hide');
$("#messages").html(`
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${response.messages}
</div>
`);
}
},
error: function(xhr, status, error) {
console.log('Erreur AJAX:', error);
$('#removeModal').modal('hide');
$("#messages").html(`
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> Une erreur est survenue lors de la suppression.
</div>
`);
},
complete: function() {
submitButton.prop('disabled', false).text('Oui');
}
});
return false;
});
// Récupère prix pour création // Réinitialiser le modal à la fermeture
function getProductDataCreate(){ $('#removeModal').on('hidden.bs.modal', function() {
$('#removeForm')[0].reset();
$('#removeForm button[type="submit"]').prop('disabled', false).text('Oui');
});
}); // Fin document.ready
// --- Fonctions utilitaires ---
function getProductDataCreate() {
var id = $('#id_product').val(); var id = $('#id_product').val();
if(!id){ brutCreate=0; $('#gross_amount,#amount_due').val(''); return; } if (!id) {
else{ brutCreate = 0;
$.post(base_url+'orders/getProductValueById',{product_id:id}, function(r){ $('#gross_amount,#amount_due,#avance_amount').val('');
brutCreate = parseFloat(r.prix_vente)||0; return;
$('#gross_amount').val(brutCreate);
$('#amount_due').val(brutCreate);
updateDueCreate();
},'json');
} }
$.post(base_url + 'orders/getProductValueById', { product_id: id }, function(r) {
} brutCreate = parseFloat(r.prix_vente) || 0;
$('#gross_amount').val(brutCreate.toFixed(2));
var avance25 = brutCreate * 0.25;
$('#avance_amount').val(avance25.toFixed(2));
$('#amount_due').val((brutCreate - avance25).toFixed(2));
updateDueCreate();
}, 'json');
}
function getProductDataUpdate(){ function updateDueCreate() {
var av = parseFloat($('#avance_amount').val()) || 0;
var brutAmount = parseFloat($('#gross_amount').val()) || 0;
$('#amount_due').val(Math.max(brutAmount - av, 0));
}
function getProductDataUpdate() {
var id = $('#id_product_edit').val(); var id = $('#id_product_edit').val();
if(!id){ brutCreate=0; $('#gross_amount_edit,#amount_due_edit').val(''); return; } console.log('getProductDataUpdate appelée avec ID:', id); // Pour déboguer
else{
$.post(base_url+'orders/getProductValueById',{product_id:id}, function(r){ if (!id) {
brutCreate = parseFloat(r.prix_vente)||0; brutEdit = 0;
$('#gross_amount_edit').val(brutCreate); $('#gross_amount_edit,#amount_due_edit,#avance_amount_edit').val('');
$('#amount_due_edit').val(brutCreate); return;
updateDueCreate();
},'json');
} }
} $.ajax({
function updateDueCreate(){ url: base_url + 'orders/getProductValueById',
var av = parseFloat($('#avance_amount').val())||0; type: 'POST',
var brutAmount = parseFloat($('#gross_amount').val())||0; data: { product_id: id },
$('#amount_due').val(Math.max(brutAmount-av,0)); dataType: 'json',
} success: function(r) {
console.log('Données produit reçues:', r); // Pour déboguer
// Prépare et affiche modal édition
function editFunc(id) { brutEdit = parseFloat(r.prix_vente) || 0;
// Récupération des données de l'avance $('#gross_amount_edit').val(brutEdit.toFixed(2));
$.getJSON(base_url + 'avances/fetchSingleAvance/' + id, function(r) { var avance25 = brutEdit * 0.25;
// Préremplissage du modal $('#avance_amount_edit').val(avance25.toFixed(2));
$('#customer_name_avance_edit').val(r.customer_name); $('#amount_due_edit').val((brutEdit - avance25).toFixed(2));
$('#customer_phone_avance_edit').val(r.customer_phone); updateDueEdit();
$('#customer_adress_avance_edit').val(r.customer_adress); },
$('#customer_cin_avance_edit').val(r.customer_cin); error: function(xhr, status, error) {
$('#gross_amount_edit').val(r.gross_amount); console.log('Erreur lors de la récupération des données produit:', error);
$('#avance_amount_edit').val(r.avance_amount);
$('#amount_due_edit').val(r.amount_due);
const product_id = r.product_id;
const productSelect = $("#id_product_edit");
const optionExists = productSelect.find('option').filter(function() {
return $(this).val().trim().toLowerCase() === product_id.trim().toLowerCase();
}).length > 0;
if (optionExists) {
productSelect.val(product_id);
} else {
productSelect.val("");
}
// Calcul du montant brut initial
brutEdit = parseFloat(r.avance_amount) + parseFloat(r.amount_due);
// Affiche le modal
$('#updateModal').modal('show');
$('#update_avance_form').off('submit');
$('#update_avance_form').on('submit', function(e) {
e.preventDefault();
const actionUrl = base_url + 'avances/updateAvance/' + id;
// Envoi AJAX
$.post(actionUrl, $(this).serialize(), function(res) {
if (res.success) {
$('#updateModal').modal('hide');
manageTable.ajax.reload(null, false);
$("#messages").html(
`<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages}
</div>`
);
} else {
$("#messages").html(
`<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages}
</div>`
);
alert(res.messages || 'Erreur lors de la modification.');
} }
}, 'json');
}); });
});
} }
function updateDueEdit(){ function updateDueEdit() {
var av = parseFloat($('#avance_amount_edit').val())||0; var av = parseFloat($('#avance_amount_edit').val()) || 0;
$('#amount_due_edit').val(Math.max(brutEdit-av,0).toFixed(2)); $('#amount_due_edit').val(Math.max(brutEdit - av, 0).toFixed(2));
} }
function removeFunc(id, product_id) {
// Affiche correctement le modal
$('#removeModal').modal('show');
if (id) {
// On évite d'attacher plusieurs fois le submit handler
$('#removeForm').off('submit').on('submit', function(e) {
e.preventDefault();
var form = $(this);
// Supprime les anciens messages d'erreur // ✅ MODIFICATION avec mise à jour dynamique - CORRIGÉ
$(".text-danger").remove(); function editFunc(id) {
// Réinitialiser d'abord le formulaire
$('#update_avance_form')[0].reset();
$.getJSON(base_url + 'avances/fetchSingleAvance/' + id, function(r) {
console.log('Données récupérées:', r); // Pour déboguer
// Pré-remplir le formulaire de modification avec les BONS IDs
$('#avance_id_edit').val(r.id || id); // Le champ caché pour l'ID
$('#customer_name_avance_edit').val(r.customer_name || '');
$('#customer_phone_avance_edit').val(r.customer_phone || '');
$('#customer_address_avance_edit').val(r.customer_address || r.customer_adress || '');
$('#customer_cin_avance_edit').val(r.customer_cin || '');
$('#type_avance_edit').val(r.type_avance || '');
$('#gross_amount_edit').val(r.gross_amount || '');
$('#avance_amount_edit').val(r.avance_amount || '');
$('#amount_due_edit').val(r.amount_due || '');
// 🔥 CORRECTION PRINCIPALE - Sélection du produit avec toutes les variantes possibles
var productId = r.product_id || r.id_product || r.productId || r.idProduct;
console.log('Product ID trouvé:', productId);
console.log('Options disponibles:', $('#id_product_edit option').length);
brutEdit = parseFloat(r.gross_amount || 0); // Utiliser gross_amount directement
// Réinitialiser le bouton
$('#update_avance_form button[type="submit"]').prop('disabled', false).text('Modifier');
// Ouvrir le modal
$('#updateModal').modal('show');
// 🔥 SÉLECTION DU PRODUIT après ouverture complète du modal
$('#updateModal').on('shown.bs.modal', function() {
if (productId) {
console.log('Tentative de sélection du produit ID:', productId);
// Méthode 1: Sélection standard
$('#id_product_edit').val(productId);
console.log('Après sélection standard:', $('#id_product_edit').val());
// Méthode 2: Si la sélection standard ne marche pas
if ($('#id_product_edit').val() != productId) {
$('#id_product_edit option').each(function() {
if ($(this).val() == productId) {
$(this).prop('selected', true);
console.log('Produit sélectionné via boucle:', $(this).val(), $(this).text());
}
});
}
// Déclencher l'événement change pour mettre à jour le prix
$('#id_product_edit').trigger('change');
console.log('Valeur finale du select:', $('#id_product_edit').val());
}
// Détacher l'événement pour éviter les appels multiples
$('#updateModal').off('shown.bs.modal');
});
// 🔥 Détacher tous les anciens événements et attacher le nouveau
$('#update_avance_form').off('submit').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $submitBtn = $form.find('button[type="submit"]');
var avance = parseFloat($('#avance_amount_edit').val()) || 0;
var minAvance = brutEdit * 0.25;
// Validation 25%
if (avance < minAvance) {
$("#messages").html(`
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong>
L'avance doit être au minimum de 25% du prix du produit (${minAvance.toFixed(2)} Ar).
</div>
`);
return;
}
// Désactiver le bouton pendant l'opération
$submitBtn.prop('disabled', true).text('Modification...');
$.ajax({
url: base_url + 'avances/updateAvance/' + id,
type: 'POST',
data: $form.serialize(),
dataType: 'json',
success: function(res) {
if (res.success === true) {
// Fermer le modal
$('#updateModal').modal('hide');
// Message de succès
$("#messages").html(`
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages}
</div>
`);
// 🔄 MISE À JOUR IMMÉDIATE de la DataTable
refreshDataTable();
// Auto-masquer le message
setTimeout(function() {
$("#messages .alert").fadeOut();
}, 3000);
} else {
$("#messages").html(`
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${res.messages}
</div>
`);
}
},
error: function(xhr, status, error) {
console.log('Erreur lors de la modification:', error);
$("#messages").html(`
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> Erreur lors de la modification.
</div>
`);
},
complete: function() {
// Réactiver le bouton dans tous les cas
$submitBtn.prop('disabled', false).text('Modifier');
}
});
});
}).fail(function() {
console.log('Erreur lors du chargement des données');
$("#messages").html(`
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> Erreur lors du chargement des données.
</div>
`);
});
$.ajax({ // Réinitialiser le modal à la fermeture
url: form.attr('action'), $('#updateModal').on('hidden.bs.modal', function() {
type: form.attr('method'), $('#update_avance_form')[0].reset();
data: { $('#update_avance_form button[type="submit"]').prop('disabled', false).text('Modifier');
avance_id: id,
product_id: product_id
},
dataType: 'json',
success: function(response) {
manageTable.ajax.reload(null, false);
if (response.success === true) {
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
'<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
'</div>');
// Cache le modal
$("#removeModal").modal('hide');
} else {
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
'<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
'</div>');
}
}
});
return false;
}); });
}
} }
</script>
</script>

25
app/Views/brands/index.php

@ -189,6 +189,31 @@
$("#brandNav").addClass('active'); $("#brandNav").addClass('active');
// initialize the datatable // initialize the datatable
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': '<?= base_url('brands/fetchBrandData') ?>', 'ajax': '<?= base_url('brands/fetchBrandData') ?>',
'order': [] 'order': []

25
app/Views/category/index.php

@ -186,6 +186,31 @@
$("#categoryNav").addClass('active'); $("#categoryNav").addClass('active');
// initialize the datatable // initialize the datatable
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': 'fetchCategoryData', 'ajax': 'fetchCategoryData',
'order': [] 'order': []

94
app/Views/commercial/addImage.php

@ -14,24 +14,28 @@
<div class="row g-0"> <div class="row g-0">
<!-- Left: Product Image --> <!-- Left: Product Image -->
<div class="col-md-6 text-center"> <div class="col-md-6 text-center">
<img src="<?= base_url('assets/images/product_image/' . $products['image']) ?>" <img src="<?= base_url('assets/images/product_image/' . ($products['image'] ?? 'default.png')) ?>"
alt="<?= $products['name'] ?>" alt="<?= esc($products['name'] ?? '') ?>"
class="img-fluid rounded" class="img-fluid rounded"
style="width: 100%; height: 100%; object-fit: cover;"> style="width: 100%; height: 100%; object-fit: cover;">
</div> </div>
<!-- Right: Product Details --> <!-- Right: Product Details -->
<div class="col-md-6 p-4"> <div class="col-md-6 p-4">
<h2 class="text-dark"><?= $products['name'] ?></h2> <h2 class="text-dark"><?= esc($products['name'] ?? '') ?></h2>
<h4 class="text-success fw-bold"><?= number_format($products['price'], 0, ',', ' ') ?> MGA</h4> <h4 class="text-success fw-bold">
<p class="text-muted">En stock: <strong><?= $products['qty'] ?></strong></p> <?= number_format((float) ($products['price'] ?? 0), 0, ',', ' ') ?> MGA
<p class="text-secondary"><?= $products['description'] ?></p> </h4>
<p class="text-muted">
En stock: <strong><?= esc($products['qty'] ?? 0) ?></strong>
</p>
<p class="text-secondary"><?= esc($products['description'] ?? '') ?></p>
<!-- Buttons --> <!-- Buttons -->
<div class="d-flex mt-4"> <div class="d-flex mt-4">
<form action="<?= base_url('ventes/moreimage/') . $products['id'] ?>" enctype="multipart/form-data" method="post"> <form action="<?= base_url('ventes/moreimage/') . ($products['id'] ?? 0) ?>" enctype="multipart/form-data" method="post">
<div class="form-group"> <div class="form-group">
<label for="image"> <b>Ajouter plus d'images</b></label> <label for="image"><b>Ajouter plus d'images</b></label>
<input type="file" required name="images[]" multiple accept="image/*" id="image" class="form-control"> <input type="file" required name="images[]" multiple accept="image/*" id="image" class="form-control">
</div> </div>
<button type="submit" class="btn btn-primary me-2"> <button type="submit" class="btn btn-primary me-2">
@ -47,50 +51,56 @@
</div> </div>
<h3>Galleries</h3> <h3>Galleries</h3>
<div class="row"> <div class="row">
<?php foreach ($galleries as $key => $value) { ?> <?php if (!empty($galleries)) : ?>
<div class="col-md-3 col-xs-12 col-sm-6 mb-3" style="margin-bottom: 1%;"> <?php foreach ($galleries as $key => $value) : ?>
<div class="card shadow-lg border-0 rounded"> <div class="col-md-3 col-xs-12 col-sm-6 mb-3" style="margin-bottom: 1%;">
<div class="card-header bg-success text-white text-center p-0.5"> <div class="card shadow-lg border-0 rounded">
<!-- Clickable Image --> <div class="card-header bg-success text-white text-center p-0.5">
<a href="#" data-toggle="modal" data-target="#imageModal<?= $key ?>"> <!-- Clickable Image -->
<img src="<?= base_url('assets/images/product_image/' . $value['images']) ?>" <a href="#" data-toggle="modal" data-target="#imageModal<?= $key ?>">
alt="<?= $value['images'] ?>" <img src="<?= base_url('assets/images/product_image/' . ($value['images'] ?? 'default.png')) ?>"
class="img-fluid img-thumbnail rounded" alt="<?= esc($value['images'] ?? '') ?>"
style="width: 100%; height: 250px; object-fit: cover;"> class="img-fluid img-thumbnail rounded"
</a> style="width: 100%; height: 250px; object-fit: cover;">
</div> </a>
</div>
<div class="card-footer" style="padding: 1%;"> <div class="card-footer" style="padding: 1%;">
<form action="<?= base_url('ventes/moreimage/supp/') . $value['id'] ?>" method="post"> <form action="<?= base_url('ventes/moreimage/supp/') . ($value['id'] ?? 0) ?>" method="post">
<button class="btn btn-danger">Supprimer</button> <button class="btn btn-danger">Supprimer</button>
</form> </form>
</div>
</div> </div>
</div> </div>
</div>
<!-- Bootstrap Modal for Each Image --> <!-- Bootstrap Modal for Each Image -->
<div class="modal fade" id="imageModal<?= $key ?>" tabindex="-1" aria-labelledby="imageModalLabel<?= $key ?>" aria-hidden="true"> <div class="modal fade" id="imageModal<?= $key ?>" tabindex="-1" aria-labelledby="imageModalLabel<?= $key ?>" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="imageModalLabel<?= $key ?>">Visualisation d'image</h5> <h5 class="modal-title" id="imageModalLabel<?= $key ?>">Visualisation d'image</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"> <i class="fas fa-times"></i></button> <button type="button" class="btn-close" data-dismiss="modal" aria-label="Close">
</div> <i class="fas fa-times"></i>
<div class="modal-body text-center" style="width: 100%;"> </button>
<img src="<?= base_url('assets/images/product_image/' . $value['images']) ?>" </div>
alt="<?= $value['images'] ?>" <div class="modal-body text-center" style="width: 100%;">
class="img-fluid rounded" <img src="<?= base_url('assets/images/product_image/' . ($value['images'] ?? 'default.png')) ?>"
style="width: 100%; height: 100%; object-fit: cover;">> alt="<?= esc($value['images'] ?? '') ?>"
class="img-fluid rounded"
style="width: 100%; height: 100%; object-fit: cover;">
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <?php endforeach; ?>
<?php } ?> <?php else : ?>
<p class="text-muted p-3">Aucune image disponible dans la galerie.</p>
<?php endif; ?>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
<script> <script>
$("#espaceMainMenu").addClass('active'); $("#espaceMainMenu").addClass('active');
</script> </script>

25
app/Views/commercial/index.php

@ -40,6 +40,31 @@
</div> </div>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$("#espaceMainMenu").addClass('active'); $("#espaceMainMenu").addClass('active');
const id = <?php echo json_encode($id); ?>; const id = <?php echo json_encode($id); ?>;

16
app/Views/commercial/single.php

@ -32,7 +32,21 @@
<p class="text-secondary">Moteur N° <?= $products['numero_de_moteur'] ?></p> <p class="text-secondary">Moteur N° <?= $products['numero_de_moteur'] ?></p>
<p class="text-secondary">Boutique <b><?= $stores ?></b></p> <p class="text-secondary">Boutique <b><?= $stores ?></b></p>
<p class="text-secondary"><?= $products['description'] ?></p> <p class="text-secondary"><?= $products['description'] ?></p>
<p class="text-secondary"> <b>Kit</b> <br><?= $products['etats'] == 1 ? $products['infoManquekit'] : 'Non Kit' ?></p> <p class="text-secondary">
<b>Etat</b> <br>
<?php
switch($products['etats']) {
case 1:
echo 'Kit';
break;
case 2:
echo 'Non kit';
break;
default:
echo 'Non défini';
}
?>
</p>
<!-- Buttons --> <!-- Buttons -->
<div class="d-flex mt-4"> <div class="d-flex mt-4">

26
app/Views/dashboard.php

@ -1,4 +1,3 @@
<!-- Content Wrapper. Contains page content -->
<style> <style>
.card { .card {
border-radius: 12px; border-radius: 12px;
@ -147,7 +146,7 @@
<div class="small-box" style="background-color: #A9A9A9;"> <div class="small-box" style="background-color: #A9A9A9;">
<div class="inner"> <div class="inner">
<h2><?php echo number_format($total, 0, '.', ' '); ?>Ar</h2> <h2><?php echo number_format($total, 0, '.', ' '); ?>Ar</h2>
<p>Totale CAISSE</p> <p>Totale FLUX</p>
</div> </div>
<div class="icon"> <div class="icon">
<i class="fa fa-credit-card"></i> <i class="fa fa-credit-card"></i>
@ -381,6 +380,29 @@
$(document).ready(function () { $(document).ready(function () {
// Initialize the datatable // Initialize the datatable
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#commperformance').DataTable({ manageTable = $('#commperformance').DataTable({
'ajax': 'reports/detail/fetchPerformances', 'ajax': 'reports/detail/fetchPerformances',
'order': [], 'order': [],

26
app/Views/demande/index.php

@ -67,6 +67,32 @@
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$("#remise_menu").addClass('active'); $("#remise_menu").addClass('active');
// Check if the URL contains the _ parameter // Check if the URL contains the _ parameter

8
app/Views/groups/create.php

@ -51,10 +51,10 @@
<label for="group_name">Désignation</label> <label for="group_name">Désignation</label>
<input type="text" class="form-control" id="group_name" name="group_name" placeholder="Enter group name"> <input type="text" class="form-control" id="group_name" name="group_name" placeholder="Enter group name">
</div> </div>
<div class="form-group"> <!-- <div class="form-group">
<label for="permission">Permission</label> <label for="permission">Permission</label> -->
<table class="table table-responsive"> <!-- <table class="table table-responsive">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
@ -228,7 +228,7 @@
<td>-</td> <td>-</td>
</tr> </tr>
</tbody> </tbody>
</table> </table> -->
</div> </div>

5
app/Views/groups/edit.php

@ -12,6 +12,11 @@
</ol> </ol>
</section> </section>
<!-- modification tandroany al -->
<?= $this->extend('layouts/adminlte') ?>
<?= $this->section('content') ?>
<!-- Main content --> <!-- Main content -->
<section class="content"> <section class="content">
<!-- Small boxes (Stat box) --> <!-- Small boxes (Stat box) -->

25
app/Views/groups/index.php

@ -92,6 +92,31 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function() { $(document).ready(function() {
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$('#groupTable').DataTable(); $('#groupTable').DataTable();
$("#mainUserNav").addClass('active'); $("#mainUserNav").addClass('active');

32
app/Views/mecanicien/index.php

@ -276,12 +276,36 @@
$("#mecanicNav").addClass('active'); $("#mecanicNav").addClass('active');
// initialize the datatable // initialize the datatable
const id = <?php echo json_encode($id); ?>; // datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': `<?= base_url('mecanicien/fetchMecanicienData') ?>`, 'ajax': `<?= base_url('mecanicien/fetchMecanicien') ?>`,
'order': [] 'order': []
}); });
// submit the create from // submit the create from
$("#createForm").unbind('submit').on('submit', function () { $("#createForm").unbind('submit').on('submit', function () {

27
app/Views/orders/avance.php

@ -76,7 +76,7 @@
<div class="modal-content"> <div class="modal-content">
<form id="create_avance_form" > <form id="create_avance_form" >
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Ajouter une avance</h4> <h4 class="modal-title">Ajouter une avance </h4>
<button type="button" class="close" data-dismiss="modal">&times;</button> <button type="button" class="close" data-dismiss="modal">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -175,6 +175,31 @@
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0; var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0;
$(document).ready(function() { $(document).ready(function() {
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$('avance_menu').addClass("active"); $('avance_menu').addClass("active");
$('.select2').select2(); $('.select2').select2();
manageTable = $('#avanceTable').DataTable({ manageTable = $('#avanceTable').DataTable({

4
app/Views/orders/create.php

@ -64,10 +64,10 @@
<div class="form-group"> <div class="form-group">
<label for="gross_amount" class="col-sm-12 control-label">Date: <?php echo date('Y-m-d') ?></label> <label for="gross_amount" class="col-sm-12 control-label"> Date : <?php echo date('Y-m-d') ?></label>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="gross_amount" class="col-sm-12 control-label">Heure: <?php echo date('h:i a') ?></label> <label for="gross_amount" class="col-sm-12 control-label"> Heure : <?php echo date('h:i a') ?></label>
</div> </div>
<div class="col-md-4 col-xs-12 pull pull-left"> <div class="col-md-4 col-xs-12 pull pull-left">

2
app/Views/orders/createbyid.php

@ -55,7 +55,7 @@
<?php echo date('Y-m-d') ?></label> <?php echo date('Y-m-d') ?></label>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="gross_amount" class="col-sm-12 control-label">Date: <label for="gross_amount" class="col-sm-12 control-label">Heure:
<?php echo date('h:i a') ?></label> <?php echo date('h:i a') ?></label>
</div> </div>

26
app/Views/orders/index.php

@ -213,6 +213,32 @@
$("#manageOrdersNav").addClass('active'); $("#manageOrdersNav").addClass('active');
// initialize the datatable // initialize the datatable
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': base_url + 'orders/fetchOrdersData', 'ajax': base_url + 'orders/fetchOrdersData',
'order': [], 'order': [],

25
app/Views/performance/index.php

@ -92,6 +92,31 @@
let manageTable; let manageTable;
$(document).ready(function () { $(document).ready(function () {
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$("#performance_menu").addClass('active'); $("#performance_menu").addClass('active');
function initDataTable(date = '', type = 'day') { function initDataTable(date = '', type = 'day') {

3
app/Views/products/create.php

@ -198,8 +198,9 @@
<div class="form-group"> <div class="form-group">
<label for="store">Etats</label> <label for="store">Etats</label>
<select class="form-control" id="etat" name="etats" required> <select class="form-control" id="etat" name="etats" required>
<option value="2" selected>Non Kit</option> <option value="" >selectionnez une Etat</option>
<option value="1">Kit</option> <option value="1">Kit</option>
<option value="2">Non kit</option>
</select> </select>
</div> </div>

62
app/Views/products/index.php

@ -282,15 +282,61 @@
$("#manageProductNav").addClass('active'); $("#manageProductNav").addClass('active');
// initialize the datatable // initialize the datatable
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': base_url + 'products/fetchProductData', 'ajax': base_url + 'products/fetchProductData',
'order': [], 'order': [],
'columnDefs': [{ 'columns': [
targets: 3, {
className: 'text-right' data: 0, // Colonne Image
} // Column index 3 corresponds to "Prix" render: function(data) {
] return data; // Affiche le HTML brut (déjà formaté en PHP)
}); },
orderable: false // Désactive le tri sur cette colonne
}, // SKU
{ data: 1 }, // Nom
{ data: 2 }, // Quantité
{
data: 3, // Prix
render: function(data, type, row) {
if (type === 'display') {
// Format: "1 900 000 Ar"
return new Intl.NumberFormat('fr-FR').format(data) + ' Ar';
}
return data; // Valeur non formatée pour le tri/filtre
}
},
{ data: 4 }, // Magasin
{ data: 5 }, // Disponibilité
{ data: 6 } // Actions
],
'columnDefs': [{
targets: 3,
className: 'text-right'
}]
});
}); });

25
app/Views/recouvrement/index.php

@ -278,6 +278,31 @@
if (window.location.search.startsWith("?_=")) { if (window.location.search.startsWith("?_=")) {
window.location.href = window.location.origin + window.location.pathname; window.location.href = window.location.origin + window.location.pathname;
} }
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': '<?= base_url('recouvrement/fetchRecouvrementData') ?>', 'ajax': '<?= base_url('recouvrement/fetchRecouvrementData') ?>',

25
app/Views/recouvrement/recouvrement.php

@ -183,6 +183,31 @@
// list of recouvrement // list of recouvrement
// initialize the datatable // initialize the datatable
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#recouvrement_table').DataTable({ manageTable = $('#recouvrement_table').DataTable({
'ajax': base_url + 'recouvrement/loadData', 'ajax': base_url + 'recouvrement/loadData',
'recouvrement': [] 'recouvrement': []

1
app/Views/reports/performance.php

@ -199,6 +199,7 @@
$("#reportNav").addClass('active'); $("#reportNav").addClass('active');
// Initialize the datatable // Initialize the datatable
manageTable = $('#commperformance').DataTable({ manageTable = $('#commperformance').DataTable({
'ajax': 'fetchPerformances', 'ajax': 'fetchPerformances',
'order': [], 'order': [],

2
app/Views/reports/stockDetail.php

@ -187,6 +187,8 @@
$("#reportNav").addClass('active'); $("#reportNav").addClass('active');
// initialize the datatable // initialize the datatable
manageTable = $('#venteTable').DataTable({ manageTable = $('#venteTable').DataTable({
'ajax': 'fetctData/' + 0, 'ajax': 'fetctData/' + 0,
'order': [], 'order': [],

1
app/Views/reports/venteStore.php

@ -183,6 +183,7 @@
$("#reportNav").addClass('active'); $("#reportNav").addClass('active');
// Initialize the datatable // Initialize the datatable
manageTable = $('#commperformance').DataTable({ manageTable = $('#commperformance').DataTable({
'ajax': 'fetchPerformances', 'ajax': 'fetchPerformances',
'order': [], 'order': [],

24
app/Views/securite/index.php

@ -75,7 +75,29 @@
$(function() { $(function() {
$("#securiteNav").addClass('active'); $("#securiteNav").addClass('active');
// ===== Initialisation DataTable ===== // ===== Initialisation DataTable =====$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$('#manageTable').DataTable({ $('#manageTable').DataTable({
ajax: { ajax: {
url: '<?= base_url('validateSecurite/fetchSecuriteData') ?>', url: '<?= base_url('validateSecurite/fetchSecuriteData') ?>',

52
app/Views/sortieCaisse/index.php

@ -342,7 +342,6 @@
<!-- Modal for validating a recouvrement --> <!-- Modal for validating a recouvrement -->
<?php if (in_array('validateSortieCaisse', $user_permission)): ?> <?php if (in_array('validateSortieCaisse', $user_permission)): ?>
<!-- update brand modal -->
<div class="modal fade" tabindex="-1" role="dialog" id="validateModal"> <div class="modal fade" tabindex="-1" role="dialog" id="validateModal">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@ -353,7 +352,6 @@
<form role="form" action="<?php echo base_url('sortieCaisse/validateSortieCaisse') ?>" method="post" id="validate_form"> <form role="form" action="<?php echo base_url('sortieCaisse/validateSortieCaisse') ?>" method="post" id="validate_form">
<div class="modal-body"> <div class="modal-body">
<div class="row form-group"> <div class="row form-group">
<div class="col-lg-6"> <div class="col-lg-6">
<label for="validation" class="control-label">statut :</label> <label for="validation" class="control-label">statut :</label>
@ -361,38 +359,60 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> <div class="form-group">
<label for="statut">Raison de validation</label> <label for="admin_raison">Raison de validation</label>
<input type="text" id="admin_raison"> <input type="text" class="form-control" id="admin_raison" name="admin_raison">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="statut">Statut du décaissement</label> <label for="statut">Statut du décaissement</label>
<sname="statut" id="statut" class="form-control"> <select name="statut" id="statut" class="form-control">
<option value="En attente" selected>En attente</option>
<option value="Valider">✔ Valider</option> <option value="Valider">✔ Valider</option>
<option value="En attente" selected> En attente</option>
<option value="Refuser">✖ Refuser</option> <option value="Refuser">✖ Refuser</option>
</select> </select>
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<div class="form-group"> <button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button> <button type="submit" class="btn btn-primary">Enregistrer</button>
<button type="submit" class="btn btn-primary">Enregistrer</button>
</div>
</div> </div>
</form> </form>
</div>
</div><!-- /.modal-content --> </div>
</div><!-- /.modal-dialog --> </div>
</div><!-- /.modal --> <?php endif; ?>
<?php endif; ?>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
$("#sortie_caisse_menu").addClass('active'); $("#sortie_caisse_menu").addClass('active');
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
ajax: '<?= base_url('sortieCaisse/fetchSortieCaisseData') ?>', ajax: '<?= base_url('sortieCaisse/fetchSortieCaisseData') ?>',

25
app/Views/stores/index.php

@ -188,11 +188,36 @@
$("#storeNav").addClass('active'); $("#storeNav").addClass('active');
// initialize the datatable // initialize the datatable
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': '<?= base_url('stores/fetchStoresData') ?>', 'ajax': '<?= base_url('stores/fetchStoresData') ?>',
'order': [] 'order': []
}); });
// submit the create from // submit the create from
$("#createForm").unbind('submit').on('submit', function() { $("#createForm").unbind('submit').on('submit', function() {
var form = $(this); var form = $(this);

49
app/Views/templates/side_menubar.php

@ -1,3 +1,4 @@
<aside class="main-sidebar"> <aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less --> <!-- sidebar: style can be found in sidebar.less -->
@ -73,17 +74,39 @@
</li> </li>
<?php endif; ?> --> <?php endif; ?> -->
<!--debut espace commerciale -->
<?php if (in_array('viewCom', $user_permission) || in_array('updateCom', $user_permission)): ?> <?php if (in_array('viewCom', $user_permission) || in_array('updateCom', $user_permission)): ?>
<li id="espaceMainMenu"> <li class="treeview" id="espaceMainMenu">
<a href="<?php echo base_url('/ventes') ?>"> <a href="#">
<i class="fa fa-shopping-cart"></i> <span> Espace commercial</span> <i class="fa fa-shopping-cart"></i>
</a> <span>Espace commercial</span>
</li> <span class="pull-right-container">
<?php endif; ?> <i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li><a href="<?php echo base_url('/ventes') ?>"><i class="fa fa-circle"></i> liste des produits disponibles</a></li>
<?php if (in_array('createOrder', $user_permission) || in_array('updateOrder', $user_permission) || in_array('viewOrder', $user_permission) || in_array('deleteOrder', $user_permission)): ?>
<li><a href="<?php echo base_url('orders') ?>"><i class="fa fa-circle"></i> Commandes</a></li>
<?php endif; ?>
<?php if (in_array('viewAvance', $user_permission)): ?>
<li><a href="<?php echo base_url('avances/') ?>"><i class="fa fa-circle"></i> Avances</a></li>
<?php endif; ?>
</ul>
</li>
<?php endif; ?>
<!-- rapport statistique -->
<!-- fin espace commerciale -->
<?php if (in_array('validateSecurite', $user_permission) || in_array('viewSecurite', $user_permission)): ?> <?php if (in_array('validateSecurite', $user_permission) || in_array('viewSecurite', $user_permission)): ?>
<li id="securiteNav"> <li id="securiteNav">
<a href="<?php echo base_url('/validateSecurite') ?>"> <a href="<?php echo base_url('/validateSecurite') ?>">
<i class="fa fa-lock"></i> <span> Espace Securite</span> <i class="fa fa-lock"></i> <span> Espace Securite</span>
</a> </a>
</li> </li>
@ -106,7 +129,7 @@
<?php if (in_array('createMecanicien', $user_permission) || in_array('updateMecanicien', $user_permission) || in_array('viewMecanicien', $user_permission) || in_array('deleteMecanicien', $user_permission)): ?> <?php if (in_array('createMecanicien', $user_permission) || in_array('updateMecanicien', $user_permission) || in_array('viewMecanicien', $user_permission) || in_array('deleteMecanicien', $user_permission)): ?>
<li id="mecanicNav"> <li id="mecanicNav">
<a href="<?php echo base_url('mecanicien/') ?>"> <a href="<?php echo base_url('mecanicien/') ?>">
<i class="fa fa-cog"></i> <span>Mécanicien</span> <i class="fa fa-cog"></i> <span>Réparations</span>
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>
@ -191,7 +214,7 @@
<?php endif; ?> <?php endif; ?>
<?php if (in_array('createOrder', $user_permission) || in_array('updateOrder', $user_permission) || in_array('viewOrder', $user_permission) || in_array('deleteOrder', $user_permission)): ?> <!-- <?php if (in_array('createOrder', $user_permission) || in_array('updateOrder', $user_permission) || in_array('viewOrder', $user_permission) || in_array('deleteOrder', $user_permission)): ?>
<li class="treeview" id="mainOrdersNav"> <li class="treeview" id="mainOrdersNav">
<a href="#"> <a href="#">
<i class="fa fa-dollar"></i> <i class="fa fa-dollar"></i>
@ -201,15 +224,15 @@
</span> </span>
</a> </a>
<ul class="treeview-menu"> <ul class="treeview-menu">
<!-- <?php if (in_array('createOrder', $user_permission)): ?> <?php if (in_array('createOrder', $user_permission)): ?>
<li id="addOrderNav"><a href="<?php echo base_url('orders/create') ?>"><i class="fa fa-circle"></i> Nouveau Commande</a></li> <li id="addOrderNav"><a href="<?php echo base_url('orders/create') ?>"><i class="fa fa-circle"></i> Nouveau Commande</a></li>
<?php endif; ?> --> <?php endif; ?>
<?php if (in_array('updateOrder', $user_permission) || in_array('viewOrder', $user_permission) || in_array('deleteOrder', $user_permission)): ?> <?php if (in_array('updateOrder', $user_permission) || in_array('viewOrder', $user_permission) || in_array('deleteOrder', $user_permission)): ?>
<li id="manageOrdersNav"><a href="<?php echo base_url('orders') ?>"><i class="fa fa-circle"></i> Gestion de Commande</a></li> <li id="manageOrdersNav"><a href="<?php echo base_url('orders') ?>"><i class="fa fa-circle"></i> Gestion de Commande</a></li>
<?php endif; ?> <?php endif; ?>
</ul> </ul>
</li> </li>
<?php endif; ?> <?php endif; ?> -->
<?php if (in_array('viewReports', $user_permission)): ?> <?php if (in_array('viewReports', $user_permission)): ?>
<li id="reportNav"> <li id="reportNav">
@ -219,13 +242,13 @@
</li> </li>
<?php endif; ?> <?php endif; ?>
<?php if (in_array('viewAvance', $user_permission)): ?> <!-- <?php if (in_array('viewAvance', $user_permission)): ?>
<li id="avance_menu"> <li id="avance_menu">
<a href="<?php echo base_url('avances/') ?>"> <a href="<?php echo base_url('avances/') ?>">
<i class="fas fa-hand-holding-dollar"></i> <span>Avances</span> <i class="fas fa-hand-holding-dollar"></i> <span>Avances</span>
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?> -->
<?php if (in_array('updateCompany', $user_permission)): ?> <?php if (in_array('updateCompany', $user_permission)): ?>
<li id="companyNav"><a href="<?php echo base_url('company/') ?>"><i class="fa fa-building"></i> <span>Entreprise</span></a></li> <li id="companyNav"><a href="<?php echo base_url('company/') ?>"><i class="fa fa-building"></i> <span>Entreprise</span></a></li>

28
app/Views/users/index.php

@ -51,8 +51,8 @@
<th>Prenom</th> <th>Prenom</th>
<th>Email</th> <th>Email</th>
<th>Phone</th> <th>Phone</th>
<th>Role</th>
<th>Point de vente</th> <th>Point de vente</th>
<th>Role</th>
<?php if (in_array('updateUser', $user_permission) || in_array('deleteUser', $user_permission)): ?> <?php if (in_array('updateUser', $user_permission) || in_array('deleteUser', $user_permission)): ?>
<th>Action</th> <th>Action</th>
<?php endif; ?> <?php endif; ?>
@ -262,6 +262,32 @@
} }
// Initialisation de DataTable // Initialisation de DataTable
// datatable-fr.js
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
manageTable = $('#manageTable').DataTable({ manageTable = $('#manageTable').DataTable({
'ajax': { 'ajax': {
url: '<?= base_url('users/fetchUserData') ?>', url: '<?= base_url('users/fetchUserData') ?>',

1
awstats-icon

@ -1 +0,0 @@
icon

1
awstats-icon

@ -0,0 +1 @@
icon

1
awstatsicons

@ -1 +0,0 @@
icon

1
awstatsicons

@ -0,0 +1 @@
icon

9
composer.json

@ -10,10 +10,11 @@
"ext-intl": "*", "ext-intl": "*",
"ext-json": "*", "ext-json": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"kint-php/kint": "^4.2",
"laminas/laminas-escaper": "^2.9", "laminas/laminas-escaper": "^2.9",
"psr/log": "^1.1", "psr/log": "^1.1",
"firebase/php-jwt": "^6.11" "firebase/php-jwt": "^6.11",
"kint-php/kint": "5.0",
"phpoffice/phpspreadsheet": "^5.0"
}, },
"require-dev": { "require-dev": {
"codeigniter/coding-standard": "^1.5", "codeigniter/coding-standard": "^1.5",
@ -52,9 +53,7 @@
] ]
}, },
"scripts": { "scripts": {
"post-update-cmd": [
"CodeIgniter\\ComposerScripts::postUpdate"
],
"test": "phpunit" "test": "phpunit"
}, },
"support": { "support": {

1
icon/mime/conf.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/conf.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/csv.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/csv.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/document.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/document.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/dtd.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/dtd.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/flv.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/flv.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/fon.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/fon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/package.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/package.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/runtime.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/runtime.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/swf.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/swf.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/vbs.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/vbs.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

1
icon/mime/xsl.png

@ -1 +0,0 @@
notavailable.png

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
icon/mime/xsl.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 B

After

Width:  |  Height:  |  Size: 88 B

BIN
public/assets/bower_components/jquery-ui/themes/humanity/images/animated-overlay.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 0 B

BIN
public/assets/bower_components/jquery-ui/themes/humanity/images/ui-bg_glass_100_f5f0e5_1x400.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 0 B

BIN
public/assets/images/product_image/686cdaac4dad4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
public/assets/images/product_image/686cdaac5f43e.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
public/assets/images/product_image/686cdaac72635.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

BIN
public/assets/images/product_image/6894a44cd6b5f.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/assets/images/product_image/689ca160619a9.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

BIN
public/assets/images/product_image/689eba65cf5a8.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
public/assets/images/product_image/689ebcb98f420.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
public/assets/images/product_image/689ebceb0d802.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
public/assets/images/product_image/689ebd3b59c5b.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

1
writable/cache/check_deadline_last_run.txt

@ -0,0 +1 @@
1755601005

1
writable/debugbar/debugbar_1751886377.352834.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886387.359498.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886393.226242.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886396.580956.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886397.779369.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886406.431574.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886416.347097.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886426.829140.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886438.478916.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886439.130326.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886443.397712.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886445.279811.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886453.171091.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886454.621281.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886456.946542.json

File diff suppressed because one or more lines are too long

1
writable/debugbar/debugbar_1751886458.223834.json

File diff suppressed because one or more lines are too long

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save