Compare commits

..

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

346 changed files with 1463 additions and 25507 deletions

2
.vscode/sftp.json vendored
View File

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

View File

@ -1,53 +1,121 @@
<?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 = 'rey342505@gmail.com'; public string $fromEmail = '';
public string $fromName = 'motorbike'; public string $fromName = '';
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 $SMTPHost = '';
public string $SMTPUser = 'rey342505@gmail.com'; /**
* SMTP Username
*/
public string $SMTPUser = '';
public string $SMTPPass = 'loirqovmfuxnasrm'; // Mot de passe dapplication (App Password) Gmail /**
* SMTP Password
*/
public string $SMTPPass = '';
public int $SMTPPort = 587; /**
* SMTP Port
*/
public int $SMTPPort = 25;
public int $SMTPTimeout = 30; /**
* 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;
} }

View File

@ -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,10 +41,8 @@ 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

View File

@ -30,14 +30,6 @@ 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
@ -61,8 +53,6 @@ $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
@ -252,7 +242,6 @@ $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');
}); });

View File

@ -1,14 +0,0 @@
<?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.";
}
}

View File

@ -6,7 +6,6 @@ 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
{ {
@ -29,49 +28,36 @@ class AvanceController extends AdminController
return $this->render_template('avances/avance', $data); return $this->render_template('avances/avance', $data);
} }
private function isAdmin($user) public function fetchAvanceData()
{
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)
{ {
helper(['url', 'form']);
$Avance = new Avance();
$product = new Products();
$result = ['data' => []];
$data = $Avance->getAllAvanceData();
$session = session();
$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'])); $date_time = date('d-m-Y h:i a', strtotime($value['avance_date']));
// Boutons daction
$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) { if ($isAdmin) {
return [ $row = [
$value['customer_name'], $value['customer_name'],
$value['customer_phone'], $value['customer_phone'],
$value['customer_address'], $value['customer_address'],
@ -82,43 +68,17 @@ class AvanceController extends AdminController
$date_time, $date_time,
$buttons, $buttons,
]; ];
} elseif ($isCommerciale || $isCaissier) { // dd($row);die;
return [ $result['data'][] = $row;
}
if ($isCommerciale || $isCaissier) {
$row = [
$value['avance_id'], $value['avance_id'],
$product->getProductNameById($value['product_id']),
number_format((int)$value['avance_amount'], 0, ',', ' '), number_format((int)$value['avance_amount'], 0, ',', ' '),
number_format((int)$value['amount_due'], 0, ',', ' '), number_format((int)$value['amount_due'], 0, ',', ' '),
$date_time, $date_time,
$buttons, $buttons,
]; ];
}
return [];
}
private function fetchAvanceDataGeneric($methodName = 'getAllAvanceData')
{
helper(['url', 'form']);
$Avance = new Avance();
$product = new Products();
$result = ['data' => []];
$data = $Avance->$methodName();
$session = session();
$users = $session->get('user');
$isAdmin = $this->isAdmin($users);
$isCommerciale = $this->isCommerciale($users);
$isCaissier = $this->isCaissier($users);
foreach ($data as $key => $value) {
$isOwner = $users['id'] === $value['user_id'];
$buttons = $this->buildActionButtons($value, $isAdmin, $isOwner);
$row = $this->buildDataRow($value, $product, $isAdmin, $isCommerciale, $isCaissier, $buttons);
if (!empty($row)) {
$result['data'][] = $row; $result['data'][] = $row;
} }
} }
@ -126,537 +86,296 @@ class AvanceController extends AdminController
return $this->response->setJSON($result); 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']);
}
}
}
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;
}
} catch (\Exception $e) {
log_message('error', "Erreur envoi email alerte: " . $e->getMessage());
return false;
}
}
/**
* Récupérer les emails du DAF et de la Directrice
*/
private function getDAFAndDirectriceEmails($store_id)
{
$User = new User();
$emails = [];
// 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'];
}
}
foreach ($directionUsers as $user) {
if (!empty($user['email'])) {
$emails[] = $user['email'];
}
}
// 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
}
/**
* 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 :</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;
if ($this->request->getMethod() !== 'post') { $Avance = new Avance();
return $this->response->setJSON([ $Products = new Products();
'success' => false, $Notification = New NotificationController();
'messages' => 'Méthode non autorisée'
]);
}
try { if ($this->request->getMethod() === 'post') {
$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' => $avance_date, 'avance_date' => date('Y-m-d'),
'deadline' => $deadline,
'user_id' => $users['id'], 'user_id' => $users['id'],
'store_id' => $users['store_id'], 'store_id' => $users['store_id'],
'product_id' => (int)$this->request->getPost('id_product'), 'product_id' => $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' => 0, 'is_order' => (float)0,
'active' => 1, 'active' => 1,
]; ];
if ($avance_id = $Avance->createAvance($data)) { if($avance_id = $Avance->createAvance($data)){
$Products->update($data['product_id'], ['product_sold' => 1]); $product = new Products();
$product->update((int)$this->request->getPost('id_product'), ['product_sold' => 1]);
$Notification->createNotification( $Notification->createNotification('Une avance a été créé', "Conseil",(int)$users['store_id'], 'avances');
'Une nouvelle avance a été créée',
"Conseil",
(int)$users['store_id'],
'avances'
);
return $this->response->setJSON([ return $this->response->setJSON([
'success' => true, 'success' => true,
'messages' => 'Avance créée avec succès !', 'messages' => 'Avance créé 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) { else{
log_message('error', "Erreur création avance: " . $e->getMessage());
return $this->response->setJSON([ return $this->response->setJSON([
'success' => false, 'success' => false,
'messages' => 'Une erreur interne est survenue' 'messages' => 'Une erreur est survenue lors de la création d\une avance !'
]); ]);
} }
} }
}
public function updateAvance(int $id) public function updateAvance(int $id)
{ {
$this->verifyRole('updateAvance'); $this->verifyRole('updateAvance');
$data['page_title'] = $this->pageTitle;
if ($this->request->getMethod() !== 'post') { $Products = new Products();
return $this->response->setJSON([ $Avance = new Avance();
'success' => false,
'messages' => 'Méthode non autorisée'
]);
}
try {
$session = session(); $session = session();
$users = $session->get('user'); $users = $session->get('user');
if ($this->request->getMethod() === 'post') {
$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 = [
'type_avance' => $type_avance,
'customer_name' => $this->request->getPost('customer_name_avance_edit'), 'customer_name' => $this->request->getPost('customer_name_avance_edit'),
'customer_address' => $this->request->getPost('customer_address_avance_edit'), 'customer_address'=> $this->request->getPost('customer_address_avance_edit'),
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'), 'customer_phone' => $this->request->getPost('customer_phone_avance_edit'),
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'), 'customer_cin' => $this->request->getPost('customer_cin_avance_edit'),
'gross_amount' => (float)$this->request->getPost('gross_amount_edit'), 'gross_amout' => $this->request->getPost('gros_amount_edit'),
'avance_amount' => (float)$this->request->getPost('avance_amount_edit'), 'avance_amount' => (int)$this->request->getPost('avance_amount_edit'),
'amount_due' => (float)$this->request->getPost('amount_due_edit'), 'amount_due' => (int)$this->request->getPost('amount_due_edit'),
'product_id' => (int)$this->request->getPost('id_product_edit'), 'product_id' => $this->request->getPost('id_product_edit'),
'deadline' => $deadline,
]; ];
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4));
$amount_due = $data['amount_due']; $Company = new Company();
$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) {
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4)); $Orders = new Orders();
$company = $Company->getCompanyData(1);
$service_charge_rate = $company['service_charge_value'] ?? 0; $data = [
$vat_charge_rate = $company['vat_charge_value'] ?? 0;
$gross_amount = $data['gross_amount'];
$vat_charge = ($gross_amount / 100) * $vat_charge_rate;
$order_data = [
'bill_no' => $bill_no, 'bill_no' => $bill_no,
'customer_name' => $data['customer_name'], 'customer_name' => $this->request->getPost('customer_name_avance_edit'),
'customer_address' => $data['customer_address'], 'customer_address'=> $this->request->getPost('customer_address_avance_edit'),
'customer_phone' => $data['customer_phone'], 'customer_phone' => $this->request->getPost('customer_phone_avance_edit'),
'customer_cin' => $data['customer_cin'], 'customer_cin' => $this->request->getPost('customer_cin_avance_edit'),
'gross_amount' => $gross_amount, 'gross_amout' => $gross_amount,
'net_amount' => $gross_amount, 'net_amount' => $gross_amount,
'date_time' => date('Y-m-d H:i:s'), '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' => 0, 'discount' => (int) 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];
$product_id = [$data['product_id']]; if($Orders->create($data,$product_id)){
$Avance->updateAvance($id,$data1);
if ($Orders->create($order_data, $product_id)) { $Notification = New NotificationController();
$Avance->updateAvance($id, ['is_order' => 1]); $Notification->createNotification('Une commande a été créé', "Conseil",(int)$users['store_id'], 'orders');
$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' => 'Avance convertie en commande avec succès.' 'messages' => 'success. Avance convertie en commande avec succès.'
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de la conversion de l\'avance en commande'
]); ]);
} }
} else { else{
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de la convertion de l\'avance'
]);
}
}
else{
if ($Avance->updateAvance($id, $data)) { if ($Avance->updateAvance($id, $data)) {
return $this->response->setJSON([ return $this->response->setJSON([
'success' => true, 'success' => true,
'messages' => 'Avance mise à jour avec succès.' 'messages' => 'success', 'Avance mise à jour avec succès.'
]); ]);
} else { } else {
return $this->response->setJSON([ return $this->response->setJSON([
'success' => false, 'success' => true,
'messages' => 'Erreur lors de la mise à jour de l\'avance.' 'messages' => 'Errors', 'Une erreur est survenue lors de la mise à jour.'
]); ]);
} }
} }
} 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');
try {
$avance_id = $this->request->getPost('avance_id'); $avance_id = $this->request->getPost('avance_id');
$product_id = $this->request->getPost('product_id'); $product_id = $this->request->getPost('product_id');
$response = [];
if (!$avance_id || !$product_id) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Données manquantes pour la suppression'
]);
}
$Avance = new Avance(); $Avance = new Avance();
$Products = new Products();
if ($Avance->removeAvance($avance_id)) { if ($Avance->removeAvance($avance_id)) {
$Products->update($product_id, ['product_sold' => 0]); $product = new Products();
$product->update($product_id, ['product_sold' => 0]);
return $this->response->setJSON([ $response['success'] = true;
'success' => true, $response['messages'] = "Avance supprimée avec succès. Ce produit peut désormais être réservé à nouveau.";
'messages' => "Avance supprimée avec succès. Le produit peut être réservé à nouveau."
]);
} else { } else {
return $this->response->setJSON([ $response['success'] = false;
'success' => false, $response['messages'] = "une erreur est survenue lors de la suppression d'une avance";
'messages' => "Erreur lors de la suppression de l'avance"
]);
}
} 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($response);
} }
public function fetchSingleAvance($avance_id) public function fetchSingleAvance($avance_id)
{ {
$this->verifyRole('updateAvance'); $this->verifyRole('updateAvance');
try { try {
if (!$avance_id || !is_numeric($avance_id)) {
return $this->response->setStatusCode(400)->setJSON([
'error' => 'ID d\'avance invalide'
]);
}
$avanceModel = new Avance(); $avanceModel = new Avance();
$data = $avanceModel->fetchSingleAvance($avance_id); $data = $avanceModel->fetchSingleAvance($avance_id);
if (!$data) {
return $this->response->setStatusCode(404)->setJSON([
'error' => 'Avance non trouvée'
]);
}
return $this->response->setJSON($data); 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'
]);
} }
catch (\Throwable $th) {
log_message('error', "Erreur lors de la récupération d'une avance: " . $th->getMessage());
return $this->response
->setStatusCode(500)
->setJSON(['error' => 'Une erreur interne est survenue. Lors de la création d\'une avance']);
} }
} }
public function fetchAvanceBecameOrder()
{
helper(['url', 'form']);
$Avance = new Avance();
$product = new Products();
$result = ['data' => []];
$data = $Avance->getAllAvanceData1();
$session = session();
$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 daction
$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;
}
}
return $this->response->setJSON($result);
}
public function fetcheExpiredAvance()
{
helper(['url', 'form']);
$Avance = new Avance();
$product = new Products();
$result = ['data' => []];
$data = $Avance->getAllAvanceData2();
$session = session();
$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 daction
$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;
}
}
return $this->response->setJSON($result);
}
}

View File

@ -1,22 +1,58 @@
<?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, /**
\CodeIgniter\HTTP\ResponseInterface $response, * Be sure to declare properties for any property fetch you initialized.
\Psr\Log\LoggerInterface $logger) * The creation of dynamic property is deprecated in PHP 8.2.
*/
// 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);
helper('alerts'); // Preload any models, libraries, etc, here.
if (function_exists('checkDeadlineAlerts')) { // E.g.: $this->session = \Config\Services::session();
checkDeadlineAlerts();
}
} }
} }

