Compare commits
No commits in common. "17803c1acb38e9fd8d87e1aa840f3a2a5158b227" and "0ce9cb608d1a236dc7238b2e096547fc7c624372" have entirely different histories.
17803c1acb
...
0ce9cb608d
2
.vscode/sftp.json
vendored
@ -6,7 +6,7 @@
|
||||
"username": "motorbike",
|
||||
"remotePath": "/home/motorbike/public_html/",
|
||||
"password": "IVrMDogT3XiBcrY",
|
||||
"uploadOnSave": true,
|
||||
"uploadOnSave": false,
|
||||
"useTempFile": false,
|
||||
"openSsh": false
|
||||
}
|
||||
|
||||
@ -1,121 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Email extends BaseConfig
|
||||
{
|
||||
public string $fromEmail = '';
|
||||
public string $fromName = '';
|
||||
public string $fromEmail = 'rey342505@gmail.com';
|
||||
public string $fromName = 'motorbike';
|
||||
public string $recipients = '';
|
||||
|
||||
/**
|
||||
* The "user agent"
|
||||
*/
|
||||
public string $userAgent = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The mail sending protocol: mail, sendmail, smtp
|
||||
*/
|
||||
public string $protocol = 'mail';
|
||||
public string $protocol = 'smtp';
|
||||
|
||||
/**
|
||||
* The server path to Sendmail.
|
||||
*/
|
||||
public string $mailPath = '/usr/sbin/sendmail';
|
||||
|
||||
/**
|
||||
* SMTP Server Hostname
|
||||
*/
|
||||
public string $SMTPHost = '';
|
||||
public string $SMTPHost = 'smtp.gmail.com';
|
||||
|
||||
/**
|
||||
* SMTP Username
|
||||
*/
|
||||
public string $SMTPUser = '';
|
||||
public string $SMTPUser = 'rey342505@gmail.com';
|
||||
|
||||
/**
|
||||
* SMTP Password
|
||||
*/
|
||||
public string $SMTPPass = '';
|
||||
public string $SMTPPass = 'loirqovmfuxnasrm'; // Mot de passe d’application (App Password) Gmail
|
||||
|
||||
/**
|
||||
* SMTP Port
|
||||
*/
|
||||
public int $SMTPPort = 25;
|
||||
public int $SMTPPort = 587;
|
||||
|
||||
/**
|
||||
* SMTP Timeout (in seconds)
|
||||
*/
|
||||
public int $SMTPTimeout = 5;
|
||||
public int $SMTPTimeout = 30;
|
||||
|
||||
/**
|
||||
* Enable persistent SMTP connections
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
* Enable word-wrap
|
||||
*/
|
||||
public bool $wordWrap = true;
|
||||
|
||||
/**
|
||||
* Character count to wrap at
|
||||
*/
|
||||
public int $wrapChars = 76;
|
||||
|
||||
/**
|
||||
* Type of mail, either 'text' or 'html'
|
||||
*/
|
||||
public string $mailType = 'text';
|
||||
public string $mailType = 'html';
|
||||
|
||||
/**
|
||||
* Character set (utf-8, iso-8859-1, etc.)
|
||||
*/
|
||||
public string $charset = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Whether to validate the email address
|
||||
*/
|
||||
public bool $validate = false;
|
||||
public bool $validate = true;
|
||||
|
||||
/**
|
||||
* Email Priority. 1 = highest. 5 = lowest. 3 = normal
|
||||
*/
|
||||
public int $priority = 3;
|
||||
|
||||
/**
|
||||
* Newline character. (Use “\r\n” to comply with RFC 822)
|
||||
*/
|
||||
public string $CRLF = "\r\n";
|
||||
|
||||
/**
|
||||
* Newline character. (Use “\r\n” to comply with RFC 822)
|
||||
*/
|
||||
public string $newline = "\r\n";
|
||||
|
||||
/**
|
||||
* Enable BCC Batch Mode.
|
||||
*/
|
||||
public bool $BCCBatchMode = false;
|
||||
|
||||
/**
|
||||
* Number of emails in each BCC batch
|
||||
*/
|
||||
public int $BCCBatchSize = 200;
|
||||
|
||||
/**
|
||||
* Enable notify message from server
|
||||
*/
|
||||
public bool $DSN = false;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Kint\Parser\ConstructablePluginInterface;
|
||||
use Kint\Renderer\AbstractRenderer;
|
||||
//use Kint\Renderer\AbstractRenderer;
|
||||
use Kint\Renderer\Rich\TabPluginInterface;
|
||||
use Kint\Renderer\Rich\ValuePluginInterface;
|
||||
|
||||
@ -41,8 +41,10 @@ class Kint extends BaseConfig
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public string $richTheme = 'aante-light.css';
|
||||
public $richSort = null;
|
||||
|
||||
public bool $richFolder = false;
|
||||
public int $richSort = AbstractRenderer::SORT_FULL;
|
||||
//public int $richSort = AbstractRenderer::SORT_FULL;
|
||||
|
||||
/**
|
||||
* @var array<string, class-string<ValuePluginInterface>>|null
|
||||
|
||||
@ -30,6 +30,14 @@ use App\Controllers\PerformanceController;
|
||||
*/
|
||||
$routes->get('/login', [Auth::class, 'login'], ['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
|
||||
@ -53,6 +61,8 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
|
||||
* route to 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
|
||||
@ -242,6 +252,7 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
|
||||
$routes->post('delete', [MecanicienController::class, 'delete']);
|
||||
$routes->post('update/(:num)', [MecanicienController::class, 'update']);
|
||||
$routes->get('fetchMecanicienPerformances', [MecanicienController::class, 'fetchMecanicienPerformances']);
|
||||
|
||||
// $routes->put('update/(:num)', 'MecanicienController::update/$1');
|
||||
});
|
||||
|
||||
|
||||
14
app/Controllers/AlertsController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
class AlertsController extends BaseController
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
helper('alerts');
|
||||
checkDeadlineAlerts();
|
||||
return "Vérification des alertes effectuée.";
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ use App\Models\Company;
|
||||
use App\Models\Orders;
|
||||
use App\Models\Products;
|
||||
use App\Models\Avance;
|
||||
use App\Models\User; // Ajout pour récupérer les emails DAF/Directrice
|
||||
|
||||
class AvanceController extends AdminController
|
||||
{
|
||||
@ -28,57 +29,96 @@ class AvanceController extends AdminController
|
||||
return $this->render_template('avances/avance', $data);
|
||||
}
|
||||
|
||||
public function fetchAvanceData()
|
||||
private function isAdmin($user)
|
||||
{
|
||||
return in_array($user['group_name'], ['Conseil', 'Direction']);
|
||||
}
|
||||
|
||||
private function isCommerciale($user)
|
||||
{
|
||||
return in_array($user['group_name'], ['COMMERCIALE']);
|
||||
}
|
||||
|
||||
private function isCaissier($user)
|
||||
{
|
||||
return in_array($user['group_name'], ['Caissier']);
|
||||
}
|
||||
|
||||
private function buildActionButtons($value, $isAdmin, $isOwner)
|
||||
{
|
||||
$buttons = '';
|
||||
|
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc(' . $value['avance_id'] . ')" title="Modifier">'
|
||||
. '<i class="fa fa-pencil"></i></button> ';
|
||||
}
|
||||
|
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')" title="Supprimer">'
|
||||
. '<i class="fa fa-trash"></i></button> ';
|
||||
}
|
||||
|
||||
if (in_array('viewAvance', $this->permission) && !$isAdmin) {
|
||||
$buttons .= '<a href="#" data-order-id="' . $value['avance_id'] . '" class="btn btn-default btn-view" title="Voir">'
|
||||
. '<i class="fa fa-eye"></i></a>';
|
||||
}
|
||||
|
||||
return $buttons;
|
||||
}
|
||||
|
||||
private function buildDataRow($value, $product, $isAdmin, $isCommerciale, $isCaissier, $buttons)
|
||||
{
|
||||
$date_time = date('d-m-Y h:i a', strtotime($value['avance_date']));
|
||||
|
||||
if ($isAdmin) {
|
||||
return [
|
||||
$value['customer_name'],
|
||||
$value['customer_phone'],
|
||||
$value['customer_address'],
|
||||
$product->getProductNameById($value['product_id']),
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '),
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
} elseif ($isCommerciale || $isCaissier) {
|
||||
return [
|
||||
$value['avance_id'],
|
||||
$product->getProductNameById($value['product_id']),
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '),
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function fetchAvanceDataGeneric($methodName = 'getAllAvanceData')
|
||||
{
|
||||
helper(['url', 'form']);
|
||||
$Avance = new Avance();
|
||||
$product = new Products();
|
||||
$result = ['data' => []];
|
||||
$data = $Avance->getAllAvanceData();
|
||||
|
||||
$data = $Avance->$methodName();
|
||||
$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']));
|
||||
$users = $session->get('user');
|
||||
|
||||
$isAdmin = $this->isAdmin($users);
|
||||
$isCommerciale = $this->isCommerciale($users);
|
||||
$isCaissier = $this->isCaissier($users);
|
||||
|
||||
// Boutons d’action
|
||||
$buttons = '';
|
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc('. $value['avance_id'] .')">'
|
||||
. '<i class="fa fa-pencil"></i></button>';
|
||||
}
|
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')"><i class="fa fa-trash"></i></button>';
|
||||
}
|
||||
if (in_array('viewAvance', $this->permission) && !$isAdmin) {
|
||||
$buttons .= ' <a href="#" data-order-id="'.$value['id'].'" class="btn btn-default btn-view" title="Voir"><i class="fa fa-eye"></i></a>';
|
||||
}
|
||||
if ($isAdmin) {
|
||||
$row = [
|
||||
$value['customer_name'],
|
||||
$value['customer_phone'],
|
||||
$value['customer_address'],
|
||||
$product->getProductNameById($value['product_id']),
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '),
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
// dd($row);die;
|
||||
$result['data'][] = $row;
|
||||
}
|
||||
if ($isCommerciale || $isCaissier) {
|
||||
$row = [
|
||||
$value['avance_id'],
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '),
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -86,296 +126,537 @@ class AvanceController extends AdminController
|
||||
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 dû :</strong> <span class='urgent'>{$amountDueFormatted}</span></li>
|
||||
<li><strong>Date avance :</strong> {$avanceDateFormatted}</li>
|
||||
<li><strong>Date limite :</strong> <span class='urgent'>{$deadlineFormatted}</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class='alert'>
|
||||
<p><strong>Action requise :</strong></p>
|
||||
<p>Veuillez contacter le client pour régulariser le paiement avant l'échéance ou prendre les mesures appropriées.</p>
|
||||
</div>
|
||||
|
||||
<div class='footer'>
|
||||
<p>Cet email a été généré automatiquement par le système de gestion des avances.</p>
|
||||
<p>Date d'envoi : " . date('d/m/Y à H:i') . "</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifier si un email a déjà été envoyé pour cette avance
|
||||
*/
|
||||
private function hasEmailBeenSent($avance_id)
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
$query = $db->query("SELECT id FROM email_alerts WHERE avance_id = ? AND alert_type = 'deadline_3days'", [$avance_id]);
|
||||
return $query->getNumRows() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marquer l'email comme envoyé
|
||||
*/
|
||||
private function markEmailAsSent($avance_id)
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
$data = [
|
||||
'avance_id' => $avance_id,
|
||||
'alert_type' => 'deadline_3days',
|
||||
'sent_date' => date('Y-m-d H:i:s'),
|
||||
'status' => 'sent'
|
||||
];
|
||||
|
||||
$db->table('email_alerts')->insert($data);
|
||||
}
|
||||
|
||||
public function createAvance()
|
||||
{
|
||||
// $this->verifyRole('createAvance');
|
||||
$data['page_title'] = $this->pageTitle;
|
||||
$this->verifyRole('createAvance');
|
||||
|
||||
$Avance = new Avance();
|
||||
$Products = new Products();
|
||||
$Notification = New NotificationController();
|
||||
if ($this->request->getMethod() !== 'post') {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Méthode non autorisée'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->request->getMethod() === 'post') {
|
||||
try {
|
||||
$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 = [
|
||||
'type_avance' => $type_avance,
|
||||
'customer_name' => $this->request->getPost('customer_name_avance'),
|
||||
'customer_address' => $this->request->getPost('customer_address_avance'),
|
||||
'customer_phone' => $this->request->getPost('customer_phone_avance'),
|
||||
'customer_cin' => $this->request->getPost('customer_cin_avance'),
|
||||
'avance_date' => date('Y-m-d'),
|
||||
'user_id' => $users['id'],
|
||||
'avance_date' => $avance_date,
|
||||
'deadline' => $deadline,
|
||||
'user_id' => $users['id'],
|
||||
'store_id' => $users['store_id'],
|
||||
'product_id' => $this->request->getPost('id_product'),
|
||||
'gross_amount' => (float)$this->request->getPost('gross_amount'),
|
||||
'product_id' => (int)$this->request->getPost('id_product'),
|
||||
'gross_amount' => (float)$this->request->getPost('gross_amount'),
|
||||
'avance_amount' => (float)$this->request->getPost('avance_amount'),
|
||||
'amount_due' => (float)$this->request->getPost('amount_due'),
|
||||
'is_order' => (float)0,
|
||||
'active' => 1,
|
||||
'is_order' => 0,
|
||||
'active' => 1,
|
||||
];
|
||||
|
||||
if($avance_id = $Avance->createAvance($data)){
|
||||
$product = new Products();
|
||||
$product->update((int)$this->request->getPost('id_product'), ['product_sold' => 1]);
|
||||
$Notification->createNotification('Une avance a été créé', "Conseil",(int)$users['store_id'], 'avances');
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'Avance créé avec succès !'
|
||||
]);
|
||||
}
|
||||
else{
|
||||
if ($avance_id = $Avance->createAvance($data)) {
|
||||
$Products->update($data['product_id'], ['product_sold' => 1]);
|
||||
|
||||
$Notification->createNotification(
|
||||
'Une nouvelle avance a été créée',
|
||||
"Conseil",
|
||||
(int)$users['store_id'],
|
||||
'avances'
|
||||
);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'Avance créée avec succès !',
|
||||
'avance_id' => $avance_id
|
||||
]);
|
||||
} else {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Erreur lors de la création de l\'avance'
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', "Erreur création avance: " . $e->getMessage());
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Une erreur est survenue lors de la création d\une avance !'
|
||||
'messages' => 'Une erreur interne est survenue'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updateAvance(int $id)
|
||||
{
|
||||
$this->verifyRole('updateAvance');
|
||||
$data['page_title'] = $this->pageTitle;
|
||||
|
||||
$Products = new Products();
|
||||
$Avance = new Avance();
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
if ($this->request->getMethod() === 'post') {
|
||||
$data = [
|
||||
'customer_name' => $this->request->getPost('customer_name_avance_edit'),
|
||||
'customer_address'=> $this->request->getPost('customer_address_avance_edit'),
|
||||
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'),
|
||||
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'),
|
||||
'gross_amout' => $this->request->getPost('gros_amount_edit'),
|
||||
'avance_amount' => (int)$this->request->getPost('avance_amount_edit'),
|
||||
'amount_due' => (int)$this->request->getPost('amount_due_edit'),
|
||||
'product_id' => $this->request->getPost('id_product_edit'),
|
||||
];
|
||||
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4));
|
||||
$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 ($this->request->getMethod() !== 'post') {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Méthode non autorisée'
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
|
||||
if ($amount_due <= 0) {
|
||||
$Orders = new Orders();
|
||||
$Avance = new Avance();
|
||||
$Products = new Products();
|
||||
$Orders = new Orders();
|
||||
$Company = new Company();
|
||||
$Notification = new NotificationController();
|
||||
|
||||
$data = [
|
||||
'bill_no' => $bill_no,
|
||||
'customer_name' => $this->request->getPost('customer_name_avance_edit'),
|
||||
'customer_address'=> $this->request->getPost('customer_address_avance_edit'),
|
||||
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'),
|
||||
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'),
|
||||
'gross_amout' => $gross_amount,
|
||||
'net_amount' => $gross_amount,
|
||||
'date_time' => date('Y-m-d H:i:s'),
|
||||
'service_charge_rate' => $service_charge_rate,
|
||||
'vat_charge_rate' => $vat_charge_rate,
|
||||
'vat_charge' => $vat_charge,
|
||||
'discount' => (int) 0,
|
||||
'paid_status' => 1,
|
||||
'user_id' => $users['id'],
|
||||
'store_id' => $users['store_id'],
|
||||
'amount_value' => $gross_amount,
|
||||
'rate_value' => $gross_amount,
|
||||
];
|
||||
$data1 = ['is_order' => 1];
|
||||
if($Orders->create($data,$product_id)){
|
||||
$Avance->updateAvance($id,$data1);
|
||||
$Notification = New NotificationController();
|
||||
$Notification->createNotification('Une commande a été créé', "Conseil",(int)$users['store_id'], 'orders');
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'success. Avance convertie en commande avec succès.'
|
||||
]);
|
||||
}
|
||||
else{
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Erreur lors de la convertion de l\'avance'
|
||||
]);
|
||||
}
|
||||
$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())
|
||||
]);
|
||||
}
|
||||
else{
|
||||
if ($Avance->updateAvance($id, $data)) {
|
||||
|
||||
// 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 = [
|
||||
'type_avance' => $type_avance,
|
||||
'customer_name' => $this->request->getPost('customer_name_avance_edit'),
|
||||
'customer_address' => $this->request->getPost('customer_address_avance_edit'),
|
||||
'customer_phone' => $this->request->getPost('customer_phone_avance_edit'),
|
||||
'customer_cin' => $this->request->getPost('customer_cin_avance_edit'),
|
||||
'gross_amount' => (float)$this->request->getPost('gross_amount_edit'),
|
||||
'avance_amount' => (float)$this->request->getPost('avance_amount_edit'),
|
||||
'amount_due' => (float)$this->request->getPost('amount_due_edit'),
|
||||
'product_id' => (int)$this->request->getPost('id_product_edit'),
|
||||
'deadline' => $deadline,
|
||||
];
|
||||
|
||||
$amount_due = $data['amount_due'];
|
||||
|
||||
if ($amount_due <= 0) {
|
||||
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4));
|
||||
$company = $Company->getCompanyData(1);
|
||||
|
||||
$service_charge_rate = $company['service_charge_value'] ?? 0;
|
||||
$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,
|
||||
'customer_name' => $data['customer_name'],
|
||||
'customer_address' => $data['customer_address'],
|
||||
'customer_phone' => $data['customer_phone'],
|
||||
'customer_cin' => $data['customer_cin'],
|
||||
'gross_amount' => $gross_amount,
|
||||
'net_amount' => $gross_amount,
|
||||
'date_time' => date('Y-m-d H:i:s'),
|
||||
'service_charge_rate' => $service_charge_rate,
|
||||
'vat_charge_rate' => $vat_charge_rate,
|
||||
'vat_charge' => $vat_charge,
|
||||
'discount' => 0,
|
||||
'paid_status' => 1,
|
||||
'user_id' => $users['id'],
|
||||
'store_id' => $users['store_id'],
|
||||
'amount_value' => $gross_amount,
|
||||
'rate_value' => $gross_amount,
|
||||
];
|
||||
|
||||
$product_id = [$data['product_id']];
|
||||
|
||||
if ($Orders->create($order_data, $product_id)) {
|
||||
$Avance->updateAvance($id, ['is_order' => 1]);
|
||||
$Notification->createNotification(
|
||||
'Une avance a été convertie en commande',
|
||||
"Conseil",
|
||||
(int)$users['store_id'],
|
||||
'orders'
|
||||
);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'success', 'Avance mise à jour avec succès.'
|
||||
|
||||
'messages' => 'Avance convertie en commande avec succès.'
|
||||
]);
|
||||
} else {
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'Errors', 'Une erreur est survenue lors de la mise à jour.'
|
||||
'success' => false,
|
||||
'messages' => 'Erreur lors de la conversion de l\'avance en commande'
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($Avance->updateAvance($id, $data)) {
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'Avance mise à jour avec succès.'
|
||||
]);
|
||||
} else {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Erreur lors de la mise à jour de l\'avance.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', "Erreur mise à jour avance: " . $e->getMessage());
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Une erreur interne est survenue'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAvance()
|
||||
{
|
||||
$this->verifyRole('deleteAvance');
|
||||
$avance_id = $this->request->getPost('avance_id');
|
||||
$product_id = $this->request->getPost('product_id');
|
||||
$response = [];
|
||||
|
||||
try {
|
||||
$avance_id = $this->request->getPost('avance_id');
|
||||
$product_id = $this->request->getPost('product_id');
|
||||
|
||||
$Avance = new Avance();
|
||||
if ($Avance->removeAvance($avance_id)) {
|
||||
$product = new Products();
|
||||
$product->update($product_id, ['product_sold' => 0]);
|
||||
$response['success'] = true;
|
||||
$response['messages'] = "Avance supprimée avec succès. Ce produit peut désormais être réservé à nouveau.";
|
||||
} else {
|
||||
$response['success'] = false;
|
||||
$response['messages'] = "une erreur est survenue lors de la suppression d'une avance";
|
||||
if (!$avance_id || !$product_id) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => 'Données manquantes pour la suppression'
|
||||
]);
|
||||
}
|
||||
|
||||
$Avance = new Avance();
|
||||
$Products = new Products();
|
||||
|
||||
if ($Avance->removeAvance($avance_id)) {
|
||||
$Products->update($product_id, ['product_sold' => 0]);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => "Avance supprimée avec succès. Le produit peut être réservé à nouveau."
|
||||
]);
|
||||
} else {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'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)
|
||||
{
|
||||
$this->verifyRole('updateAvance');
|
||||
|
||||
try {
|
||||
$avanceModel = new Avance();
|
||||
|
||||
$data = $avanceModel->fetchSingleAvance($avance_id);
|
||||
|
||||
return $this->response->setJSON($data);
|
||||
}
|
||||
catch (\Throwable $th) {
|
||||
log_message('error', "Erreur lors de la récupération d'une avance: " . $th->getMessage());
|
||||
|
||||
return $this->response
|
||||
->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']));
|
||||
$this->verifyRole('updateAvance');
|
||||
|
||||
// Boutons d’action
|
||||
$buttons = '';
|
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc('. $value['avance_id'] .')">'
|
||||
. '<i class="fa fa-pencil"></i></button>';
|
||||
}
|
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')"><i class="fa fa-trash"></i></button>';
|
||||
try {
|
||||
if (!$avance_id || !is_numeric($avance_id)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'error' => 'ID d\'avance invalide'
|
||||
]);
|
||||
}
|
||||
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;
|
||||
|
||||
$avanceModel = new Avance();
|
||||
$data = $avanceModel->fetchSingleAvance($avance_id);
|
||||
|
||||
if (!$data) {
|
||||
return $this->response->setStatusCode(404)->setJSON([
|
||||
'error' => 'Avance non trouvée'
|
||||
]);
|
||||
}
|
||||
|
||||
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'
|
||||
]);
|
||||
}
|
||||
|
||||
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 d’action
|
||||
$buttons = '';
|
||||
if (in_array('updateAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-default" onclick="editFunc('. $value['avance_id'] .')">'
|
||||
. '<i class="fa fa-pencil"></i></button>';
|
||||
}
|
||||
if (in_array('deleteAvance', $this->permission) && ($isAdmin || $isOwner)) {
|
||||
$buttons .= '<button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['avance_id'] . ',' . $value['product_id'] . ')"><i class="fa fa-trash"></i></button>';
|
||||
}
|
||||
if (in_array('viewAvance', $this->permission) && !$isAdmin) {
|
||||
$buttons .= ' <a href="#" data-order-id="'.$value['id'].'" class="btn btn-default btn-view" title="Voir"><i class="fa fa-eye"></i></a>';
|
||||
}
|
||||
if ($isAdmin) {
|
||||
$row = [
|
||||
$value['customer_name'],
|
||||
$value['customer_phone'],
|
||||
$value['customer_address'],
|
||||
$product->getProductNameById($value['product_id']),
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '),
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
// dd($row);die;
|
||||
$result['data'][] = $row;
|
||||
}
|
||||
if ($isCommerciale || $isCaissier) {
|
||||
$row = [
|
||||
$value['avance_id'],
|
||||
$product->getProductNameById($value['product_id']),
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['amount_due'], 0, ',', ' '),
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
$result['data'][] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response->setJSON($result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,58 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
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
|
||||
*
|
||||
* 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
|
||||
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 = [];
|
||||
|
||||
/**
|
||||
* Be sure to declare properties for any property fetch you initialized.
|
||||
* The creation of dynamic property is deprecated in PHP 8.2.
|
||||
*/
|
||||
// protected $session;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
public function initController(\CodeIgniter\HTTP\RequestInterface $request,
|
||||
\CodeIgniter\HTTP\ResponseInterface $response,
|
||||
\Psr\Log\LoggerInterface $logger)
|
||||
{
|
||||
// Do Not Edit This Line
|
||||
parent::initController($request, $response, $logger);
|
||||
|
||||
// Preload any models, libraries, etc, here.
|
||||
helper('alerts');
|
||||
|
||||
// E.g.: $this->session = \Config\Services::session();
|
||||
if (function_exists('checkDeadlineAlerts')) {
|
||||
checkDeadlineAlerts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,81 +74,79 @@ class ProductCOntroller extends AdminController
|
||||
|
||||
public function fetchProductData()
|
||||
{
|
||||
// Initialize the response array
|
||||
$result = ['data' => []];
|
||||
$Products = new Products();
|
||||
$Stores = new Stores();
|
||||
|
||||
|
||||
function convertString($name)
|
||||
{
|
||||
return "$name";
|
||||
}
|
||||
// Fetch product data from the model
|
||||
$data = $Products->getProductData(); // Ensure this method exists in your ProductModel
|
||||
|
||||
|
||||
$data = $Products->getProductData();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
|
||||
// Fetch store data
|
||||
$store_data = $Stores->getStoresData($value['store_id']); // Ensure this method exists in your StoreModel
|
||||
$store_data['name'] = $value['store_id'] == 0 ? "TOUS" : $Stores->getStoresData($value['store_id'])["name"];
|
||||
// Construct buttons
|
||||
|
||||
// Gestion du nom du magasin
|
||||
if ($value['store_id'] == 0) {
|
||||
$store_name = "TOUS";
|
||||
} else {
|
||||
$store_info = $Stores->getStoresData($value['store_id']);
|
||||
$store_name = $store_info && isset($store_info['name']) ? $store_info['name'] : "Inconnu";
|
||||
}
|
||||
|
||||
// Disponibilité
|
||||
$availability = ($value['qty'] > 0) ? '<span class="label label-success">En stock</span>' : '<span class="label label-danger">Rupture</span>';
|
||||
|
||||
// Construction des boutons
|
||||
$buttons = '';
|
||||
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>';
|
||||
}
|
||||
|
||||
|
||||
if (in_array('deleteProduct', $this->permission ?? [])) {
|
||||
$buttons .= ' <button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['id'] . ')" data-toggle="modal" data-target="#removeModal"><i class="fa fa-trash"></i></button>';
|
||||
}
|
||||
|
||||
|
||||
if (in_array('updateProduct', $this->permission ?? [])) {
|
||||
$buttons .= ' <a href="ventes/' . $value['id'] . '" class="btn btn-default"><i class="fa fa-image"></i></a>';
|
||||
}
|
||||
|
||||
|
||||
if (in_array('updateProduct', $this->permission ?? [])) {
|
||||
$buttons .= ' <button class="btn btn-default" onclick="generateQrPdf(' . $value["id"] . ')"><i class="fa fa-qrcode"></i></button>';
|
||||
}
|
||||
|
||||
|
||||
if (in_array('viewProduct', $this->permission ?? [])) {
|
||||
$buttons .= " <a href='/ventes/show/" . $value['id'] . "' class='btn btn-default'><i class='fa fa-eye'></i></a>";
|
||||
}
|
||||
|
||||
if (in_array('assignStore', $this->permission ?? [])) {
|
||||
$buttons .=
|
||||
'<button type="button" class="btn btn-info assignbtn" title="Assigner sur un magasin" data-magasin="' . $store_data['name'] . '" data-products-id="' . $value["id"] . '" data-toggle="modal" data-target="#assignStoreModal">
|
||||
'<button type="button" class="btn btn-info assignbtn" title="Assigner sur un magasin" data-magasin="' . $store_name . '" data-products-id="' . $value["id"] . '" data-toggle="modal" data-target="#assignStoreModal">
|
||||
<i class="fa fa-forward"></i>
|
||||
</button>';
|
||||
}
|
||||
|
||||
|
||||
// Image HTML
|
||||
$img = '<img src="' . base_url('assets/images/product_image/' . $value['image']) . '" alt="' . $value['name'] . '" class="img-circle" width="50" height="50" />';
|
||||
|
||||
// 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'],
|
||||
$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] = [
|
||||
$value['image'],
|
||||
convertString($value['sku']),
|
||||
$value['name'],
|
||||
number_format($value['prix_vente'], 0, ',', ' '),
|
||||
$store_data['name'] ?? 'Unknown Store',
|
||||
$availability,
|
||||
$value['price'],
|
||||
$store_name,
|
||||
$availability, // <-- ici la disponibilité ajoutée
|
||||
$buttons
|
||||
];
|
||||
}
|
||||
|
||||
// Return JSON response
|
||||
|
||||
return $this->response->setJSON($result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function create()
|
||||
{
|
||||
@ -356,233 +354,147 @@ class ProductCOntroller extends AdminController
|
||||
return $this->response->setJSON($response);
|
||||
}
|
||||
|
||||
public function createByExcel()
|
||||
{
|
||||
$this->verifyRole("createProduct");
|
||||
|
||||
// 1) Récupération et validation du fichier
|
||||
$file = $this->request->getFile('excel_product');
|
||||
if (!$file || !$file->isValid() || $file->hasMoved()) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Aucun fichier valide reçu"
|
||||
]);
|
||||
}
|
||||
$ext = strtolower($file->getClientExtension());
|
||||
if (! in_array($ext, ['xls', 'xlsx'])) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Seuls les fichiers xls/xlsx sont autorisés"
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
// 2) Chargement du fichier Excel
|
||||
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file->getTempName());
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
// 3) Lecture des données brutes et mapping des en-têtes
|
||||
$allRows = $sheet->toArray(null, true, true, true);
|
||||
if (count($allRows) < 2) {
|
||||
public function createByExcel()
|
||||
{
|
||||
$this->verifyRole("createProduct");
|
||||
|
||||
try {
|
||||
$file = $this->request->getFile('excel_product');
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Fichier invalide ou non reçu"
|
||||
]);
|
||||
}
|
||||
|
||||
$ext = strtolower($file->getClientExtension());
|
||||
if (!in_array($ext, ['xls', 'xlsx'])) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Seuls les fichiers Excel (.xls, .xlsx) sont acceptés"
|
||||
]);
|
||||
}
|
||||
|
||||
$spreadsheet = IOFactory::load($file->getTempName());
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$rows = $sheet->toArray();
|
||||
|
||||
if (count($rows) <= 1) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Le fichier ne contient pas de données"
|
||||
]);
|
||||
}
|
||||
|
||||
// Récupérer les en-têtes
|
||||
$headers = array_shift($rows);
|
||||
$headers = array_map('strtolower', $headers);
|
||||
|
||||
// Mapping des colonnes Excel vers les champs de la base
|
||||
$columnMapping = [
|
||||
'n° série' => 'sku',
|
||||
'marque' => 'marque',
|
||||
'désignation' => 'name',
|
||||
'fournisseur' => 'info', // À adapter selon votre besoin
|
||||
'date d\'arrivage' => 'date_arivage',
|
||||
'n° moteur' => 'numero_de_moteur',
|
||||
'châssis' => 'chasis',
|
||||
'puissance' => 'puissance',
|
||||
'clé' => 'cler',
|
||||
'prix d\'achat' => 'prix_vente',
|
||||
'prix ar' => 'price',
|
||||
'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();
|
||||
|
||||
$countInserted = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (empty(array_filter($row))) continue; // Ignore les lignes vides
|
||||
|
||||
$data = [
|
||||
'is_piece' => 0,
|
||||
'product_sold' => 0,
|
||||
'qty' => 1
|
||||
];
|
||||
|
||||
// Mapper chaque colonne
|
||||
foreach ($headers as $index => $header) {
|
||||
$header = trim($header);
|
||||
if (isset($columnMapping[$header]) && isset($row[$index])) {
|
||||
$field = $columnMapping[$header];
|
||||
$value = trim($row[$index]);
|
||||
|
||||
// Traitements spécifiques pour certains champs
|
||||
switch ($field) {
|
||||
case 'marque':
|
||||
// Chercher ou créer la marque
|
||||
$brand = $BrandsModel->where('name', $value)->first();
|
||||
if (!$brand) {
|
||||
$brandId = $BrandsModel->insert(['name' => $value, 'active' => 1]);
|
||||
} else {
|
||||
$brandId = $brand['id'];
|
||||
}
|
||||
$data[$field] = $brandId;
|
||||
break;
|
||||
|
||||
case 'store_id':
|
||||
// Gestion du magasin
|
||||
if ($value == 'TOUS') {
|
||||
$data[$field] = 0;
|
||||
} else {
|
||||
$store = $StoresModel->where('name', $value)->first();
|
||||
$data[$field] = $store ? $store['id'] : 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'date_arivage':
|
||||
// Convertir la date Excel en format MySQL
|
||||
if (is_numeric($value)) {
|
||||
$data[$field] = date('Y-m-d', \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($value));
|
||||
} else {
|
||||
$data[$field] = date('Y-m-d', strtotime($value));
|
||||
}
|
||||
break;
|
||||
case 'price':
|
||||
// Nettoyer "1 900 000 Ar" → 1900000.00
|
||||
$cleanedValue = str_replace(['Ar', ' ', ','], '', $value);
|
||||
$data[$field] = (float)$cleanedValue;
|
||||
break;
|
||||
|
||||
default:
|
||||
$data[$field] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insertion
|
||||
if (!empty($data['name'])) {
|
||||
if ($ProductsModel->insert($data)) {
|
||||
$countInserted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => "$countInserted produits importés avec succès"
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Le fichier ne contient aucune donnée"
|
||||
'messages' => "Erreur lors de l'import: " . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
$headerRow = array_shift($allRows);
|
||||
$map = [];
|
||||
foreach ($headerRow as $col => $heading) {
|
||||
$h = mb_strtolower(trim($heading));
|
||||
$h = str_replace(['’', '‘', '“', '”'], '\'', $h);
|
||||
$h = iconv('UTF-8', 'ASCII//TRANSLIT', $h);
|
||||
$h = preg_replace('/[^a-z0-9_]/', '_', $h);
|
||||
$h = preg_replace('/_+/', '_', $h);
|
||||
$h = trim($h, '_');
|
||||
|
||||
switch ($h) {
|
||||
case 'designation':
|
||||
case 'nom':
|
||||
$map[$col] = 'name'; break;
|
||||
case 'n_serie':
|
||||
$map[$col] = 'sku'; break;
|
||||
case 'prix_ar':
|
||||
$map[$col] = 'prix_vente'; break;
|
||||
case 'prix_d_achat':
|
||||
case 'prix_dachat':
|
||||
case 'prixd_achat':
|
||||
$map[$col] = 'price'; break;
|
||||
case 'marque':
|
||||
$map[$col] = 'marque'; break;
|
||||
case 'description':
|
||||
$map[$col] = 'description'; break;
|
||||
case 'code_moteur':
|
||||
case 'n_moteur':
|
||||
$map[$col] = 'numero_de_moteur'; break;
|
||||
case 'chassis':
|
||||
case 'chasis':
|
||||
$map[$col] = 'chasis'; break;
|
||||
case 'date_arrivage':
|
||||
case 'date_d_arivage':
|
||||
$map[$col] = 'date_arrivage'; break;
|
||||
case 'puissance':
|
||||
$map[$col] = 'puissance'; break;
|
||||
case 'availability':
|
||||
case 'disponibilite':
|
||||
$map[$col] = 'availability'; break;
|
||||
case 'piece':
|
||||
case 'piece_manquant':
|
||||
$map[$col] = 'is_piece'; break;
|
||||
case 'cle':
|
||||
$map[$col] = 'cler'; break;
|
||||
case 'categories':
|
||||
case 'categorie_id':
|
||||
$map[$col] = 'categorie_id'; break;
|
||||
case 'etat':
|
||||
case 'etats':
|
||||
$map[$col] = 'etats'; break;
|
||||
case 'magasin':
|
||||
$map[$col] = 'store_id'; break;
|
||||
case 'info_manquekit':
|
||||
case 'infomanquekit':
|
||||
$map[$col] = 'infoManquekit'; break;
|
||||
case 'info':
|
||||
case 'info_piece':
|
||||
$map[$col] = 'info'; break;
|
||||
case 'info_manque':
|
||||
$map[$col] = 'infoManque'; break;
|
||||
case 'image':
|
||||
case 'image_s':
|
||||
$map[$col] = 'image'; break;
|
||||
default:
|
||||
// Non mappé
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 4) Extraction des images intégrées, si présent
|
||||
$imagesMap = [];
|
||||
foreach ($sheet->getDrawingCollection() as $drawing) {
|
||||
if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
|
||||
$coord = $drawing->getCoordinates();
|
||||
$extImg = pathinfo($drawing->getPath(), PATHINFO_EXTENSION);
|
||||
$name = uniqid('img_') . ".$extImg";
|
||||
$dir = FCPATH . 'assets/images/product_image/';
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
file_put_contents($dir . $name, file_get_contents($drawing->getPath()));
|
||||
$imagesMap[$coord] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
// 5) Chargement des modèles
|
||||
$ProductsModel = new \App\Models\Products();
|
||||
$ProductsModel->skipValidation(true);
|
||||
$BrandsModel = new \App\Models\Brands();
|
||||
$CatModel = new \App\Models\Category();
|
||||
$countInserted = 0;
|
||||
|
||||
// 6) Boucle sur chaque ligne de données
|
||||
foreach ($allRows as $rowIndex => $row) {
|
||||
$data = [];
|
||||
|
||||
// Lecture des cellules formatées pour chaque champ mappé
|
||||
foreach ($map as $col => $field) {
|
||||
$cellValue = $sheet
|
||||
->getCell($col . ($rowIndex + 2))
|
||||
->getFormattedValue();
|
||||
$data[$field] = trim((string)$cellValue);
|
||||
}
|
||||
|
||||
if (empty($data['name'])) {
|
||||
continue; // champ désignation vide
|
||||
}
|
||||
|
||||
// Conversion du prix AR : capture tous les groupes de chiffres
|
||||
if (! empty($data['prix_vente'])) {
|
||||
preg_match_all('/\d+/', $data['prix_vente'], $matches);
|
||||
$digits = implode('', $matches[0]); // ex. ["2","000","000"] => "2000000"
|
||||
$data['prix_vente'] = intval($digits);
|
||||
} else {
|
||||
$data['prix_vente'] = 0;
|
||||
}
|
||||
|
||||
// Valeurs par défaut
|
||||
$data['qty'] = 1;
|
||||
$data['product_sold'] = 0;
|
||||
$data['availability'] = isset($data['availability'])
|
||||
? (strtolower($data['availability']) === 'oui' ? 1 : 0)
|
||||
: 0;
|
||||
$data['is_piece'] = isset($data['is_piece'])
|
||||
? (strtolower($data['is_piece']) === 'oui' ? 1 : 0)
|
||||
: 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;
|
||||
|
||||
// Association d’image si présente
|
||||
foreach ($map as $col => $field) {
|
||||
if ($field === 'image') {
|
||||
$coordImg = $col . ($rowIndex + 2);
|
||||
if (isset($imagesMap[$coordImg])) {
|
||||
$data['image'] = $imagesMap[$coordImg];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion des clés étrangères
|
||||
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
|
||||
$id = $ProductsModel->insert($data);
|
||||
if ($id !== false) {
|
||||
$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([
|
||||
'success' => true,
|
||||
'messages' => "Produits importés avec succès ($countInserted)"
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', $e->getMessage());
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'messages' => "Erreur pendant l’import : " . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -22,39 +22,35 @@ class ReportController extends AdminController
|
||||
{
|
||||
$this->verifyRole('viewReports');
|
||||
$data['page_title'] = $this->pageTitle;
|
||||
|
||||
|
||||
// Get the current year or the selected year from the form
|
||||
$today_year = date('Y');
|
||||
if ($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();
|
||||
$Orders = new Orders();
|
||||
$Store = new Stores();
|
||||
$parking_data = $Reports->getOrderData($today_year);
|
||||
$data['report_years'] = $Reports->getOrderYear();
|
||||
|
||||
// // Process the parking data and calculate total amounts
|
||||
|
||||
// Process the parking data and calculate total amounts
|
||||
$final_parking_data = [];
|
||||
foreach ($parking_data as $month => $orders) {
|
||||
$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)) {
|
||||
foreach ($orders as $order) {
|
||||
// Cast gross_amount to float and add to the total
|
||||
$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;
|
||||
}
|
||||
|
||||
// //data for the camembert
|
||||
|
||||
|
||||
// Data for the camembert (pie chart)
|
||||
$paymentModes = $Orders->getPaymentModes();
|
||||
$total_mvola1 = $paymentModes->total_mvola1;
|
||||
$total_mvola2 = $paymentModes->total_mvola2;
|
||||
@ -68,57 +64,59 @@ class ReportController extends AdminController
|
||||
$totalOrders = $Orders->getTotalOrders();
|
||||
$totalAmountPerPaymentModes = ["MVOLA" => $total_mvola, "Espece" => $total_espece, "Virement Bancaire" => $total_banque];
|
||||
$totalOrdersCount = (int) $totalOrders->total_orders;
|
||||
// // dd($paymentModes);
|
||||
$labels = [];
|
||||
$totals = [];
|
||||
|
||||
|
||||
if ($totalOrdersCount > 0) {
|
||||
foreach ($totalAmountPerPaymentModes as $mode => $total) {
|
||||
$labels[] = $mode;
|
||||
$totals[] = $total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$data['labels'] = json_encode($labels);
|
||||
$data['totals'] = json_encode($totals);
|
||||
|
||||
// // prepare data for product chart
|
||||
|
||||
// Prepare data for product chart
|
||||
$OrderItem = new OrderItems();
|
||||
$productTable = $OrderItem->getAllSoldProductToday();
|
||||
|
||||
|
||||
$product_sold = (int) $productTable->total_product_sold;
|
||||
$unsold_product = (int) $productTable->total_unsold_product;
|
||||
|
||||
// Définir les labels et les valeurs pour le Pie Chart
|
||||
|
||||
$labels1 = ["Produits vendus", "Produits non vendus"];
|
||||
$totals2 = [$product_sold, $unsold_product];
|
||||
|
||||
// Encoder les données en JSON pour le Pie Chart
|
||||
|
||||
$data['labels_product'] = json_encode($labels1);
|
||||
$data['totals_product'] = json_encode($totals2);
|
||||
|
||||
// // Prepare data for the view
|
||||
|
||||
// Prepare data for the view
|
||||
$data['selected_year'] = $today_year;
|
||||
$data['company_currency'] = $this->companycurrency();
|
||||
$data['results'] = $final_parking_data;
|
||||
|
||||
// //data for the camember in dashboard
|
||||
|
||||
// Data for the camembert in dashboard
|
||||
$totalStoreOrder = $Orders->getTotalOrderPerStore();
|
||||
$totalOrders = $Orders->getTotalOrders();
|
||||
$totalOrdersCount = (int) $totalOrders->total_orders;
|
||||
|
||||
// Initialisation des variables pour éviter l'erreur "Undefined variable"
|
||||
$labelStore = [];
|
||||
$totalPerStore = [];
|
||||
|
||||
foreach ($totalStoreOrder as $totalOrdersInStore) {
|
||||
$storeList = $Store->getStoreById($totalOrdersInStore->store_id);
|
||||
$labelStore[] = $storeList->name ?? 'Inconnu';
|
||||
$totalPerStore[] = ((int) $totalOrdersInStore->total / $totalOrdersCount) * 100;
|
||||
}
|
||||
|
||||
$data['labelStore'] = json_encode($labelStore);
|
||||
$data['totalPerStore'] = json_encode($totalPerStore);
|
||||
|
||||
|
||||
// Load the view
|
||||
// return view('reports/index', $this->data);
|
||||
return $this->render_template('reports/index', $data);
|
||||
}
|
||||
|
||||
private function companycurrency()
|
||||
{
|
||||
return 'AR'; // Replace with your actual logic for company currency
|
||||
|
||||
25
app/Controllers/TestDeadline.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\Controller;
|
||||
|
||||
class TestDeadline extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
// Charger le helper qui contient ta fonction
|
||||
helper('alerts'); // si ton fichier s'appelle alerts_helper.php
|
||||
|
||||
// 🔹 Supprimer le cache 24h pour forcer l'exécution
|
||||
$cacheFile = WRITEPATH . 'cache/check_deadline_last_run.txt';
|
||||
if (file_exists($cacheFile)) {
|
||||
unlink($cacheFile);
|
||||
}
|
||||
|
||||
// Lancer la vérification
|
||||
checkDeadlineAlerts();
|
||||
|
||||
echo "✅ Test de l'envoi d'alertes terminé.";
|
||||
}
|
||||
}
|
||||
@ -350,34 +350,25 @@ class UserController extends AdminController
|
||||
return redirect()->to('/users');
|
||||
}
|
||||
|
||||
|
||||
// supression utilisateur
|
||||
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->verifyRole('deleteUser');
|
||||
|
||||
if ($id) {
|
||||
// Check if the form has been submitted with confirmation
|
||||
if ($this->request->getPost('confirm')) {
|
||||
$usersModel = new Users(); // Ensure Users model is loaded
|
||||
|
||||
$delete = $usersModel->delete($id);
|
||||
$data['page_title'] = $this->pageTitle;
|
||||
|
||||
if ($delete) {
|
||||
session()->setFlashdata('success', 'Supprimé avec succès');
|
||||
return redirect()->to('/users');
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
if (!$id) {
|
||||
return $this->response->setJSON(['success' => false, 'message' => 'ID manquant']);
|
||||
}
|
||||
|
||||
$usersModel = new Users();
|
||||
$delete = $usersModel->delete($id);
|
||||
|
||||
if ($delete) {
|
||||
return $this->response->setJSON(['success' => true, 'message' => 'Supprimé avec succès']);
|
||||
} else {
|
||||
return $this->response->setJSON(['success' => false, 'message' => 'Échec de la suppression']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
145
app/Controllers/test.html
Normal file
@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">
|
||||
<style>
|
||||
body { font-size: 14px; font-family: Arial, sans-serif; }
|
||||
.invoice-container {
|
||||
max-width: 350px; /* Réduire la largeur du cadre */
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
border: 2px solid #007bff; /* Bordure plus visible */
|
||||
border-radius: 10px;
|
||||
background: #f0f8ff; /* Couleur de fond plus douce */
|
||||
}
|
||||
.invoice-header {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
.invoice-footer {
|
||||
background: #343a40;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
|
||||
th { background: #e9ecef; }
|
||||
p, strong { color: #333; }
|
||||
</style>
|
||||
</head>
|
||||
<body onload="window.print();">
|
||||
<div class="invoice-container">
|
||||
<div class="invoice-header">
|
||||
' . esc($company_info['company_name']) . '
|
||||
</div>
|
||||
<p><strong>Facture ID:</strong> ' . esc($order_data['bill_no']) . '</p>
|
||||
<p><strong>Nom:</strong> ' . esc($order_data['customer_name']) . '</p>
|
||||
<p><strong>Adresse:</strong> ' . esc($order_data['customer_address']) . '</p>
|
||||
<p><strong>Téléphone:</strong> ' . esc($order_data['customer_phone']) . '</p>
|
||||
|
||||
<div style="display: flex;align-items: center;justify-content: space-around;margin-bottom: 3%;">
|
||||
<div>
|
||||
<p>Signature du client</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>Signature du commercial</p>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Qté</th>
|
||||
<th>Prix</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>';
|
||||
|
||||
foreach ($orders_items as $item) {
|
||||
$product_data = $Products->getProductData($item['product_id']);
|
||||
$html .= '<tr>
|
||||
<td>' . esc($product_data['name']) . '</td>
|
||||
<td>' . esc($item['qty']) . '</td>
|
||||
<td>' . number_format((float)$item['rate'], 2, '.', ' ') . '</td>
|
||||
<td>' . number_format((float)$item['amount'], 2, '.', ' ') . '</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$html .= ' </tbody>
|
||||
</table>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Total:</th>
|
||||
<td>' . number_format((float)$order_data['gross_amount'], 2, '.', ' ') . '</td>
|
||||
</tr>';
|
||||
|
||||
if (!empty($order_data['service_charge']) && (float)$order_data['service_charge'] > 0) {
|
||||
$html .= '<tr>
|
||||
<th>Frais de service:</th>
|
||||
<td>' . number_format((float)$order_data['service_charge'], 2, '.', ' ') . '</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
if (!empty($order_data['vat_charge']) && (float)$order_data['vat_charge'] > 0) {
|
||||
$html .= '<tr>
|
||||
<th>TVA:</th>
|
||||
<td>' . number_format((float)$order_data['vat_charge'], 2, '.', ' ') . '</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$html .= '<tr>
|
||||
<th>Réduction:</th>
|
||||
<td>' . number_format((float)$order_data['discount'], 2, '.', ' ') . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total à payer:</th>
|
||||
<td><strong>' . number_format((float)$order_data['net_amount'], 2, '.', ' ') . '</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Statut:</th>
|
||||
<td>' . $paid_status . '</td>
|
||||
</tr>';
|
||||
|
||||
// Vérification et ajout des informations de paiement
|
||||
if (!empty($order_data['order_payment_mode'])) {
|
||||
$html .= '<tr>
|
||||
<th>Mode de paiement:</th>
|
||||
<td><strong>' . esc($order_data['order_payment_mode']) . '</strong></td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
if (!empty($order_data['tranche_1'])) {
|
||||
$html .= '<tr>
|
||||
<th>Tranche 1:</th>
|
||||
<td><strong>' . number_format((float)$order_data['tranche_1'], 2, '.', ' ') . '</strong></td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
if (!empty($order_data['tranche_2'])) {
|
||||
$html .= '<tr>
|
||||
<th>Tranche 2:</th>
|
||||
<td><strong>' . number_format((float)$order_data['tranche_2'], 2, '.', ' ') . '</strong></td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$html .= '</table>
|
||||
|
||||
<div class="invoice-footer">
|
||||
Merci pour votre achat !<br>
|
||||
<strong>' . esc($company_info['company_name']) . '</strong>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
176
app/Helpers/alerts_helper.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Users;
|
||||
use App\Models\Avance;
|
||||
use App\Models\AlertMail;
|
||||
|
||||
function checkDeadlineAlerts()
|
||||
{
|
||||
log_message('info', "=== DÉBUT checkDeadlineAlerts ===");
|
||||
|
||||
$cacheFile = WRITEPATH . 'cache/check_deadline_last_run.txt';
|
||||
|
||||
// On enlève la vérification de 24h pour s'assurer que le script tourne quotidiennement
|
||||
file_put_contents($cacheFile, time());
|
||||
|
||||
$avanceModel = new Avance();
|
||||
$alertMailModel = new AlertMail();
|
||||
$usersModel = new Users();
|
||||
|
||||
$today = date('Y-m-d');
|
||||
log_message('info', "Date du jour: {$today}");
|
||||
|
||||
// Modification pour vérifier les avances dans 0-3 jours
|
||||
$avances = $avanceModel
|
||||
->where('DATE(deadline) >=', $today) // Inclut le jour même
|
||||
->where('DATE(deadline) <=', date('Y-m-d', strtotime('+3 days')))
|
||||
->where('active', 1)
|
||||
->findAll();
|
||||
|
||||
log_message('info', "Nombre d'avances trouvées (0-3 jours): " . count($avances));
|
||||
|
||||
$users = $usersModel->select('users.email, users.firstname, users.lastname')
|
||||
->join('user_group', 'user_group.user_id = users.id')
|
||||
->join('groups', 'groups.id = user_group.group_id')
|
||||
->where('groups.group_name', 'DAF')
|
||||
->findAll();
|
||||
|
||||
log_message('info', "Utilisateurs DAF trouvés: " . json_encode($users));
|
||||
|
||||
$emails = array_column($users, 'email');
|
||||
log_message('info', "Emails extraits: " . json_encode($emails));
|
||||
|
||||
if (empty($emails)) {
|
||||
log_message('error', "Aucun email DAF trouvé");
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$allGroups = $db->query("SELECT DISTINCT group_name FROM groups")->getResult();
|
||||
log_message('info', "Groupes disponibles: " . json_encode($allGroups));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($avances as $avance) {
|
||||
$deadline = date('Y-m-d', strtotime($avance['deadline']));
|
||||
$daysLeft = (int) ceil((strtotime($deadline) - strtotime($today)) / 86400);
|
||||
|
||||
log_message('info', "Avance ID: {$avance['avance_id']}, Deadline: {$deadline}, Jours restants: {$daysLeft}");
|
||||
|
||||
// Modification des types d'alerte pour 0, 1, 2, 3 jours
|
||||
$alertType = match($daysLeft) {
|
||||
3 => 'deadline_3_days',
|
||||
2 => 'deadline_2_days',
|
||||
1 => 'deadline_1_day',
|
||||
0 => 'deadline_today',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($alertType === null) {
|
||||
log_message('info', "Pas d'alerte nécessaire pour {$daysLeft} jours restants");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_message('info', "Type d'alerte: {$alertType}");
|
||||
|
||||
// Vérification si l'alerte a déjà été envoyée
|
||||
$alreadySent = $alertMailModel
|
||||
->where('avance_id', $avance['avance_id'])
|
||||
->where('alert_type', $alertType)
|
||||
->first();
|
||||
|
||||
if ($alreadySent) {
|
||||
log_message('info', "Alerte déjà envoyée pour avance_id={$avance['avance_id']} type={$alertType}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Message modifié pour inclure le cas du jour même
|
||||
$urgencyText = $daysLeft === 0 ? "ÉCHÉANCE AUJOURD'HUI" : "{$daysLeft} jour(s) restant(s)";
|
||||
$message = "
|
||||
<h3>⚠️ URGENT : Avance approchant de la deadline</h3>
|
||||
<p><strong>ID Avance :</strong> {$avance['avance_id']}</p>
|
||||
<p><strong>Client :</strong> {$avance['customer_name']}</p>
|
||||
<p><strong>Montant avance :</strong> " . number_format($avance['avance_amount'], 0, ',', ' ') . " Ar</p>
|
||||
<p><strong>Montant dû :</strong> " . number_format($avance['amount_due'], 0, ',', ' ') . " Ar</p>
|
||||
<p><strong>Deadline :</strong> {$deadline}</p>
|
||||
<p><strong>Statut :</strong> <span style='color: red; font-weight: bold;'>{$urgencyText}</span></p>
|
||||
<p><strong>Téléphone client :</strong> {$avance['customer_phone']}</p>
|
||||
<p><strong>Adresse client :</strong> {$avance['customer_address']}</p>
|
||||
<hr>
|
||||
<p><em>Cette avance " . ($daysLeft === 0 ? "arrive à échéance aujourd'hui" : "arrivera à échéance dans {$daysLeft} jour(s)") . ". Action requise immédiatement.</em></p>
|
||||
";
|
||||
|
||||
$emailsSent = 0;
|
||||
foreach ($emails as $to) {
|
||||
log_message('info', "Tentative d'envoi email à: {$to}");
|
||||
|
||||
$subject = $daysLeft === 0
|
||||
? "⚠️ AVANCE URGENTE - ÉCHÉANCE AUJOURD'HUI"
|
||||
: "⚠️ AVANCE URGENTE - {$daysLeft} jour(s) restant(s)";
|
||||
|
||||
if (sendEmailInBackground($to, $subject, $message)) {
|
||||
$emailsSent++;
|
||||
log_message('info', "Email envoyé avec succès à: {$to}");
|
||||
} else {
|
||||
log_message('error', "Échec envoi email à: {$to}");
|
||||
}
|
||||
}
|
||||
|
||||
if ($emailsSent > 0) {
|
||||
log_message('info', "Insertion alerte pour avance_id={$avance['avance_id']} avec type {$alertType}");
|
||||
$alertMailModel->insert([
|
||||
'avance_id' => $avance['avance_id'],
|
||||
'alert_type' => $alertType,
|
||||
'sent_date' => date('Y-m-d H:i:s'),
|
||||
'status' => 'sent',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
} else {
|
||||
log_message('error', "Aucun email envoyé pour avance_id={$avance['avance_id']} avec type {$alertType}");
|
||||
}
|
||||
}
|
||||
|
||||
log_message('info', "=== FIN checkDeadlineAlerts ===");
|
||||
}
|
||||
|
||||
function sendEmailInBackground($to, $subject, $message)
|
||||
{
|
||||
try {
|
||||
log_message('info', "Préparation envoi email à: {$to}");
|
||||
|
||||
$email = \Config\Services::email();
|
||||
|
||||
$config = [
|
||||
'protocol' => 'smtp',
|
||||
'SMTPHost' => 'smtp.gmail.com',
|
||||
'SMTPUser' => 'rey342505@gmail.com',
|
||||
'SMTPPass' => 'loirqovmfuxnasrm',
|
||||
'SMTPPort' => 587,
|
||||
'SMTPCrypto' => 'tls',
|
||||
'mailType' => 'html',
|
||||
'charset' => 'utf-8',
|
||||
'newline' => "\r\n"
|
||||
];
|
||||
|
||||
$email->initialize($config);
|
||||
|
||||
$email->setFrom('rey342505@gmail.com', 'Système Motorbike - Alertes Avances');
|
||||
$email->setTo($to);
|
||||
$email->setSubject($subject);
|
||||
$email->setMessage($message);
|
||||
|
||||
log_message('info', "Configuration email terminée, tentative d'envoi...");
|
||||
|
||||
if (!$email->send()) {
|
||||
$debugInfo = $email->printDebugger(['headers']);
|
||||
log_message('error', "Erreur email à {$to}: " . print_r($debugInfo, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
log_message('info', "Email envoyé avec succès à: {$to}");
|
||||
return true;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', "Exception email à {$to}: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
21
app/Models/AlertMail.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class AlertMail extends Model
|
||||
{
|
||||
protected $table = 'email_alerts';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $allowedFields = [
|
||||
'avance_id',
|
||||
'alert_type',
|
||||
'sent_date',
|
||||
'status',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
// Pas de fonction checkDeadlineAlerts ici !
|
||||
}
|
||||
@ -2,104 +2,120 @@
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
class Avance extends Model{
|
||||
/**
|
||||
* table name
|
||||
* @var string
|
||||
*/
|
||||
|
||||
class Avance extends Model {
|
||||
protected $table = 'avances';
|
||||
protected $primaryKey = 'avance_id';
|
||||
|
||||
protected $allowedFields = [
|
||||
'avance_amount', 'avance_date','user_id',
|
||||
'customer_name',
|
||||
'customer_address',
|
||||
'customer_phone',
|
||||
'customer_cin','gross_amount','amount_due','product_id','is_order','active','store_id'];
|
||||
'customer_name', 'customer_address', 'customer_phone', 'customer_cin',
|
||||
'gross_amount','amount_due','product_id','is_order','active','store_id',
|
||||
'type_avance', 'deadline' // Ajout du champ type et deadline
|
||||
];
|
||||
|
||||
public function createAvance( array $data) {
|
||||
try {
|
||||
return $this->insert($data);
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de l\'ajout de l\'avance : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public function createAvance(array $data) {
|
||||
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');
|
||||
}
|
||||
|
||||
public function updateAvance(int $id, array $data) {
|
||||
|
||||
if ($id <= 0) {
|
||||
log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
return $this->update($id, $data);
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage());
|
||||
return false;
|
||||
// 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);
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de l\'ajout de l\'avance : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateAvance(int $id, array $data) {
|
||||
if ($id <= 0) {
|
||||
log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAllAvanceData(int $id=null) {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
try {
|
||||
// Recalcul de la deadline si le type change
|
||||
if (!empty($data['type']) && !empty($data['avance_date'])) {
|
||||
if (strtolower($data['type']) === 'avance sur terre') {
|
||||
$data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +15 days'));
|
||||
} elseif (strtolower($data['type']) === 'avance sur mer') {
|
||||
$data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +2 months'));
|
||||
}
|
||||
}
|
||||
return $this->update($id, $data);
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 📌 Le reste de tes fonctions restent inchangées
|
||||
public function getAllAvanceData(int $id=null) {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
->where('is_order',0)
|
||||
->where('active',1)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',1)
|
||||
->orderBy('avance_date', 'DESC') ->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',1)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
->where('is_order',0)
|
||||
->where('active',1)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',1)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC') ->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',1)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function fetchSingleAvance(int $avance_id){
|
||||
return $this->where('avance_id',$avance_id)
|
||||
->first();
|
||||
return $this->where('avance_id',$avance_id)->first();
|
||||
}
|
||||
|
||||
public function removeAvance(int $avance_id){
|
||||
@ -112,159 +128,165 @@ class Avance extends Model{
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
try {
|
||||
return $this->select('
|
||||
SUM(avance_amount) AS ta,
|
||||
|
||||
')
|
||||
->where('is_order', 0)
|
||||
->get()
|
||||
->getRowObject();
|
||||
return $this->select('SUM(avance_amount) AS ta')
|
||||
->where('is_order', 0)
|
||||
->get()
|
||||
->getRowObject();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
try {
|
||||
return $this->select('
|
||||
SUM(avance_amount) AS ta,
|
||||
|
||||
')
|
||||
->where('is_order', 0)
|
||||
->where('store_id',$users['store_id'])
|
||||
->get()
|
||||
->getRowObject();
|
||||
return $this->select('SUM(avance_amount) AS ta')
|
||||
->where('is_order', 0)
|
||||
->where('store_id',$users['store_id'])
|
||||
->get()
|
||||
->getRowObject();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getAllAvanceData1(int $id=null) {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
->where('is_order',1)
|
||||
->where('active',1)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',1)
|
||||
->where('active',1)
|
||||
->orderBy('avance_date', 'DESC') ->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',1)
|
||||
->where('active',1)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
->where('is_order',1)
|
||||
->where('active',1)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->orderBy('avance_date', 'DESC') ->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllAvanceData2(int $id=null) {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
|
||||
if($isAdmin) {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
->where('is_order',0)
|
||||
->where('active',0)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',0)
|
||||
->orderBy('avance_date', 'DESC') ->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',0)
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if($id){
|
||||
try {
|
||||
return $this->where('user_id',$id)
|
||||
->where('is_order',0)
|
||||
->where('active',0)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',0)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC') ->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return $this
|
||||
->where('is_order',0)
|
||||
->where('active',0)
|
||||
->where('store_id',$users['store_id'])
|
||||
->orderBy('avance_date', 'DESC')
|
||||
->findAll();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors de la récupération des recouvrements : ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function checkExpiredAvance()
|
||||
{
|
||||
public function checkExpiredAvance() {
|
||||
$now = date('Y-m-d');
|
||||
$avances = $this->where('active', '1')
|
||||
->where('DATE_ADD(avance_date, INTERVAL 15 DAY) <', $now)
|
||||
->where('deadline <', $now)
|
||||
->findAll();
|
||||
|
||||
if (!empty($avances)) {
|
||||
$productModel = new Products();
|
||||
|
||||
foreach ($avances as $avance) {
|
||||
// Mettre l'avance à expirée
|
||||
$this->update($avance['id'], ['active' => '0']);
|
||||
|
||||
// Remettre le produit disponible
|
||||
$this->update($avance['avance_id'], ['active' => '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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -173,27 +173,46 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
|
||||
$("#attributeNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
// initialisation de la DataTable en français
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': base_url + 'attributes/fetchAttributeValueData/' + <?php echo $attribute_data['id']; ?>,
|
||||
'order': []
|
||||
'order': [],
|
||||
"language": {
|
||||
"sProcessing": "Traitement en cours...",
|
||||
"sSearch": "Rechercher :",
|
||||
"sLengthMenu": "Afficher _MENU_ éléments",
|
||||
"sInfo": "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
"sInfoEmpty": "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
"sInfoFiltered": "(filtré de _MAX_ éléments au total)",
|
||||
"sInfoPostFix": "",
|
||||
"sLoadingRecords": "Chargement en cours...",
|
||||
"sZeroRecords": "Aucun élément à afficher",
|
||||
"sEmptyTable": "Aucune donnée disponible dans le tableau",
|
||||
"oPaginate": {
|
||||
"sFirst": "Premier",
|
||||
"sPrevious": "Précédent",
|
||||
"sNext": "Suivant",
|
||||
"sLast": "Dernier"
|
||||
},
|
||||
"oAria": {
|
||||
"sSortAscending": ": activer pour trier la colonne par ordre croissant",
|
||||
"sSortDescending": ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// submit the create from
|
||||
// soumission du formulaire de création
|
||||
$("#createForm").unbind('submit').on('submit', function() {
|
||||
var form = $(this);
|
||||
|
||||
// remove the text-danger
|
||||
$(".text-danger").remove();
|
||||
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
type: form.attr('method'),
|
||||
data: form.serialize(), // /converting the form data into array and sending it to server
|
||||
data: form.serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
@ -201,20 +220,15 @@
|
||||
|
||||
if (response.success === true) {
|
||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
|
||||
|
||||
// hide the modal
|
||||
$("#addModal").modal('hide');
|
||||
|
||||
// reset the form
|
||||
$("#createForm")[0].reset();
|
||||
$("#createForm .form-group").removeClass('has-error').removeClass('has-success');
|
||||
|
||||
} else {
|
||||
|
||||
if (response.messages instanceof Object) {
|
||||
$.each(response.messages, function(index, value) {
|
||||
var id = $("#" + index);
|
||||
@ -229,8 +243,8 @@
|
||||
});
|
||||
} else {
|
||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
}
|
||||
}
|
||||
@ -242,31 +256,24 @@
|
||||
|
||||
});
|
||||
|
||||
// edit function
|
||||
// id => attribute value id
|
||||
// fonction de modification
|
||||
function editFunc(id) {
|
||||
|
||||
$.ajax({
|
||||
url: base_url + 'attributes/fetchAttributeValueById/' + id,
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
console.log(response);
|
||||
|
||||
$("#edit_attribute_value_name").val(response.value);
|
||||
|
||||
// submit the edit from
|
||||
$("#updateForm").unbind('submit').bind('submit', function() {
|
||||
var form = $(this);
|
||||
|
||||
// remove the text-danger
|
||||
$(".text-danger").remove();
|
||||
|
||||
$.ajax({
|
||||
url: form.attr('action') + '/' + id,
|
||||
type: form.attr('method'),
|
||||
data: form.serialize(), // /converting the form data into array and sending it to server
|
||||
data: form.serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
@ -274,18 +281,14 @@
|
||||
|
||||
if (response.success === true) {
|
||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
|
||||
|
||||
// hide the modal
|
||||
$("#editModal").modal('hide');
|
||||
// reset the form
|
||||
$("#updateForm .form-group").removeClass('has-error').removeClass('has-success');
|
||||
|
||||
} else {
|
||||
|
||||
if (response.messages instanceof Object) {
|
||||
$.each(response.messages, function(index, value) {
|
||||
var id = $("#" + index);
|
||||
@ -300,8 +303,8 @@
|
||||
});
|
||||
} else {
|
||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
}
|
||||
}
|
||||
@ -315,22 +318,18 @@
|
||||
});
|
||||
}
|
||||
|
||||
// remove functions
|
||||
// fonction de suppression
|
||||
function removeFunc(id) {
|
||||
if (id) {
|
||||
$("#removeForm").on('submit', function() {
|
||||
|
||||
var form = $(this);
|
||||
|
||||
// remove the text-danger
|
||||
$(".text-danger").remove();
|
||||
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
type: form.attr('method'),
|
||||
data: {
|
||||
attribute_value_id: id
|
||||
},
|
||||
data: { attribute_value_id: id },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
@ -338,18 +337,16 @@
|
||||
|
||||
if (response.success === true) {
|
||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
|
||||
// hide the modal
|
||||
$("#removeModal").modal('hide');
|
||||
|
||||
} else {
|
||||
|
||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Fermer"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
}
|
||||
}
|
||||
@ -359,4 +356,4 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -96,11 +96,29 @@
|
||||
<div class="modal-content">
|
||||
<form id="create_avance_form">
|
||||
<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">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<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 -->
|
||||
<div class="form-group col-md-6">
|
||||
<label>Nom du client</label>
|
||||
@ -129,15 +147,15 @@
|
||||
<div class="row">
|
||||
<!-- Produit -->
|
||||
<div class="form-group col-md-6">
|
||||
<label for="id_product" class="form-label">Produit</label>
|
||||
<select name="id_product" id="id_product" class="form-control " onchange="getProductDataCreate()" required>
|
||||
<option value="">Sélectionnez un produit</option>
|
||||
<?php foreach($products as $p): ?>
|
||||
<option value="<?= $p['id'] ?>" <?= $p['product_sold'] ? 'disabled' : '' ?>>
|
||||
<?= esc($p['name']) ?> <?= $p['product_sold'] ? '(Rupture)' : '' ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label for="id_product" class="form-label">Produit</label>
|
||||
<select name="id_product" id="id_product" class="form-control" onchange="getProductDataCreate()" required>
|
||||
<option value="">Sélectionnez un produit</option>
|
||||
<?php foreach($products as $p): ?>
|
||||
<option value="<?= $p['id'] ?>" <?= $p['product_sold'] ? 'disabled' : '' ?>>
|
||||
<?= esc($p['name']) ?> <?= $p['product_sold'] ? '(Rupture)' : '' ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Prix brut -->
|
||||
<div class="form-group col-md-6">
|
||||
@ -182,6 +200,16 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<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 -->
|
||||
<div class="form-group col-md-6">
|
||||
<label>Nom du client</label>
|
||||
@ -249,362 +277,493 @@
|
||||
</div>
|
||||
<?php endif;?>
|
||||
|
||||
|
||||
<?php if (in_array('deleteAvance', $user_permission)): ?>
|
||||
<!-- remove brand modal -->
|
||||
<div class="modal fade" tabindex="-1" role="dialog" id="removeModal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Supprimer cette avance</h4>
|
||||
<!-- remove brand modal -->
|
||||
<div class="modal fade" tabindex="-1" role="dialog" id="removeModal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Supprimer cette avance</h4>
|
||||
</div>
|
||||
<form role="form" action="<?php echo base_url('avances/deleteAvance') ?>" method="post" id="removeForm">
|
||||
<input type="hidden" name="avance_id" value="">
|
||||
<input type="hidden" name="product_id" value="">
|
||||
<div class="modal-body">
|
||||
<p>Voulez-vous vraiment supprimer ?</p>
|
||||
</div>
|
||||
<form role="form" action="<?php echo base_url('avances/deleteAvance') ?>" method="post" id="removeForm">
|
||||
<div class="modal-body">
|
||||
<p>Voulez-vous vraiment supprimer ?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
|
||||
<button type="submit" class="btn btn-primary">Oui</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
<?php endif; ?>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
|
||||
<button type="submit" class="btn btn-primary">Oui</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<script>
|
||||
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0;
|
||||
$(document).ready(function() {
|
||||
$('#avance_menu').addClass("active");
|
||||
$('.select2').select2();
|
||||
<?php if ($isAdmin):?>
|
||||
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)): ?>
|
||||
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0;
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#avance_menu').addClass("active");
|
||||
$('.select2').select2();
|
||||
|
||||
// 📌 Configuration langue FR
|
||||
var datatableLangFr = {
|
||||
lengthMenu: "Afficher _MENU_ enregistrements par page",
|
||||
zeroRecords: "Aucun résultat trouvé",
|
||||
info: "Affichage de _START_ à _END_ sur _TOTAL_ enregistrements",
|
||||
infoEmpty: "Aucun enregistrement disponible",
|
||||
infoFiltered: "(filtré depuis _MAX_ enregistrements au total)",
|
||||
search: "Rechercher :",
|
||||
paginate: {
|
||||
first: "Premier",
|
||||
last: "Dernier",
|
||||
next: "Suivant",
|
||||
previous: "Précédent"
|
||||
}
|
||||
};
|
||||
|
||||
// 📌 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: "Téléphone" },
|
||||
{ title: "Adresse" },
|
||||
{ title: "Produit" },
|
||||
{ title: "Prix" },
|
||||
{ title: "Avance" },
|
||||
{ title: "Reste à payer" },
|
||||
{ title: "Date" }
|
||||
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
|
||||
,{ title: "Action", orderable: false, searchable: false }
|
||||
<?php endif; ?>
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
$('#avance_order').on('click', function () {
|
||||
$('#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; ?>
|
||||
]
|
||||
});
|
||||
});
|
||||
<?php endif; ?>
|
||||
];
|
||||
|
||||
$('#avance_expired').on('click', function () {
|
||||
$('#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; ?>
|
||||
]
|
||||
});
|
||||
});
|
||||
var manageTable = initAvanceTable('fetchAvanceData', adminColumns);
|
||||
|
||||
|
||||
$('#avance_order').on('click', function () {
|
||||
manageTable = initAvanceTable('fetchAvanceBecameOrder', adminColumns);
|
||||
});
|
||||
|
||||
$('#avance_no_order').on('click', function () {
|
||||
$('#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; ?>
|
||||
$('#avance_expired').on('click', function () {
|
||||
manageTable = initAvanceTable('fetchExpiredAvance', adminColumns);
|
||||
});
|
||||
|
||||
<?php if ($isCaissier || $isCommerciale):?>
|
||||
manageTable = $('#avanceTable').DataTable({
|
||||
ajax: 'fetchAvanceData',
|
||||
columns: [
|
||||
{ title: "#" },
|
||||
{ title: "Produit" },
|
||||
{ title: "Avance" },
|
||||
{ title: "Reste à payer" },
|
||||
{ title: "Date" }
|
||||
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
|
||||
$('#avance_no_order').on('click', function () {
|
||||
manageTable = initAvanceTable('fetchAvanceData', adminColumns);
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($isCaissier || $isCommerciale): ?>
|
||||
var userColumns = [
|
||||
{ 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_order').on('click', function () {
|
||||
$('#avanceTable').DataTable().destroy();
|
||||
manageTable = $('#avanceTable').DataTable({
|
||||
ajax: 'fetchAvanceBecameOrder',
|
||||
columns: [
|
||||
{ title: "#" },
|
||||
{ title: "Produit" },
|
||||
{ title: "Avance" },
|
||||
{ title: "Reste à payer" },
|
||||
{ title: "Date" }
|
||||
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
|
||||
,{ title: "Action", orderable: false, searchable: false }
|
||||
<?php endif; ?>
|
||||
]
|
||||
});
|
||||
});
|
||||
<?php endif; ?>
|
||||
];
|
||||
|
||||
$('#avance_no_order').on('click', function () {
|
||||
$('#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; ?>
|
||||
var manageTable = initAvanceTable('fetchAvanceData', userColumns);
|
||||
|
||||
$('#avance_order').on('click', function () {
|
||||
manageTable = initAvanceTable('fetchAvanceBecameOrder', userColumns);
|
||||
});
|
||||
|
||||
$('#avance_no_order').on('click', function () {
|
||||
manageTable = initAvanceTable('fetchAvanceData', userColumns);
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// Création AJAX
|
||||
// ✅ CRÉATION avec actualisation automatique
|
||||
$('#create_avance_form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const $form = $(this);
|
||||
$.post('/avances/createAvance', $form.serialize(), function(res) {
|
||||
if (res.success === true) {
|
||||
|
||||
manageTable.ajax.reload(null, false);
|
||||
e.preventDefault();
|
||||
const $form = $(this);
|
||||
var brut = parseFloat($('#gross_amount').val()) || 0;
|
||||
var avance = parseFloat($('#avance_amount').val()) || 0;
|
||||
var minAvance = brut * 0.25;
|
||||
|
||||
$("#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">×</span>
|
||||
</button>
|
||||
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages}
|
||||
</div>`
|
||||
);
|
||||
|
||||
// Cacher le modal de création
|
||||
$("#createModal").modal('hide');
|
||||
|
||||
|
||||
$form[0].reset();
|
||||
} else {
|
||||
$("#createModal").modal('hide');
|
||||
|
||||
$("#messages").html(
|
||||
`<div class="alert alert-warning alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${res.messages}
|
||||
</div>`
|
||||
);
|
||||
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">×</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;
|
||||
}
|
||||
}, 'json');
|
||||
|
||||
// Désactiver le bouton de soumission
|
||||
$form.find('button[type="submit"]').prop('disabled', true).text('Enregistrement...');
|
||||
|
||||
$.post('/avances/createAvance', $form.serialize(), function(res) {
|
||||
if (res.success === true) {
|
||||
$("#messages").html(`
|
||||
<div class="alert alert-success alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${res.messages}
|
||||
</div>
|
||||
`);
|
||||
$("#createModal").modal('hide');
|
||||
$form[0].reset(); // Reset le formulaire
|
||||
|
||||
// 🔄 MISE À JOUR DYNAMIQUE après ajout
|
||||
refreshDataTable();
|
||||
|
||||
// Auto-masquer le message après 3 secondes
|
||||
setTimeout(function() {
|
||||
$("#messages .alert").fadeOut();
|
||||
}, 3000);
|
||||
|
||||
} else {
|
||||
$("#createModal").modal('hide');
|
||||
$("#messages").html(`
|
||||
<div class="alert alert-warning alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ${res.messages}
|
||||
</div>
|
||||
`);
|
||||
// Réactiver le bouton en cas d'erreur
|
||||
$form.find('button[type="submit"]').prop('disabled', false).text('Enregistrer');
|
||||
}
|
||||
}, 'json');
|
||||
});
|
||||
|
||||
});
|
||||
// 🔥 SUPPRESSION avec actualisation automatique
|
||||
window.removeFunc = function(id, product_id) {
|
||||
$('#removeModal').modal('show');
|
||||
$('#removeForm input[name="avance_id"]').val(id);
|
||||
$('#removeForm input[name="product_id"]').val(product_id);
|
||||
};
|
||||
|
||||
// Récupère prix pour création
|
||||
function getProductDataCreate(){
|
||||
$('#removeForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var form = $(this);
|
||||
var submitButton = form.find('button[type="submit"]');
|
||||
|
||||
// Désactiver le bouton
|
||||
submitButton.prop('disabled', true).text('Suppression...');
|
||||
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
type: form.attr('method'),
|
||||
data: form.serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success === true) {
|
||||
$('#removeModal').modal('hide');
|
||||
|
||||
$("#messages").html(`
|
||||
<div class="alert alert-success alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ${response.messages}
|
||||
</div>
|
||||
`);
|
||||
|
||||
// 🔄 MISE À JOUR DYNAMIQUE après suppression
|
||||
refreshDataTable();
|
||||
|
||||
// Auto-masquer le message après 3 secondes
|
||||
setTimeout(function() {
|
||||
$("#messages .alert").fadeOut();
|
||||
}, 3000);
|
||||
|
||||
} else {
|
||||
$('#removeModal').modal('hide');
|
||||
$("#messages").html(`
|
||||
<div class="alert alert-warning alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</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">×</span>
|
||||
</button>
|
||||
<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> Une erreur est survenue lors de la suppression.
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
complete: function() {
|
||||
submitButton.prop('disabled', false).text('Oui');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Ré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').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');
|
||||
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 getProductDataUpdate(){
|
||||
function updateDueCreate() {
|
||||
var av = parseFloat($('#avance_amount').val()) || 0;
|
||||
var brutAmount = parseFloat($('#gross_amount').val()) || 0;
|
||||
$('#amount_due').val(Math.max(brutAmount - av, 0));
|
||||
}
|
||||
|
||||
function getProductDataUpdate() {
|
||||
var id = $('#id_product_edit').val();
|
||||
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');
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
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">×</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">×</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();
|
||||
|
||||
var form = $(this);
|
||||
|
||||
// Supprime les anciens messages d'erreur
|
||||
$(".text-danger").remove();
|
||||
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
type: form.attr('method'),
|
||||
data: {
|
||||
avance_id: id,
|
||||
product_id: product_id
|
||||
},
|
||||
$.ajax({
|
||||
url: base_url + 'orders/getProductValueById',
|
||||
type: 'POST',
|
||||
data: { product_id: id },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
manageTable.ajax.reload(null, false);
|
||||
|
||||
if (response.success === true) {
|
||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-ok-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
|
||||
// Cache le modal
|
||||
$("#removeModal").modal('hide');
|
||||
} else {
|
||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||
'<strong><span class="glyphicon glyphicon-exclamation-sign"></span></strong> ' + response.messages +
|
||||
'</div>');
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateDueEdit() {
|
||||
var av = parseFloat($('#avance_amount_edit').val()) || 0;
|
||||
$('#amount_due_edit').val(Math.max(brutEdit - av, 0).toFixed(2));
|
||||
}
|
||||
|
||||
</script>
|
||||
// ✅ 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">×</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">×</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">×</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">×</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">×</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>
|
||||
@ -189,6 +189,31 @@
|
||||
$("#brandNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': '<?= base_url('brands/fetchBrandData') ?>',
|
||||
'order': []
|
||||
|
||||
@ -186,6 +186,31 @@
|
||||
$("#categoryNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': 'fetchCategoryData',
|
||||
'order': []
|
||||
|
||||
@ -14,24 +14,28 @@
|
||||
<div class="row g-0">
|
||||
<!-- Left: Product Image -->
|
||||
<div class="col-md-6 text-center">
|
||||
<img src="<?= base_url('assets/images/product_image/' . $products['image']) ?>"
|
||||
alt="<?= $products['name'] ?>"
|
||||
<img src="<?= base_url('assets/images/product_image/' . ($products['image'] ?? 'default.png')) ?>"
|
||||
alt="<?= esc($products['name'] ?? '') ?>"
|
||||
class="img-fluid rounded"
|
||||
style="width: 100%; height: 100%; object-fit: cover;">
|
||||
</div>
|
||||
|
||||
<!-- Right: Product Details -->
|
||||
<div class="col-md-6 p-4">
|
||||
<h2 class="text-dark"><?= $products['name'] ?></h2>
|
||||
<h4 class="text-success fw-bold"><?= number_format($products['price'], 0, ',', ' ') ?> MGA</h4>
|
||||
<p class="text-muted">En stock: <strong><?= $products['qty'] ?></strong></p>
|
||||
<p class="text-secondary"><?= $products['description'] ?></p>
|
||||
<h2 class="text-dark"><?= esc($products['name'] ?? '') ?></h2>
|
||||
<h4 class="text-success fw-bold">
|
||||
<?= number_format((float) ($products['price'] ?? 0), 0, ',', ' ') ?> MGA
|
||||
</h4>
|
||||
<p class="text-muted">
|
||||
En stock: <strong><?= esc($products['qty'] ?? 0) ?></strong>
|
||||
</p>
|
||||
<p class="text-secondary"><?= esc($products['description'] ?? '') ?></p>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="d-flex mt-4">
|
||||
<form action="<?= base_url('ventes/moreimage/') . $products['id'] ?>" enctype="multipart/form-data" method="post">
|
||||
<form action="<?= base_url('ventes/moreimage/') . ($products['id'] ?? 0) ?>" enctype="multipart/form-data" method="post">
|
||||
<div class="form-group">
|
||||
<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">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary me-2">
|
||||
@ -47,50 +51,56 @@
|
||||
</div>
|
||||
<h3>Galleries</h3>
|
||||
<div class="row">
|
||||
<?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="card shadow-lg border-0 rounded">
|
||||
<div class="card-header bg-success text-white text-center p-0.5">
|
||||
<!-- Clickable Image -->
|
||||
<a href="#" data-toggle="modal" data-target="#imageModal<?= $key ?>">
|
||||
<img src="<?= base_url('assets/images/product_image/' . $value['images']) ?>"
|
||||
alt="<?= $value['images'] ?>"
|
||||
class="img-fluid img-thumbnail rounded"
|
||||
style="width: 100%; height: 250px; object-fit: cover;">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card-footer" style="padding: 1%;">
|
||||
<form action="<?= base_url('ventes/moreimage/supp/') . $value['id'] ?>" method="post">
|
||||
<button class="btn btn-danger">Supprimer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap Modal for Each Image -->
|
||||
<div class="modal fade" id="imageModal<?= $key ?>" tabindex="-1" aria-labelledby="imageModalLabel<?= $key ?>" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="imageModalLabel<?= $key ?>">Visualisation d'image</h5>
|
||||
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"> <i class="fas fa-times"></i></button>
|
||||
<?php if (!empty($galleries)) : ?>
|
||||
<?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="card shadow-lg border-0 rounded">
|
||||
<div class="card-header bg-success text-white text-center p-0.5">
|
||||
<!-- Clickable Image -->
|
||||
<a href="#" data-toggle="modal" data-target="#imageModal<?= $key ?>">
|
||||
<img src="<?= base_url('assets/images/product_image/' . ($value['images'] ?? 'default.png')) ?>"
|
||||
alt="<?= esc($value['images'] ?? '') ?>"
|
||||
class="img-fluid img-thumbnail rounded"
|
||||
style="width: 100%; height: 250px; object-fit: cover;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="modal-body text-center" style="width: 100%;">
|
||||
<img src="<?= base_url('assets/images/product_image/' . $value['images']) ?>"
|
||||
alt="<?= $value['images'] ?>"
|
||||
class="img-fluid rounded"
|
||||
style="width: 100%; height: 100%; object-fit: cover;">>
|
||||
|
||||
<div class="card-footer" style="padding: 1%;">
|
||||
<form action="<?= base_url('ventes/moreimage/supp/') . ($value['id'] ?? 0) ?>" method="post">
|
||||
<button class="btn btn-danger">Supprimer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<!-- Bootstrap Modal for Each Image -->
|
||||
<div class="modal fade" id="imageModal<?= $key ?>" tabindex="-1" aria-labelledby="imageModalLabel<?= $key ?>" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="imageModalLabel<?= $key ?>">Visualisation d'image</h5>
|
||||
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center" style="width: 100%;">
|
||||
<img src="<?= base_url('assets/images/product_image/' . ($value['images'] ?? 'default.png')) ?>"
|
||||
alt="<?= esc($value['images'] ?? '') ?>"
|
||||
class="img-fluid rounded"
|
||||
style="width: 100%; height: 100%; object-fit: cover;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else : ?>
|
||||
<p class="text-muted p-3">Aucune image disponible dans la galerie.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$("#espaceMainMenu").addClass('active');
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -40,6 +40,31 @@
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#espaceMainMenu").addClass('active');
|
||||
const id = <?php echo json_encode($id); ?>;
|
||||
|
||||
|
||||
@ -32,7 +32,21 @@
|
||||
<p class="text-secondary">Moteur N° <?= $products['numero_de_moteur'] ?></p>
|
||||
<p class="text-secondary">Boutique <b><?= $stores ?></b></p>
|
||||
<p class="text-secondary"><?= $products['description'] ?></p>
|
||||
<p class="text-secondary"> <b>Kit</b> <br><?= $products['etats'] == 1 ? $products['infoManquekit'] : 'Non Kit' ?></p>
|
||||
<p class="text-secondary">
|
||||
<b>Etat</b> <br>
|
||||
<?php
|
||||
switch($products['etats']) {
|
||||
case 1:
|
||||
echo 'Kit';
|
||||
break;
|
||||
case 2:
|
||||
echo 'Non kit';
|
||||
break;
|
||||
default:
|
||||
echo 'Non défini';
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="d-flex mt-4">
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
<!-- Content Wrapper. Contains page content -->
|
||||
<style>
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
@ -147,7 +146,7 @@
|
||||
<div class="small-box" style="background-color: #A9A9A9;">
|
||||
<div class="inner">
|
||||
<h2><?php echo number_format($total, 0, '.', ' '); ?>Ar</h2>
|
||||
<p>Totale CAISSE</p>
|
||||
<p>Totale FLUX</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-credit-card"></i>
|
||||
@ -381,6 +380,29 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
// Initialize the datatable
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
manageTable = $('#commperformance').DataTable({
|
||||
'ajax': 'reports/detail/fetchPerformances',
|
||||
'order': [],
|
||||
|
||||
@ -67,6 +67,32 @@
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#remise_menu").addClass('active');
|
||||
|
||||
// Check if the URL contains the _ parameter
|
||||
|
||||
@ -51,10 +51,10 @@
|
||||
<label for="group_name">Désignation</label>
|
||||
<input type="text" class="form-control" id="group_name" name="group_name" placeholder="Enter group name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="permission">Permission</label>
|
||||
<!-- <div class="form-group">
|
||||
<label for="permission">Permission</label> -->
|
||||
|
||||
<table class="table table-responsive">
|
||||
<!-- <table class="table table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
@ -228,7 +228,7 @@
|
||||
<td>-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</table> -->
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@ -12,6 +12,11 @@
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<!-- modification tandroany al -->
|
||||
|
||||
<?= $this->extend('layouts/adminlte') ?>
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<!-- Small boxes (Stat box) -->
|
||||
|
||||
@ -92,6 +92,31 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#groupTable').DataTable();
|
||||
|
||||
$("#mainUserNav").addClass('active');
|
||||
|
||||
@ -276,12 +276,36 @@
|
||||
$("#mecanicNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
const id = <?php echo json_encode($id); ?>;
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': `<?= base_url('mecanicien/fetchMecanicienData') ?>`,
|
||||
'order': []
|
||||
});
|
||||
'ajax': `<?= base_url('mecanicien/fetchMecanicien') ?>`,
|
||||
'order': []
|
||||
});
|
||||
|
||||
|
||||
// submit the create from
|
||||
$("#createForm").unbind('submit').on('submit', function () {
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
<div class="modal-content">
|
||||
<form id="create_avance_form" >
|
||||
<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">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@ -175,6 +175,31 @@
|
||||
var base_url = "<?= base_url() ?>", brutCreate = 0, brutEdit = 0;
|
||||
|
||||
$(document).ready(function() {
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('avance_menu').addClass("active");
|
||||
$('.select2').select2();
|
||||
manageTable = $('#avanceTable').DataTable({
|
||||
|
||||
@ -64,10 +64,10 @@
|
||||
|
||||
|
||||
<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 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 class="col-md-4 col-xs-12 pull pull-left">
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
<?php echo date('Y-m-d') ?></label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="gross_amount" class="col-sm-12 control-label">Date:
|
||||
<label for="gross_amount" class="col-sm-12 control-label">Heure:
|
||||
<?php echo date('h:i a') ?></label>
|
||||
</div>
|
||||
|
||||
|
||||
@ -213,6 +213,32 @@
|
||||
$("#manageOrdersNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': base_url + 'orders/fetchOrdersData',
|
||||
'order': [],
|
||||
|
||||
@ -92,6 +92,31 @@
|
||||
let manageTable;
|
||||
|
||||
$(document).ready(function () {
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#performance_menu").addClass('active');
|
||||
|
||||
function initDataTable(date = '', type = 'day') {
|
||||
|
||||
@ -198,8 +198,9 @@
|
||||
<div class="form-group">
|
||||
<label for="store">Etats</label>
|
||||
<select class="form-control" id="etat" name="etats" required>
|
||||
<option value="2" selected>Non Kit</option>
|
||||
<option value="" >selectionnez une Etat</option>
|
||||
<option value="1">Kit</option>
|
||||
<option value="2">Non kit</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
@ -282,15 +282,61 @@
|
||||
$("#manageProductNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': base_url + 'products/fetchProductData',
|
||||
'order': [],
|
||||
'columnDefs': [{
|
||||
targets: 3,
|
||||
className: 'text-right'
|
||||
} // Column index 3 corresponds to "Prix"
|
||||
]
|
||||
});
|
||||
'ajax': base_url + 'products/fetchProductData',
|
||||
'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': [{
|
||||
targets: 3,
|
||||
className: 'text-right'
|
||||
}]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -278,6 +278,31 @@
|
||||
if (window.location.search.startsWith("?_=")) {
|
||||
window.location.href = window.location.origin + window.location.pathname;
|
||||
}
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': '<?= base_url('recouvrement/fetchRecouvrementData') ?>',
|
||||
|
||||
@ -183,6 +183,31 @@
|
||||
|
||||
// list of recouvrement
|
||||
// initialize the datatable
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#recouvrement_table').DataTable({
|
||||
'ajax': base_url + 'recouvrement/loadData',
|
||||
'recouvrement': []
|
||||
|
||||
@ -199,6 +199,7 @@
|
||||
$("#reportNav").addClass('active');
|
||||
|
||||
// Initialize the datatable
|
||||
|
||||
manageTable = $('#commperformance').DataTable({
|
||||
'ajax': 'fetchPerformances',
|
||||
'order': [],
|
||||
|
||||
@ -187,6 +187,8 @@
|
||||
$("#reportNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
|
||||
|
||||
manageTable = $('#venteTable').DataTable({
|
||||
'ajax': 'fetctData/' + 0,
|
||||
'order': [],
|
||||
|
||||
@ -183,6 +183,7 @@
|
||||
$("#reportNav").addClass('active');
|
||||
|
||||
// Initialize the datatable
|
||||
|
||||
manageTable = $('#commperformance').DataTable({
|
||||
'ajax': 'fetchPerformances',
|
||||
'order': [],
|
||||
|
||||
@ -75,7 +75,29 @@
|
||||
$(function() {
|
||||
$("#securiteNav").addClass('active');
|
||||
|
||||
// ===== Initialisation DataTable =====
|
||||
// ===== Initialisation DataTable =====$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#manageTable').DataTable({
|
||||
ajax: {
|
||||
url: '<?= base_url('validateSecurite/fetchSecuriteData') ?>',
|
||||
|
||||
@ -342,7 +342,6 @@
|
||||
|
||||
<!-- Modal for validating a recouvrement -->
|
||||
<?php if (in_array('validateSortieCaisse', $user_permission)): ?>
|
||||
<!-- update brand modal -->
|
||||
<div class="modal fade" tabindex="-1" role="dialog" id="validateModal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@ -353,7 +352,6 @@
|
||||
|
||||
<form role="form" action="<?php echo base_url('sortieCaisse/validateSortieCaisse') ?>" method="post" id="validate_form">
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-lg-6">
|
||||
<label for="validation" class="control-label">statut :</label>
|
||||
@ -361,38 +359,60 @@
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label for="statut">Raison de validation</label>
|
||||
<input type="text" id="admin_raison">
|
||||
<label for="admin_raison">Raison de validation</label>
|
||||
<input type="text" class="form-control" id="admin_raison" name="admin_raison">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="statut">Statut du décaissement</label>
|
||||
<sname="statut" id="statut" class="form-control">
|
||||
<select name="statut" id="statut" class="form-control">
|
||||
<option value="En attente" selected>En attente</option>
|
||||
<option value="Valider">✔ Valider</option>
|
||||
<option value="En attente" selected> En attente</option>
|
||||
<option value="Refuser">✖ Refuser</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button>
|
||||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button>
|
||||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#sortie_caisse_menu").addClass('active');
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
ajax: '<?= base_url('sortieCaisse/fetchSortieCaisseData') ?>',
|
||||
|
||||
@ -188,11 +188,36 @@
|
||||
$("#storeNav").addClass('active');
|
||||
|
||||
// initialize the datatable
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': '<?= base_url('stores/fetchStoresData') ?>',
|
||||
'order': []
|
||||
});
|
||||
|
||||
|
||||
// submit the create from
|
||||
$("#createForm").unbind('submit').on('submit', function() {
|
||||
var form = $(this);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
<aside class="main-sidebar">
|
||||
|
||||
<!-- sidebar: style can be found in sidebar.less -->
|
||||
@ -73,17 +74,39 @@
|
||||
</li>
|
||||
<?php endif; ?> -->
|
||||
|
||||
<!--debut espace commerciale -->
|
||||
|
||||
<?php if (in_array('viewCom', $user_permission) || in_array('updateCom', $user_permission)): ?>
|
||||
<li id="espaceMainMenu">
|
||||
<a href="<?php echo base_url('/ventes') ?>">
|
||||
<i class="fa fa-shopping-cart"></i> <span> Espace commercial</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li class="treeview" id="espaceMainMenu">
|
||||
<a href="#">
|
||||
<i class="fa fa-shopping-cart"></i>
|
||||
<span>Espace commercial</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li><a href="<?php echo base_url('/ventes') ?>"><i class="fa fa-circle"></i> liste des produits disponibles</a></li>
|
||||
|
||||
<?php if (in_array('createOrder', $user_permission) || in_array('updateOrder', $user_permission) || in_array('viewOrder', $user_permission) || in_array('deleteOrder', $user_permission)): ?>
|
||||
<li><a href="<?php echo base_url('orders') ?>"><i class="fa fa-circle"></i> Commandes</a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (in_array('viewAvance', $user_permission)): ?>
|
||||
<li><a href="<?php echo base_url('avances/') ?>"><i class="fa fa-circle"></i> Avances</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- rapport statistique -->
|
||||
|
||||
<!-- fin espace commerciale -->
|
||||
|
||||
<?php if (in_array('validateSecurite', $user_permission) || in_array('viewSecurite', $user_permission)): ?>
|
||||
<li id="securiteNav">
|
||||
<a href="<?php echo base_url('/validateSecurite') ?>">
|
||||
|
||||
<i class="fa fa-lock"></i> <span> Espace Securite</span>
|
||||
</a>
|
||||
</li>
|
||||
@ -106,7 +129,7 @@
|
||||
<?php if (in_array('createMecanicien', $user_permission) || in_array('updateMecanicien', $user_permission) || in_array('viewMecanicien', $user_permission) || in_array('deleteMecanicien', $user_permission)): ?>
|
||||
<li id="mecanicNav">
|
||||
<a href="<?php echo base_url('mecanicien/') ?>">
|
||||
<i class="fa fa-cog"></i> <span>Mécanicien</span>
|
||||
<i class="fa fa-cog"></i> <span>Réparations</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
@ -191,7 +214,7 @@
|
||||
<?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">
|
||||
<a href="#">
|
||||
<i class="fa fa-dollar"></i>
|
||||
@ -201,15 +224,15 @@
|
||||
</span>
|
||||
</a>
|
||||
<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>
|
||||
<?php endif; ?> -->
|
||||
<?php endif; ?>
|
||||
<?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>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?> -->
|
||||
|
||||
<?php if (in_array('viewReports', $user_permission)): ?>
|
||||
<li id="reportNav">
|
||||
@ -219,13 +242,13 @@
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (in_array('viewAvance', $user_permission)): ?>
|
||||
<!-- <?php if (in_array('viewAvance', $user_permission)): ?>
|
||||
<li id="avance_menu">
|
||||
<a href="<?php echo base_url('avances/') ?>">
|
||||
<i class="fas fa-hand-holding-dollar"></i> <span>Avances</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?> -->
|
||||
|
||||
<?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>
|
||||
|
||||
@ -51,8 +51,8 @@
|
||||
<th>Prenom</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>Role</th>
|
||||
<th>Point de vente</th>
|
||||
<th>Role</th>
|
||||
<?php if (in_array('updateUser', $user_permission) || in_array('deleteUser', $user_permission)): ?>
|
||||
<th>Action</th>
|
||||
<?php endif; ?>
|
||||
@ -262,6 +262,32 @@
|
||||
}
|
||||
|
||||
// Initialisation de DataTable
|
||||
|
||||
// datatable-fr.js
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ éléments",
|
||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun élément à afficher",
|
||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Précédent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
},
|
||||
oAria: {
|
||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manageTable = $('#manageTable').DataTable({
|
||||
'ajax': {
|
||||
url: '<?= base_url('users/fetchUserData') ?>',
|
||||
|
||||
@ -1 +0,0 @@
|
||||
icon
|
||||
1
awstats-icon
Normal file
@ -0,0 +1 @@
|
||||
icon
|
||||
@ -1 +0,0 @@
|
||||
icon
|
||||
1
awstatsicons
Normal file
@ -0,0 +1 @@
|
||||
icon
|
||||
@ -10,10 +10,11 @@
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"kint-php/kint": "^4.2",
|
||||
"laminas/laminas-escaper": "^2.9",
|
||||
"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": {
|
||||
"codeigniter/coding-standard": "^1.5",
|
||||
@ -52,9 +53,7 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-update-cmd": [
|
||||
"CodeIgniter\\ComposerScripts::postUpdate"
|
||||
],
|
||||
|
||||
"test": "phpunit"
|
||||
},
|
||||
"support": {
|
||||
|
||||
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/conf.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/csv.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/document.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/dtd.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/flv.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/fon.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/package.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/runtime.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/swf.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/vbs.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
@ -1 +0,0 @@
|
||||
notavailable.png
|
||||
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
BIN
icon/mime/xsl.png
Normal file
|
Before Width: | Height: | Size: 16 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 0 B |
BIN
public/assets/images/product_image/686cdaac4dad4.jpg
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/assets/images/product_image/686cdaac5f43e.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/assets/images/product_image/686cdaac72635.jpg
Normal file
|
After Width: | Height: | Size: 848 KiB |
BIN
public/assets/images/product_image/6894a44cd6b5f.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/assets/images/product_image/689ca160619a9.jpg
Normal file
|
After Width: | Height: | Size: 657 KiB |
BIN
public/assets/images/product_image/689eba65cf5a8.jpg
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
public/assets/images/product_image/689ebcb98f420.jpg
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
public/assets/images/product_image/689ebceb0d802.jpg
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
public/assets/images/product_image/689ebd3b59c5b.jpg
Normal file
|
After Width: | Height: | Size: 196 KiB |
1
writable/cache/check_deadline_last_run.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
1755601005
|
||||