View File

@ -74,6 +74,7 @@ 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();
@ -82,23 +83,15 @@ class ProductCOntroller extends AdminController
{ {
return "$name"; return "$name";
} }
// Fetch product data from the model
$data = $Products->getProductData(); $data = $Products->getProductData(); // Ensure this method exists in your ProductModel
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
// Gestion du nom du magasin // Fetch store data
if ($value['store_id'] == 0) { $store_data = $Stores->getStoresData($value['store_id']); // Ensure this method exists in your StoreModel
$store_name = "TOUS"; $store_data['name'] = $value['store_id'] == 0 ? "TOUS" : $Stores->getStoresData($value['store_id'])["name"];
} else { // Construct buttons
$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>';
@ -119,35 +112,44 @@ class ProductCOntroller extends AdminController
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_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_data['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'] ?
'<img src="' . base_url($imagePath) . '" width="50" height="50" class="img-thumbnail">' :
'<div class="no-image">Aucune image</div>';
// Préparer les données pour DataTables (7 colonnes)
$result['data'][$key] = [ // Image HTML
$value['image'], $img = '<img src="' . base_url('assets/images/product_image/' . $value['image']) . '" alt="' . $value['name'] . '" class="img-circle" width="50" height="50" />';
convertString($value['sku']),
// Availability Status
$availability = ($value['availability'] == 1) ? '<span class="label label-success">Disponible</span>' : '<span class="label label-warning">Indisponible</span>';
// Quantity Status
$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'],
$value['price'], number_format($value['prix_vente'], 0, ',', ' '),
$store_name, $store_data['name'] ?? 'Unknown Store',
$availability, // <-- ici la disponibilité ajoutée $availability,
$buttons $buttons
]; ];
} }
// Return JSON response
return $this->response->setJSON($result); return $this->response->setJSON($result);
} }
public function create() public function create()
{ {
$Products = new Products(); $Products = new Products();
@ -354,147 +356,233 @@ 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");
try { // 1) Récupération et validation du fichier
$file = $this->request->getFile('excel_product'); $file = $this->request->getFile('excel_product');
if (!$file || !$file->isValid()) { if (!$file || !$file->isValid() || $file->hasMoved()) {
return $this->response->setJSON([ return $this->response->setJSON([
'success' => false, 'success' => false,
'messages' => "Fichier invalide ou non reçu" 'messages' => "Aucun fichier valide reçu"
]); ]);
} }
$ext = strtolower($file->getClientExtension()); $ext = strtolower($file->getClientExtension());
if (!in_array($ext, ['xls', 'xlsx'])) { if (! in_array($ext, ['xls', 'xlsx'])) {
return $this->response->setJSON([ return $this->response->setJSON([
'success' => false, 'success' => false,
'messages' => "Seuls les fichiers Excel (.xls, .xlsx) sont acceptés" 'messages' => "Seuls les fichiers xls/xlsx sont autorisés"
]); ]);
} }
$spreadsheet = IOFactory::load($file->getTempName()); try {
// 2) Chargement du fichier Excel
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file->getTempName());
$sheet = $spreadsheet->getActiveSheet(); $sheet = $spreadsheet->getActiveSheet();
$rows = $sheet->toArray();
if (count($rows) <= 1) { // 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([ return $this->response->setJSON([
'success' => false, 'success' => false,
'messages' => "Le fichier ne contient pas de données" '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, '_');
// Récupérer les en-têtes switch ($h) {
$headers = array_shift($rows); case 'designation':
$headers = array_map('strtolower', $headers); 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;
}
}
// Mapping des colonnes Excel vers les champs de la base // 4) Extraction des images intégrées, si présent
$columnMapping = [ $imagesMap = [];
'n° série' => 'sku', foreach ($sheet->getDrawingCollection() as $drawing) {
'marque' => 'marque', if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
'désignation' => 'name', $coord = $drawing->getCoordinates();
'fournisseur' => 'info', // À adapter selon votre besoin $extImg = pathinfo($drawing->getPath(), PATHINFO_EXTENSION);
'date d\'arrivage' => 'date_arivage', $name = uniqid('img_') . ".$extImg";
'n° moteur' => 'numero_de_moteur', $dir = FCPATH . 'assets/images/product_image/';
'châssis' => 'chasis', if (! is_dir($dir)) {
'puissance' => 'puissance', mkdir($dir, 0777, true);
'clé' => 'cler', }
'prix d\'achat' => 'prix_vente', file_put_contents($dir . $name, file_get_contents($drawing->getPath()));
'prix ar' => 'price', $imagesMap[$coord] = $name;
'catégories' => 'categorie_id', }
'magasin' => 'store_id', }
'disponibilité' => 'availability',
'état' => 'etats',
'pièce manquant' => 'infoManque'
];
$ProductsModel = new Products();
$BrandsModel = new Brands();
$StoresModel = new Stores();
$CategoryModel = new Category();
// 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; $countInserted = 0;
foreach ($rows as $row) { // 6) Boucle sur chaque ligne de données
if (empty(array_filter($row))) continue; // Ignore les lignes vides foreach ($allRows as $rowIndex => $row) {
$data = [];
$data = [ // Lecture des cellules formatées pour chaque champ mappé
'is_piece' => 0, foreach ($map as $col => $field) {
'product_sold' => 0, $cellValue = $sheet
'qty' => 1 ->getCell($col . ($rowIndex + 2))
]; ->getFormattedValue();
$data[$field] = trim((string)$cellValue);
}
// Mapper chaque colonne if (empty($data['name'])) {
foreach ($headers as $index => $header) { continue; // champ désignation vide
$header = trim($header); }
if (isset($columnMapping[$header]) && isset($row[$index])) {
$field = $columnMapping[$header];
$value = trim($row[$index]);
// Traitements spécifiques pour certains champs // Conversion du prix AR : capture tous les groupes de chiffres
switch ($field) { if (! empty($data['prix_vente'])) {
case 'marque': preg_match_all('/\d+/', $data['prix_vente'], $matches);
// Chercher ou créer la marque $digits = implode('', $matches[0]); // ex. ["2","000","000"] => "2000000"
$brand = $BrandsModel->where('name', $value)->first(); $data['prix_vente'] = intval($digits);
if (!$brand) {
$brandId = $BrandsModel->insert(['name' => $value, 'active' => 1]);
} else { } else {
$brandId = $brand['id']; $data['prix_vente'] = 0;
} }
$data[$field] = $brandId;
break;
case 'store_id': // Valeurs par défaut
// Gestion du magasin $data['qty'] = 1;
if ($value == 'TOUS') { $data['product_sold'] = 0;
$data[$field] = 0; $data['availability'] = isset($data['availability'])
} else { ? (strtolower($data['availability']) === 'oui' ? 1 : 0)
$store = $StoresModel->where('name', $value)->first(); : 0;
$data[$field] = $store ? $store['id'] : 0; $data['is_piece'] = isset($data['is_piece'])
} ? (strtolower($data['is_piece']) === 'oui' ? 1 : 0)
break; : 0;
$data['cler'] = isset($data['cler'])
? (strtolower($data['cler']) === 'oui' ? 1 : 0)
: 1;
$data['etats'] = isset($data['etats'])
? (strtolower($data['etats']) === 'kit' ? 1 : 0)
: 1;
case 'date_arivage': // Association dimage si présente
// Convertir la date Excel en format MySQL foreach ($map as $col => $field) {
if (is_numeric($value)) { if ($field === 'image') {
$data[$field] = date('Y-m-d', \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($value)); $coordImg = $col . ($rowIndex + 2);
} else { if (isset($imagesMap[$coordImg])) {
$data[$field] = date('Y-m-d', strtotime($value)); $data['image'] = $imagesMap[$coordImg];
} }
break; break;
case 'price': }
// Nettoyer "1 900 000 Ar" → 1900000.00 }
$cleanedValue = str_replace(['Ar', ' ', ','], '', $value);
$data[$field] = (float)$cleanedValue;
break;
default: // Gestion des clés étrangères
$data[$field] = $value; if (! empty($data['marque'])) {
$data['marque'] = $BrandsModel->getOrCreateIdByName($data['marque']);
}
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 // Insertion
if (!empty($data['name'])) { $id = $ProductsModel->insert($data);
if ($ProductsModel->insert($data)) { if ($id !== false) {
$countInserted++; $countInserted++;
} }
} }
}
// 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([ return $this->response->setJSON([
'success' => true, 'success' => true,
'messages' => "$countInserted produits importés avec succès" 'messages' => "Produits importés avec succès ($countInserted)"
]); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', $e->getMessage());
return $this->response->setJSON([ return $this->response->setJSON([
'success' => false, 'success' => false,
'messages' => "Erreur lors de l'import: " . $e->getMessage() 'messages' => "Erreur pendant limport : " . $e->getMessage()
]); ]);
} }
} }
} }

View File

@ -29,28 +29,32 @@ class ReportController extends AdminController
$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 (pie chart) // //data for the camembert
$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;
@ -64,6 +68,7 @@ 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 = [];
@ -74,49 +79,46 @@ class ReportController extends AdminController
} }
} }
$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 camembert in dashboard // //data for the camember 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

View File

@ -1,25 +0,0 @@
<?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é.";
}
}

View File

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

View File

@ -1,145 +0,0 @@
<!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>

View File

@ -1,176 +0,0 @@
<?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 :</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;
}
}

View File

@ -1,21 +0,0 @@
<?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 !
}

View File

@ -2,34 +2,23 @@
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_address', 'customer_phone', 'customer_cin', 'customer_name',
'gross_amount','amount_due','product_id','is_order','active','store_id', 'customer_address',
'type_avance', 'deadline' // Ajout du champ type et deadline 'customer_phone',
]; '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 {
// Si la date de création n'est pas définie, on prend aujourd'hui
if (empty($data['avance_date'])) {
$data['avance_date'] = date('Y-m-d');
}
// Calcul de la deadline en fonction du type
if (!empty($data['type'])) {
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->insert($data); return $this->insert($data);
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de l\'ajout de l\'avance : ' . $e->getMessage()); log_message('error', 'Erreur lors de l\'ajout de l\'avance : ' . $e->getMessage());
@ -38,19 +27,14 @@ class Avance extends Model {
} }
public function updateAvance(int $id, array $data) { public function updateAvance(int $id, array $data) {
if ($id <= 0) { if ($id <= 0) {
log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id); log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id);
return false; return false;
} }
try { 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); return $this->update($id, $data);
} catch (\Exception $e) { } catch (\Exception $e) {
log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage()); log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage());
@ -58,7 +42,6 @@ class Avance extends Model {
} }
} }
// 📌 Le reste de tes fonctions restent inchangées
public function getAllAvanceData(int $id=null) { public function getAllAvanceData(int $id=null) {
$session = session(); $session = session();
$users = $session->get('user'); $users = $session->get('user');
@ -80,13 +63,13 @@ class Avance extends Model {
return $this return $this
->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) { } 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 { }
else{
if($id){ if($id){
try { try {
return $this->where('user_id',$id) return $this->where('user_id',$id)
@ -105,17 +88,18 @@ class Avance extends Model {
->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) { } 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;
} }
} }
} }
public function fetchSingleAvance(int $avance_id){ public function fetchSingleAvance(int $avance_id){
return $this->where('avance_id',$avance_id)->first(); return $this->where('avance_id',$avance_id)
->first();
} }
public function removeAvance(int $avance_id){ public function removeAvance(int $avance_id){
@ -128,7 +112,10 @@ 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('SUM(avance_amount) AS ta') return $this->select('
SUM(avance_amount) AS ta,
')
->where('is_order', 0) ->where('is_order', 0)
->get() ->get()
->getRowObject(); ->getRowObject();
@ -136,9 +123,13 @@ class Avance extends Model {
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('SUM(avance_amount) AS ta') return $this->select('
SUM(avance_amount) AS ta,
')
->where('is_order', 0) ->where('is_order', 0)
->where('store_id',$users['store_id']) ->where('store_id',$users['store_id'])
->get() ->get()
@ -148,6 +139,7 @@ class Avance extends Model {
return false; return false;
} }
} }
} }
public function getAllAvanceData1(int $id=null) { public function getAllAvanceData1(int $id=null) {
@ -171,13 +163,13 @@ class Avance extends Model {
return $this return $this
->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) { } 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 { }
else{
if($id){ if($id){
try { try {
return $this->where('user_id',$id) return $this->where('user_id',$id)
@ -194,8 +186,7 @@ class Avance extends Model {
try { try {
return $this return $this
->where('is_order',0) ->where('is_order',0)
->orderBy('avance_date', 'DESC') ->orderBy('avance_date', 'DESC') ->findAll();
->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;
@ -224,13 +215,13 @@ class Avance extends Model {
return $this return $this
->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) { } 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 { }
else{
if($id){ if($id){
try { try {
return $this->where('user_id',$id) return $this->where('user_id',$id)
@ -249,8 +240,7 @@ class Avance extends Model {
->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) { } 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;
@ -258,35 +248,23 @@ class Avance extends Model {
} }
} }
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('deadline <', $now) ->where('DATE_ADD(avance_date, INTERVAL 15 DAY) <', $now)
->findAll(); ->findAll();
if (!empty($avances)) { if (!empty($avances)) {
$productModel = new Products(); $productModel = new Products();
foreach ($avances as $avance) { foreach ($avances as $avance) {
$this->update($avance['avance_id'], ['active' => '0']); // Mettre l'avance à expirée
$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();
} }
}

View File

@ -173,46 +173,27 @@
$(document).ready(function() { $(document).ready(function() {
$("#attributeNav").addClass('active'); $("#attributeNav").addClass('active');
// initialisation de la DataTable en français // initialize the datatable
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"
}
}
}); });
// soumission du formulaire de création // submit the create from
$("#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(), data: form.serialize(), // /converting the form data into array and sending it to server
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
@ -220,15 +201,20 @@
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="Fermer"><span aria-hidden="true">&times;</span></button>' + '<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 + '<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);
@ -243,8 +229,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="Fermer"><span aria-hidden="true">&times;</span></button>' + '<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 + '<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
'</div>'); '</div>');
} }
} }
@ -256,24 +242,31 @@
}); });
// fonction de modification // edit function
// 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(), data: form.serialize(), // /converting the form data into array and sending it to server
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
@ -281,14 +274,18 @@
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="Fermer"><span aria-hidden="true">&times;</span></button>' + '<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 + '<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);
@ -303,8 +300,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="Fermer"><span aria-hidden="true">&times;</span></button>' + '<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 + '<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
'</div>'); '</div>');
} }
} }
@ -318,18 +315,22 @@
}); });
} }
// fonction de suppression // remove functions
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: { attribute_value_id: id }, data: {
attribute_value_id: id
},
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
@ -337,16 +338,18 @@
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="Fermer"><span aria-hidden="true">&times;</span></button>' + '<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 + '<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="Fermer"><span aria-hidden="true">&times;</span></button>' + '<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 + '<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
'</div>'); '</div>');
} }
} }

View File

@ -96,29 +96,11 @@
<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>
@ -148,7 +130,7 @@
<!-- 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' : '' ?>>
@ -200,16 +182,6 @@
</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>
@ -277,21 +249,16 @@
</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"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Supprimer cette avance</h4> <h4 class="modal-title">Supprimer cette avance</h4>
</div> </div>
<form role="form" action="<?php echo base_url('avances/deleteAvance') ?>" method="post" id="removeForm"> <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"> <div class="modal-body">
<p>Voulez-vous vraiment supprimer ?</p> <p>Voulez-vous vraiment supprimer ?</p>
</div> </div>
@ -300,57 +267,22 @@
<button type="submit" class="btn btn-primary">Oui</button> <button type="submit" class="btn btn-primary">Oui</button>
</div> </div>
</form> </form>
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
<?php endif; ?> <?php endif; ?>
<script> <script>
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0; var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0;
$(document).ready(function() {
$(document).ready(function() {
$('#avance_menu').addClass("active"); $('#avance_menu').addClass("active");
$('.select2').select2(); $('.select2').select2();
<?php if ($isAdmin):?>
// 📌 Configuration langue FR manageTable = $('#avanceTable').DataTable({
var datatableLangFr = { ajax: 'fetchAvanceData',
lengthMenu: "Afficher _MENU_ enregistrements par page", columns: [
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"
}
};
// 📌 Fonction pour initialiser la DataTable
function initAvanceTable(url, columns) {
if ($.fn.DataTable.isDataTable('#avanceTable')) {
$('#avanceTable').DataTable().destroy();
}
return $('#avanceTable').DataTable({
ajax: url,
columns: columns,
language: datatableLangFr
});
}
// 🔄 FONCTION DE MISE À JOUR DYNAMIQUE DE LA DATATABLE
function refreshDataTable() {
if (typeof manageTable !== 'undefined' && manageTable) {
manageTable.ajax.reload(null, false); // Recharger seulement les données de la table
}
}
<?php if ($isAdmin): ?>
var adminColumns = [
{ title: "Client" }, { title: "Client" },
{ title: "Téléphone" }, { title: "Téléphone" },
{ title: "Adresse" }, { title: "Adresse" },
@ -362,25 +294,77 @@ $(document).ready(function() {
<?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; ?>
]; ]
});
var manageTable = initAvanceTable('fetchAvanceData', adminColumns);
$('#avance_order').on('click', function () { $('#avance_order').on('click', function () {
manageTable = initAvanceTable('fetchAvanceBecameOrder', adminColumns); $('#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 () { $('#avance_expired').on('click', function () {
manageTable = initAvanceTable('fetchExpiredAvance', 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_no_order').on('click', function () { $('#avance_no_order').on('click', function () {
manageTable = initAvanceTable('fetchAvanceData', adminColumns); $('#avanceTable').DataTable().destroy();
manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceData',
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; ?>
]
});
}); });
<?php endif; ?> <?php endif; ?>
<?php if ($isCaissier || $isCommerciale): ?> <?php if ($isCaissier || $isCommerciale):?>
var userColumns = [ manageTable = $('#avanceTable').DataTable({
ajax: 'fetchAvanceData',
columns: [
{ title: "#" }, { title: "#" },
{ title: "Produit" }, { title: "Produit" },
{ title: "Avance" }, { title: "Avance" },
@ -389,381 +373,238 @@ $(document).ready(function() {
<?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; ?>
]; ]
});
var manageTable = initAvanceTable('fetchAvanceData', userColumns);
$('#avance_order').on('click', function () { $('#avance_order').on('click', function () {
manageTable = initAvanceTable('fetchAvanceBecameOrder', userColumns); $('#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 () { $('#avance_no_order').on('click', function () {
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; ?> <?php endif; ?>
// ✅ CRÉATION avec actualisation automatique
// Création AJAX
$('#create_avance_form').on('submit', function(e) { $('#create_avance_form').on('submit', function(e) {
e.preventDefault(); e.preventDefault();
const $form = $(this); const $form = $(this);
var brut = parseFloat($('#gross_amount').val()) || 0;
var avance = parseFloat($('#avance_amount').val()) || 0;
var minAvance = brut * 0.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 de soumission
$form.find('button[type="submit"]').prop('disabled', true).text('Enregistrement...');
$.post('/avances/createAvance', $form.serialize(), function(res) { $.post('/avances/createAvance', $form.serialize(), function(res) {
if (res.success === true) { if (res.success === true) {
$("#messages").html(`
<div class="alert alert-success alert-dismissible" role="alert"> 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"> <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-ok-sign"></span></strong> ${res.messages}
</div> </div>`
`); );
// Cacher le modal de création
$("#createModal").modal('hide'); $("#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);
$form[0].reset();
} else { } else {
$("#createModal").modal('hide'); $("#createModal").modal('hide');
$("#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"> <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-exclamation-sign"></span></strong> ${res.messages} <strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${res.messages}
</div> </div>`
`); );
// Réactiver le bouton en cas d'erreur
$form.find('button[type="submit"]').prop('disabled', false).text('Enregistrer');
} }
}, 'json'); }, '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) { // Récupère prix pour création
function getProductDataCreate(){
var id = $('#id_product').val();
if(!id){ brutCreate=0; $('#gross_amount,#amount_due').val(''); return; }
else{
$.post(base_url+'orders/getProductValueById',{product_id:id}, function(r){
brutCreate = parseFloat(r.prix_vente)||0;
$('#gross_amount').val(brutCreate);
$('#amount_due').val(brutCreate);
updateDueCreate();
},'json');
}
}
function getProductDataUpdate(){
var id = $('#id_product_edit').val();
if(!id){ brutCreate=0; $('#gross_amount_edit,#amount_due_edit').val(''); return; }
else{
$.post(base_url+'orders/getProductValueById',{product_id:id}, function(r){
brutCreate = parseFloat(r.prix_vente)||0;
$('#gross_amount_edit').val(brutCreate);
$('#amount_due_edit').val(brutCreate);
updateDueCreate();
},'json');
}
}
function updateDueCreate(){
var av = parseFloat($('#avance_amount').val())||0;
var brutAmount = parseFloat($('#gross_amount').val())||0;
$('#amount_due').val(Math.max(brutAmount-av,0));
}
// Prépare et affiche modal édition
function editFunc(id) {
// Récupération des données de l'avance
$.getJSON(base_url + 'avances/fetchSingleAvance/' + id, function(r) {
// Préremplissage du modal
$('#customer_name_avance_edit').val(r.customer_name);
$('#customer_phone_avance_edit').val(r.customer_phone);
$('#customer_adress_avance_edit').val(r.customer_adress);
$('#customer_cin_avance_edit').val(r.customer_cin);
$('#gross_amount_edit').val(r.gross_amount);
$('#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(){
var av = parseFloat($('#avance_amount_edit').val())||0;
$('#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(); e.preventDefault();
var form = $(this); var form = $(this);
var submitButton = form.find('button[type="submit"]');
// Désactiver le bouton // Supprime les anciens messages d'erreur
submitButton.prop('disabled', true).text('Suppression...'); $(".text-danger").remove();
$.ajax({ $.ajax({
url: form.attr('action'), url: form.attr('action'),
type: form.attr('method'), type: form.attr('method'),
data: form.serialize(), data: {
avance_id: id,
product_id: product_id
},
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
manageTable.ajax.reload(null, false);
if (response.success === true) { 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>' +
$("#messages").html(` '<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
<div class="alert alert-success alert-dismissible" role="alert"> '</div>');
<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);
// Cache le modal
$("#removeModal").modal('hide');
} else { } else {
$('#removeModal').modal('hide'); $("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
$("#messages").html(` '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
<div class="alert alert-warning alert-dismissible" role="alert"> '<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> '</div>');
<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; return false;
}); });
// Réinitialiser le modal à la fermeture
$('#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();
if (!id) {
brutCreate = 0;
$('#gross_amount,#amount_due,#avance_amount').val('');
return;
} }
$.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 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();
console.log('getProductDataUpdate appelée avec ID:', id); // Pour déboguer
if (!id) {
brutEdit = 0;
$('#gross_amount_edit,#amount_due_edit,#avance_amount_edit').val('');
return;
}
$.ajax({
url: base_url + 'orders/getProductValueById',
type: 'POST',
data: { product_id: id },
dataType: 'json',
success: function(r) {
console.log('Données produit reçues:', r); // Pour déboguer
brutEdit = parseFloat(r.prix_vente) || 0;
$('#gross_amount_edit').val(brutEdit.toFixed(2));
var avance25 = brutEdit * 0.25;
$('#avance_amount_edit').val(avance25.toFixed(2));
$('#amount_due_edit').val((brutEdit - avance25).toFixed(2));
updateDueEdit();
},
error: function(xhr, status, error) {
console.log('Erreur lors de la récupération des données produit:', error);
}
});
}
function updateDueEdit() {
var av = parseFloat($('#avance_amount_edit').val()) || 0;
$('#amount_due_edit').val(Math.max(brutEdit - av, 0).toFixed(2));
}
// ✅ MODIFICATION avec mise à jour dynamique - CORRIGÉ
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>
`);
});
// Réinitialiser le modal à la fermeture
$('#updateModal').on('hidden.bs.modal', function() {
$('#update_avance_form')[0].reset();
$('#update_avance_form button[type="submit"]').prop('disabled', false).text('Modifier');
});
}
</script> </script>

View File

@ -189,31 +189,6 @@
$("#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': []

View File

@ -186,31 +186,6 @@
$("#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': []

View File

@ -14,28 +14,24 @@
<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'] ?? 'default.png')) ?>" <img src="<?= base_url('assets/images/product_image/' . $products['image']) ?>"
alt="<?= esc($products['name'] ?? '') ?>" alt="<?= $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"><?= esc($products['name'] ?? '') ?></h2> <h2 class="text-dark"><?= $products['name'] ?></h2>
<h4 class="text-success fw-bold"> <h4 class="text-success fw-bold"><?= number_format($products['price'], 0, ',', ' ') ?> MGA</h4>
<?= number_format((float) ($products['price'] ?? 0), 0, ',', ' ') ?> MGA <p class="text-muted">En stock: <strong><?= $products['qty'] ?></strong></p>
</h4> <p class="text-secondary"><?= $products['description'] ?></p>
<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'] ?? 0) ?>" enctype="multipart/form-data" method="post"> <form action="<?= base_url('ventes/moreimage/') . $products['id'] ?>" 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">
@ -51,22 +47,21 @@
</div> </div>
<h3>Galleries</h3> <h3>Galleries</h3>
<div class="row"> <div class="row">
<?php if (!empty($galleries)) : ?> <?php foreach ($galleries as $key => $value) { ?>
<?php foreach ($galleries as $key => $value) : ?>
<div class="col-md-3 col-xs-12 col-sm-6 mb-3" style="margin-bottom: 1%;"> <div class="col-md-3 col-xs-12 col-sm-6 mb-3" style="margin-bottom: 1%;">
<div class="card shadow-lg border-0 rounded"> <div class="card shadow-lg border-0 rounded">
<div class="card-header bg-success text-white text-center p-0.5"> <div class="card-header bg-success text-white text-center p-0.5">
<!-- Clickable Image --> <!-- Clickable Image -->
<a href="#" data-toggle="modal" data-target="#imageModal<?= $key ?>"> <a href="#" data-toggle="modal" data-target="#imageModal<?= $key ?>">
<img src="<?= base_url('assets/images/product_image/' . ($value['images'] ?? 'default.png')) ?>" <img src="<?= base_url('assets/images/product_image/' . $value['images']) ?>"
alt="<?= esc($value['images'] ?? '') ?>" alt="<?= $value['images'] ?>"
class="img-fluid img-thumbnail rounded" class="img-fluid img-thumbnail rounded"
style="width: 100%; height: 250px; object-fit: cover;"> style="width: 100%; height: 250px; object-fit: cover;">
</a> </a>
</div> </div>
<div class="card-footer" style="padding: 1%;"> <div class="card-footer" style="padding: 1%;">
<form action="<?= base_url('ventes/moreimage/supp/') . ($value['id'] ?? 0) ?>" method="post"> <form action="<?= base_url('ventes/moreimage/supp/') . $value['id'] ?>" method="post">
<button class="btn btn-danger">Supprimer</button> <button class="btn btn-danger">Supprimer</button>
</form> </form>
</div> </div>
@ -79,28 +74,23 @@
<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"> <button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"> <i class="fas fa-times"></i></button>
<i class="fas fa-times"></i>
</button>
</div> </div>
<div class="modal-body text-center" style="width: 100%;"> <div class="modal-body text-center" style="width: 100%;">
<img src="<?= base_url('assets/images/product_image/' . ($value['images'] ?? 'default.png')) ?>" <img src="<?= base_url('assets/images/product_image/' . $value['images']) ?>"
alt="<?= esc($value['images'] ?? '') ?>" alt="<?= $value['images'] ?>"
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>
</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>

View File

@ -40,31 +40,6 @@
</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); ?>;

View File

@ -32,21 +32,7 @@
<p class="text-secondary">Moteur <?= $products['numero_de_moteur'] ?></p> <p class="text-secondary">Moteur <?= $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"> <p class="text-secondary"> <b>Kit</b> <br><?= $products['etats'] == 1 ? $products['infoManquekit'] : 'Non Kit' ?></p>
<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">

View File

@ -1,3 +1,4 @@
<!-- Content Wrapper. Contains page content -->
<style> <style>
.card { .card {
border-radius: 12px; border-radius: 12px;
@ -146,7 +147,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 FLUX</p> <p>Totale CAISSE</p>
</div> </div>
<div class="icon"> <div class="icon">
<i class="fa fa-credit-card"></i> <i class="fa fa-credit-card"></i>
@ -380,29 +381,6 @@
$(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': [],

View File

@ -67,32 +67,6 @@
<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

View File

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

View File

@ -12,11 +12,6 @@
</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) -->

View File

@ -92,31 +92,6 @@
<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');

View File

@ -276,36 +276,12 @@
$("#mecanicNav").addClass('active'); $("#mecanicNav").addClass('active');
// initialize the datatable // initialize the datatable
// datatable-fr.js const id = <?php echo json_encode($id); ?>;
$.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/fetchMecanicien') ?>`, 'ajax': `<?= base_url('mecanicien/fetchMecanicienData') ?>`,
'order': [] 'order': []
}); });
// submit the create from // submit the create from
$("#createForm").unbind('submit').on('submit', function () { $("#createForm").unbind('submit').on('submit', function () {

View File

@ -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,31 +175,6 @@
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({

View File

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

View File

@ -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">Heure: <label for="gross_amount" class="col-sm-12 control-label">Date:
<?php echo date('h:i a') ?></label> <?php echo date('h:i a') ?></label>
</div> </div>

View File

@ -213,32 +213,6 @@
$("#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': [],

View File

@ -92,31 +92,6 @@
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') {

View File

@ -198,9 +198,8 @@
<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="" >selectionnez une Etat</option> <option value="2" selected>Non Kit</option>
<option value="1">Kit</option> <option value="1">Kit</option>
<option value="2">Non kit</option>
</select> </select>
</div> </div>

View File

@ -282,61 +282,15 @@
$("#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': [],
'columns': [
{
data: 0, // Colonne Image
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': [{ 'columnDefs': [{
targets: 3, targets: 3,
className: 'text-right' className: 'text-right'
}] } // Column index 3 corresponds to "Prix"
}); ]
});
}); });

View File

@ -278,31 +278,6 @@
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') ?>',

View File

@ -183,31 +183,6 @@
// 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': []

View File

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

View File

@ -187,8 +187,6 @@
$("#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': [],

View File

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

View File

@ -75,29 +75,7 @@
$(function() { $(function() {
$("#securiteNav").addClass('active'); $("#securiteNav").addClass('active');
// ===== Initialisation DataTable =====$.extend(true, $.fn.dataTable.defaults, { // ===== Initialisation DataTable =====
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') ?>',

View File

@ -342,6 +342,7 @@
<!-- 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">
@ -352,6 +353,7 @@
<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>
@ -359,14 +361,14 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> <div class="form-group">
<label for="admin_raison">Raison de validation</label> <label for="statut">Raison de validation</label>
<input type="text" class="form-control" id="admin_raison" name="admin_raison"> <input type="text" id="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>
<select name="statut" id="statut" class="form-control"> <sname="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>
@ -374,45 +376,23 @@
</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> </div><!-- /.modal-content -->
</div> </div><!-- /.modal-dialog -->
<?php endif; ?> </div><!-- /.modal -->
<?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') ?>',

View File

@ -188,36 +188,11 @@
$("#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);

View File

@ -1,4 +1,3 @@
<aside class="main-sidebar"> <aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less --> <!-- sidebar: style can be found in sidebar.less -->
@ -74,39 +73,17 @@
</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 class="treeview" id="espaceMainMenu"> <li id="espaceMainMenu">
<a href="#"> <a href="<?php echo base_url('/ventes') ?>">
<i class="fa fa-shopping-cart"></i> <i class="fa fa-shopping-cart"></i> <span> Espace commercial</span>
<span>Espace commercial</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a> </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> </li>
<?php endif; ?> <?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>
@ -129,7 +106,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>Réparations</span> <i class="fa fa-cog"></i> <span>Mécanicien</span>
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>
@ -214,7 +191,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>
@ -224,15 +201,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">
@ -242,13 +219,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>

View File

@ -51,8 +51,8 @@
<th>Prenom</th> <th>Prenom</th>
<th>Email</th> <th>Email</th>
<th>Phone</th> <th>Phone</th>
<th>Point de vente</th>
<th>Role</th> <th>Role</th>
<th>Point de vente</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,32 +262,6 @@
} }
// 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') ?>',

View File

@ -1 +0,0 @@
icon

1
awstats-icon Symbolic link
View File

@ -0,0 +1 @@
icon

View File

@ -1 +0,0 @@
icon

1
awstatsicons Symbolic link
View File

@ -0,0 +1 @@
icon

View File

@ -10,11 +10,10 @@
"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",
@ -53,7 +52,9 @@
] ]
}, },
"scripts": { "scripts": {
"post-update-cmd": [
"CodeIgniter\\ComposerScripts::postUpdate"
],
"test": "phpunit" "test": "phpunit"
}, },
"support": { "support": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/conf.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/csv.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/document.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/dtd.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/flv.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/fon.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/package.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/runtime.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/swf.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/vbs.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

1
icon/mime/xsl.png Symbolic link
View File

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

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 16 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

View File

@ -1 +0,0 @@
1755601005

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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