feat: refonte gestion commandes, sécurité, impressions et notifications
## Gestion Moto & Commandes - Correction des notifications pour la Caissière - Le bouton "Enregistrer" changé en "Payer" - La moto commandée reste visible dans la liste jusqu'à livraison par la sécurité ## Espace Sécurité - Ajout des notifications de livraison - Transfert vers l'espace commande après livraison ## Espace SuperAdmin - Rejet de commande : le produit redevient disponible en stock automatiquement - Correction de la gestion des rôles (permissions inversées) - Avance complète : s'affiche directement chez la Caissière ## Historique des Actions - Ajout de l'historique des actions pour SuperAdmin (traçabilité) ## Dashboard - Filtre par date ajouté (par défaut : aujourd'hui) - Affichage uniquement des données du site concerné ## Espace Commercial - Liste des produits disponibles sur la liste déroulante dans l'ajout des commandes - Le bouton "+" se cache après le premier clic pour les clients particuliers ## Impression Documents - Refonte facture, bon de livraison, facture d'acompte (QR codes, infos dynamiques) ## Sidebar - Correction des animations et du logo dynamique Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a18ccbf34b
commit
a195d24e78
@ -49,6 +49,7 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
|
||||
* dashboard route
|
||||
*/
|
||||
$routes->get('/', [Dashboard::class, 'index']);
|
||||
$routes->get('/dashboard/getTresorerieData', [Dashboard::class, 'getTresorerieData']);
|
||||
$routes->get('/ventes', [Auth::class, 'ventes']);
|
||||
$routes->get('/ventes/(:num)', [Auth::class, 'addImage']);
|
||||
$routes->get('/ventes/fetchProductVente/(:num)', [Auth::class, 'fetchProductVente']);
|
||||
@ -388,6 +389,13 @@ $routes->group('avances', function ($routes) {
|
||||
$routes->post('validateAvance', [AvanceController::class, 'validateAvance']);
|
||||
});
|
||||
|
||||
// Historique des actions (SuperAdmin)
|
||||
$routes->group('action-log', ['filter' => 'auth'], static function ($routes) {
|
||||
$routes->get('/', 'ActionLogController::index');
|
||||
$routes->get('fetchData', 'ActionLogController::fetchData');
|
||||
$routes->get('export', 'ActionLogController::export');
|
||||
});
|
||||
|
||||
// historique
|
||||
$routes->group('historique', ['filter' => 'auth'], static function ($routes) {
|
||||
$routes->get('/', 'HistoriqueController::index');
|
||||
|
||||
154
app/Controllers/ActionLogController.php
Normal file
154
app/Controllers/ActionLogController.php
Normal file
@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Models\Historique;
|
||||
use App\Models\Stores;
|
||||
|
||||
class ActionLogController extends AdminController
|
||||
{
|
||||
private $pageTitle = 'Historique des Actions';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
helper(['form', 'url']);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$session = session();
|
||||
$user = $session->get('user');
|
||||
|
||||
if ($user['group_name'] !== 'SuperAdmin') {
|
||||
return redirect()->to('/');
|
||||
}
|
||||
|
||||
$storesModel = new Stores();
|
||||
|
||||
$data['page_title'] = $this->pageTitle;
|
||||
$data['stores'] = $storesModel->getActiveStore();
|
||||
|
||||
return $this->render_template('action_log/index', $data);
|
||||
}
|
||||
|
||||
public function fetchData()
|
||||
{
|
||||
$session = session();
|
||||
$user = $session->get('user');
|
||||
|
||||
if ($user['group_name'] !== 'SuperAdmin') {
|
||||
return $this->response->setJSON(['data' => []]);
|
||||
}
|
||||
|
||||
$historiqueModel = new Historique();
|
||||
|
||||
$filters = [
|
||||
'action' => $this->request->getGet('action'),
|
||||
'store_name' => $this->request->getGet('store_name'),
|
||||
'product_name' => $this->request->getGet('product'),
|
||||
'sku' => $this->request->getGet('sku'),
|
||||
'date_from' => $this->request->getGet('date_from'),
|
||||
'date_to' => $this->request->getGet('date_to'),
|
||||
];
|
||||
|
||||
$allData = $historiqueModel->getHistoriqueWithFilters($filters);
|
||||
|
||||
$result = ['data' => []];
|
||||
|
||||
foreach ($allData as $row) {
|
||||
$result['data'][] = [
|
||||
date('d/m/Y H:i', strtotime($row['created_at'])),
|
||||
$row['user_name'] ?? '<em>Système</em>',
|
||||
$this->getActionBadge($row['action']),
|
||||
$this->getTableLabel($row['table_name']),
|
||||
$row['description'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
return $this->response->setJSON($result);
|
||||
}
|
||||
|
||||
private function getActionBadge($action)
|
||||
{
|
||||
$badges = [
|
||||
'CREATE' => '<span class="label label-success">Création</span>',
|
||||
'UPDATE' => '<span class="label label-warning">Modification</span>',
|
||||
'DELETE' => '<span class="label label-danger">Suppression</span>',
|
||||
'PAYMENT' => '<span class="label label-primary">Paiement</span>',
|
||||
'VALIDATE' => '<span class="label label-info">Validation</span>',
|
||||
'REFUSE' => '<span class="label label-danger">Refus</span>',
|
||||
'DELIVERY' => '<span class="label label-success">Livraison</span>',
|
||||
'ASSIGN_STORE' => '<span class="label label-info">Assignation</span>',
|
||||
'ENTRER' => '<span class="label label-primary">Entrée</span>',
|
||||
'SORTIE' => '<span class="label label-default">Sortie</span>',
|
||||
'IMPORT' => '<span class="label label-success">Import</span>',
|
||||
'LOGIN' => '<span class="label label-default">Connexion</span>',
|
||||
];
|
||||
|
||||
return $badges[$action] ?? '<span class="label label-secondary">' . $action . '</span>';
|
||||
}
|
||||
|
||||
private function getTableLabel($tableName)
|
||||
{
|
||||
$labels = [
|
||||
'orders' => 'Commande',
|
||||
'products' => 'Produit',
|
||||
'users' => 'Utilisateur',
|
||||
'groups' => 'Rôle',
|
||||
'avances' => 'Avance',
|
||||
'securite' => 'Sécurité',
|
||||
'remise' => 'Remise',
|
||||
'sortie_caisse' => 'Décaissement',
|
||||
'autres_encaissements' => 'Encaissement',
|
||||
'recouvrement' => 'Recouvrement',
|
||||
'stores' => 'Point de vente',
|
||||
'brands' => 'Marque',
|
||||
'categories' => 'Catégorie',
|
||||
];
|
||||
|
||||
return $labels[$tableName] ?? $tableName;
|
||||
}
|
||||
|
||||
public function export()
|
||||
{
|
||||
$session = session();
|
||||
$user = $session->get('user');
|
||||
|
||||
if ($user['group_name'] !== 'SuperAdmin') {
|
||||
return redirect()->to('/');
|
||||
}
|
||||
|
||||
$historiqueModel = new Historique();
|
||||
|
||||
$filters = [
|
||||
'action' => $this->request->getGet('action'),
|
||||
'store_name' => $this->request->getGet('store_name'),
|
||||
'date_from' => $this->request->getGet('date_from'),
|
||||
'date_to' => $this->request->getGet('date_to'),
|
||||
];
|
||||
|
||||
$data = $historiqueModel->getHistoriqueWithFilters($filters);
|
||||
|
||||
$csv = "\xEF\xBB\xBF"; // BOM UTF-8 pour Excel
|
||||
$csv .= "Date;Heure;Utilisateur;Action;Module;Description\n";
|
||||
|
||||
foreach ($data as $row) {
|
||||
$date = date('d-m-Y', strtotime($row['created_at']));
|
||||
$heure = date('H:i', strtotime($row['created_at']));
|
||||
$userName = $row['user_name'] ?? 'Système';
|
||||
$action = $row['action'];
|
||||
$module = $this->getTableLabel($row['table_name']);
|
||||
$description = str_replace('"', '""', $row['description'] ?? '');
|
||||
|
||||
$csv .= "{$date};{$heure};{$userName};{$action};{$module};\"{$description}\"\n";
|
||||
}
|
||||
|
||||
$filename = 'historique_actions_' . date('Y-m-d_H-i') . '.csv';
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'text/csv; charset=utf-8')
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"')
|
||||
->setBody($csv);
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,13 @@ abstract class AdminController extends BaseController
|
||||
protected function render_template($page = null, $data = [])
|
||||
{
|
||||
$data['user_permission'] = $this->permission;
|
||||
// Charger le logo dynamiquement
|
||||
if (!isset($data['company_logo'])) {
|
||||
$companyModel = new Company();
|
||||
$companyData = $companyModel->getCompanyData(1);
|
||||
$data['company_logo'] = $companyData['logo'] ?? 'assets/images/company_logo.jpg';
|
||||
$data['company_name'] = $companyData['company_name'] ?? 'MotorBike';
|
||||
}
|
||||
echo view('templates/header', $data);
|
||||
echo view('templates/header_menu', $data);
|
||||
echo view('templates/side_menubar', $data);
|
||||
@ -51,6 +58,14 @@ abstract class AdminController extends BaseController
|
||||
echo view('templates/footer', $data);
|
||||
}
|
||||
|
||||
// Get company logo path
|
||||
protected function getCompanyLogo()
|
||||
{
|
||||
$model = new Company();
|
||||
$data = $model->getCompanyData(1);
|
||||
return $data['logo'] ?? 'assets/images/company_logo.jpg';
|
||||
}
|
||||
|
||||
// Get company currency using model
|
||||
public function company_currency()
|
||||
{
|
||||
|
||||
@ -6,6 +6,7 @@ use App\Models\ProductImage;
|
||||
use App\Models\Users;
|
||||
use App\Models\Stores;
|
||||
use App\Models\Products;
|
||||
use App\Models\Historique;
|
||||
|
||||
class Auth extends AdminController
|
||||
{
|
||||
@ -79,6 +80,10 @@ public function loginPost()
|
||||
'logged_in' => true
|
||||
]);
|
||||
|
||||
// Log connexion
|
||||
$historique = new Historique();
|
||||
$historique->logAction('users', 'LOGIN', $user['id'], "Connexion de {$user['firstname']} {$user['lastname']} ({$user['group_name']})");
|
||||
|
||||
// Redirect to dashboard
|
||||
return redirect()->to('/');
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ namespace App\Controllers;
|
||||
use App\Models\AutresEncaissements;
|
||||
use App\Models\Stores;
|
||||
use App\Models\Users;
|
||||
use App\Models\Historique;
|
||||
|
||||
class AutresEncaissementsController extends AdminController
|
||||
{
|
||||
@ -106,7 +107,11 @@ class AutresEncaissementsController extends AdminController
|
||||
$Notification->notifyGroupsByPermissionAllStores('notifEncaissement', $notificationMessage, 'encaissements');
|
||||
|
||||
log_message('info', "✅ Encaissement {$encaissementId} créé - Notifications envoyées");
|
||||
|
||||
|
||||
// Log de l'action
|
||||
$historique = new Historique();
|
||||
$historique->logAction('autres_encaissements', 'CREATE', $encaissementId, "Encaissement {$finalType} - Montant: {$montantFormate} Ar");
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'messages' => 'Encaissement enregistré avec succès'
|
||||
|
||||
@ -195,7 +195,72 @@ private function fetchAvanceDataGeneric($methodName = 'getAllAvanceData')
|
||||
|
||||
public function fetchAvanceBecameOrder()
|
||||
{
|
||||
return $this->fetchAvanceDataGeneric('getCompletedAvances');
|
||||
helper(['url', 'form']);
|
||||
$Avance = new Avance();
|
||||
$product = new Products();
|
||||
$result = ['data' => []];
|
||||
|
||||
$data = $Avance->getCompletedAvances();
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
|
||||
$isAdmin = $this->isAdmin($users);
|
||||
$isCommerciale = $this->isCommerciale($users);
|
||||
$isCaissier = $this->isCaissier($users);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$isOwner = $users['id'] === $value['user_id'];
|
||||
|
||||
$buttons = $this->buildActionButtons(
|
||||
$value,
|
||||
$isAdmin,
|
||||
$isOwner,
|
||||
$isCaissier,
|
||||
$isCommerciale
|
||||
);
|
||||
|
||||
// Déterminer le statut
|
||||
if ($value['is_order'] == 1) {
|
||||
$status = '<span class="label label-success"><i class="fa fa-check"></i> Payé</span>';
|
||||
} else {
|
||||
$status = '<span class="label label-warning"><i class="fa fa-clock-o"></i> En attente de paiement</span>';
|
||||
}
|
||||
|
||||
$date_time = date('d-m-Y h:i a', strtotime($value['avance_date']));
|
||||
|
||||
if ($value['type_avance'] === 'mere') {
|
||||
$productName = $value['product_name'] ?? 'Produit sur mer';
|
||||
} else {
|
||||
$productName = !empty($value['product_name'])
|
||||
? $value['product_name']
|
||||
: $product->getProductNameById($value['product_id'] ?? 0);
|
||||
}
|
||||
|
||||
if ($isAdmin) {
|
||||
$result['data'][] = [
|
||||
$value['customer_name'],
|
||||
$value['customer_phone'],
|
||||
$value['customer_address'],
|
||||
$productName,
|
||||
number_format((int)$value['gross_amount'], 0, ',', ' '),
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
$status,
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
} elseif ($isCommerciale || $isCaissier) {
|
||||
$result['data'][] = [
|
||||
$value['avance_id'],
|
||||
$productName,
|
||||
number_format((int)$value['avance_amount'], 0, ',', ' '),
|
||||
$status,
|
||||
$date_time,
|
||||
$buttons,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response->setJSON($result);
|
||||
}
|
||||
|
||||
public function fetchExpiredAvance()
|
||||
@ -998,24 +1063,29 @@ public function printInvoice($avance_id)
|
||||
return redirect()->back()->with('error', 'Accès non autorisé');
|
||||
}
|
||||
|
||||
// ✅ CORRECTION SIMPLIFIÉE
|
||||
// Récupérer les données de l'entreprise
|
||||
$companyModel = new Company();
|
||||
$companyData = $companyModel->getCompanyData(1);
|
||||
|
||||
// Récupérer les détails du produit
|
||||
$product = null;
|
||||
$brandName = '';
|
||||
if ($avance['type_avance'] === 'mere' && !empty($avance['product_name'])) {
|
||||
$productName = $avance['product_name'];
|
||||
$productDetails = [
|
||||
'marque' => $avance['product_name'],
|
||||
'numero_moteur' => '',
|
||||
'puissance' => ''
|
||||
'puissance' => '',
|
||||
'couleur' => '',
|
||||
'chasis' => '',
|
||||
'type_avance' => 'mere',
|
||||
];
|
||||
} else {
|
||||
$product = $Products->find($avance['product_id']);
|
||||
|
||||
|
||||
if (!$product) {
|
||||
return redirect()->back()->with('error', 'Produit non trouvé');
|
||||
}
|
||||
|
||||
$productName = $product['name'] ?? 'N/A';
|
||||
|
||||
// ✅ Récupérer le nom de la marque
|
||||
|
||||
$brandName = 'N/A';
|
||||
if (!empty($product['marque'])) {
|
||||
$db = \Config\Database::connect();
|
||||
@ -1028,15 +1098,18 @@ public function printInvoice($avance_id)
|
||||
$brandName = $brandResult['name'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$productDetails = [
|
||||
'marque' => $brandName,
|
||||
'numero_moteur' => $product['numero_de_moteur'] ?? '',
|
||||
'puissance' => $product['puissance'] ?? ''
|
||||
'puissance' => $product['puissance'] ?? '',
|
||||
'couleur' => $product['cler'] ?? '',
|
||||
'chasis' => $product['chasis'] ?? '',
|
||||
'type_avance' => $avance['type_avance'] ?? 'terre',
|
||||
];
|
||||
}
|
||||
|
||||
$html = $this->generatePrintableInvoiceHTML($avance, $productName, $productDetails);
|
||||
|
||||
$html = $this->generatePrintableInvoiceHTML($avance, $productDetails, $companyData);
|
||||
|
||||
return $this->response->setBody($html);
|
||||
|
||||
@ -1821,27 +1894,42 @@ public function getFullInvoiceForPrint($avance_id)
|
||||
/**
|
||||
* Générer le HTML optimisé pour l'impression (version identique à printInvoice)
|
||||
*/
|
||||
private function generatePrintableInvoiceHTML($avance, $productName, $productDetails)
|
||||
private function generatePrintableInvoiceHTML($avance, $productDetails, $companyData)
|
||||
{
|
||||
$avanceDate = date('d/m/Y', strtotime($avance['avance_date']));
|
||||
$avanceNumber = str_pad($avance['avance_id'], 5, '0', STR_PAD_LEFT);
|
||||
$customerName = strtoupper(esc($avance['customer_name']));
|
||||
$customerPhone = esc($avance['customer_phone']);
|
||||
$customerCin = esc($avance['customer_cin']);
|
||||
$customerPhone = esc($avance['customer_phone'] ?? '');
|
||||
$grossAmount = number_format($avance['gross_amount'], 0, ',', ' ');
|
||||
$avanceAmount = number_format($avance['avance_amount'], 0, ',', ' ');
|
||||
$amountDue = number_format($avance['amount_due'], 0, ',', ' ');
|
||||
$marque = esc($productDetails['marque']) ?: $productName;
|
||||
$marque = esc($productDetails['marque']);
|
||||
$numeroMoteur = esc($productDetails['numero_moteur']);
|
||||
$puissance = esc($productDetails['puissance']);
|
||||
|
||||
$couleur = esc($productDetails['couleur'] ?? '');
|
||||
$chasis = esc($productDetails['chasis'] ?? '');
|
||||
$typeAvance = $productDetails['type_avance'] ?? 'terre';
|
||||
$nSerieOuArrivage = ($typeAvance === 'mere') ? 'Arrivage' : ($chasis ?: $numeroMoteur);
|
||||
$typePayment = esc($avance['type_payment'] ?? '');
|
||||
$unite = '1';
|
||||
|
||||
// Company data
|
||||
$companyName = esc($companyData['company_name'] ?? 'MOTORBIKE STORE');
|
||||
$companyNIF = esc($companyData['NIF'] ?? '');
|
||||
$companySTAT = esc($companyData['STAT'] ?? '');
|
||||
$companyPhone = esc($companyData['phone'] ?? '');
|
||||
$companyPhone2 = esc($companyData['phone2'] ?? '');
|
||||
$companyAddress = esc($companyData['address'] ?? '');
|
||||
|
||||
$year = date('Y');
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Facture Avance - KELY SCOOTERS</title>
|
||||
<title>Facture d'Acompte - {$companyName}</title>
|
||||
<style>
|
||||
@media print {
|
||||
body { margin: 0; padding: 0; }
|
||||
@ -1849,281 +1937,352 @@ private function generatePrintableInvoiceHTML($avance, $productName, $productDet
|
||||
.page { page-break-after: always; }
|
||||
.page:last-child { page-break-after: auto; }
|
||||
}
|
||||
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
|
||||
.page {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
padding: 15mm;
|
||||
min-height: 297mm;
|
||||
padding: 15mm 20mm;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.invoice-container {
|
||||
border: 2px solid #000;
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
||||
/* === RECTO === */
|
||||
.recto-title {
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #000;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 25px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
|
||||
.recto-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
font-size: 11px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-left, .header-right {
|
||||
text-align: left;
|
||||
|
||||
.recto-company {
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.invoice-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #000;
|
||||
|
||||
.recto-company strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.invoice-title h2 {
|
||||
font-size: 18px;
|
||||
|
||||
.recto-date-qr {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.recto-date-qr .date-block {
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.watermark {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-25deg);
|
||||
font-size: 60px;
|
||||
color: rgba(0, 0, 0, 0.06);
|
||||
font-weight: bold;
|
||||
letter-spacing: 10px;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.original-badge {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
padding: 5px 15px;
|
||||
font-weight: bold;
|
||||
transform: skewX(-10deg);
|
||||
|
||||
.recto-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.customer-info {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #000;
|
||||
|
||||
.field-line {
|
||||
margin: 12px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.customer-info div {
|
||||
margin: 5px 0;
|
||||
|
||||
.field-line strong {
|
||||
display: inline-block;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
|
||||
.field-line .field-value {
|
||||
border-bottom: 1px dotted #000;
|
||||
display: inline-block;
|
||||
min-width: 300px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.field-indent {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.product-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
|
||||
.product-table th, .product-table td {
|
||||
border: 1px solid #000;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border: 2px solid #000;
|
||||
padding: 10px 12px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
.product-table th {
|
||||
background: #f0f0f0;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
.product-table td {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
/* === VERSO === */
|
||||
.verso-header {
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.verso-header strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.verso-title {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.verso-intro {
|
||||
margin-bottom: 15px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.article {
|
||||
margin: 15px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.article ul {
|
||||
margin-left: 15px;
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.article li {
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.signature-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: auto;
|
||||
padding: 20px 0;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
|
||||
.signature-box {
|
||||
text-align: center;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.signature-box p {
|
||||
font-weight: bold;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
/* Style spécifique pour le verso */
|
||||
.contract-section {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
width: 40%;
|
||||
border: 2px solid #000;
|
||||
font-size: 10px;
|
||||
min-height: 80px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.contract-section h3 {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.contract-article {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.contract-article strong {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.verso-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.signature-box .sig-title {
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- RECTO -->
|
||||
<!-- ==================== RECTO ==================== -->
|
||||
<div class="page">
|
||||
<div class="invoice-container">
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
<h1>KELY SCOOTERS</h1>
|
||||
<div class="header-info">
|
||||
<div class="header-left">
|
||||
<div>NIF: 401 840 5554</div>
|
||||
<div>STAT: 46101 11 2024 00317</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div>Contact: +261 34 27 946 35 / +261 34 07 079 69</div>
|
||||
<div>Antsakaviro en face WWF</div>
|
||||
</div>
|
||||
<div class="watermark">ORIGINAL</div>
|
||||
|
||||
<div class="recto-title">FACTURE D'ACOMPTE DE RESERVATION</div>
|
||||
|
||||
<div class="recto-header">
|
||||
<div class="recto-company">
|
||||
<strong>{$companyName}</strong><br>
|
||||
NIF : {$companyNIF}<br>
|
||||
STAT : {$companySTAT}<br>
|
||||
Contact : {$companyPhone} / {$companyPhone2}<br>
|
||||
{$companyAddress}
|
||||
</div>
|
||||
<div class="recto-date-qr">
|
||||
<div class="date-block">
|
||||
Date : <strong>{$avanceDate}</strong><br>
|
||||
N° : <strong>{$avanceNumber}</strong>
|
||||
</div>
|
||||
<canvas id="qrcode" class="qr-code"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Invoice Title -->
|
||||
<div class="invoice-title">
|
||||
<div>
|
||||
<h2>FACTURE</h2>
|
||||
<div>Date: {$avanceDate}</div>
|
||||
<div>N°: {$avanceNumber}CI 2025</div>
|
||||
</div>
|
||||
<div class="original-badge">DOIT ORIGINAL</div>
|
||||
</div>
|
||||
|
||||
<div class="recto-body">
|
||||
<div class="field-line"><strong>DOIT</strong></div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>NOM :</strong>
|
||||
<span class="field-value">{$customerName}</span>
|
||||
</div>
|
||||
|
||||
<!-- Customer Info -->
|
||||
<div class="customer-info">
|
||||
<div><strong>NOM:</strong> {$customerName} ({$customerPhone})</div>
|
||||
<div><strong>CIN:</strong> {$customerCin}</div>
|
||||
<div><strong>PC:</strong> {$grossAmount} Ar</div>
|
||||
<div><strong>AVANCE:</strong> {$avanceAmount} Ar</div>
|
||||
<div><strong>RAP:</strong> {$amountDue} Ar</div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>N de Série ou Arrivage :</strong>
|
||||
<span class="field-value">{$nSerieOuArrivage}</span>
|
||||
</div>
|
||||
|
||||
<!-- Product Table -->
|
||||
|
||||
<div class="field-line field-indent">
|
||||
Si Arrivage, préciser la caractéristique de la moto : Couleur
|
||||
<span class="field-value">{$couleur}</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>MOTO :</strong>
|
||||
<span class="field-value">{$marque}</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line field-indent">
|
||||
<strong>Unité :</strong>
|
||||
<span class="field-value">{$unite}</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line field-indent">
|
||||
<strong>Prix Unitaire :</strong>
|
||||
<span class="field-value">{$grossAmount} Ar</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>Montant Total :</strong>
|
||||
<span class="field-value">{$grossAmount} Ar</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>Mode de Paiement :</strong>
|
||||
<span class="field-value">{$typePayment}</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>AVANCE :</strong>
|
||||
<span class="field-value">{$avanceAmount} Ar</span>
|
||||
</div>
|
||||
|
||||
<div class="field-line">
|
||||
<strong>Reste à payer :</strong>
|
||||
<span class="field-value">{$amountDue} Ar</span>
|
||||
</div>
|
||||
|
||||
<!-- Tableau récapitulatif -->
|
||||
<table class="product-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MARQUE</th>
|
||||
<th>N°MOTEUR</th>
|
||||
<th>N° Châssis/Arrivage</th>
|
||||
<th>PUISSANCE</th>
|
||||
<th>RAP (Ariary)</th>
|
||||
<th>Reste à payer (Ariary)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{$marque}</td>
|
||||
<td>{$numeroMoteur}</td>
|
||||
<td>{$chasis}</td>
|
||||
<td>{$puissance}</td>
|
||||
<td>{$amountDue}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VERSO -->
|
||||
<div class="page">
|
||||
<div class="invoice-container">
|
||||
|
||||
|
||||
<div class="invoice-title">
|
||||
<div>
|
||||
<h2>CONDITIONS GÉNÉRALES</h2>
|
||||
<div>Date: {$avanceDate}</div>
|
||||
<div>N°: {$avanceNumber}CI 2025</div>
|
||||
|
||||
<div class="verso-content">
|
||||
<!-- Contract Section -->
|
||||
<div class="contract-section">
|
||||
<h3>FIFANEKENA ARA-BAROTRA (Réservations)</h3>
|
||||
<p><strong>Ry mpanjifa hajaina,</strong></p>
|
||||
<p>Natao ity fifanekena ity mba hialana amin'ny fivadihana hampitokisana amin'ny andaniny sy ankilany.</p>
|
||||
|
||||
<div class="contract-article">
|
||||
<strong>Andininy faha-1: FAMANDRAHANA SY FANDOAVAM-BOLA</strong>
|
||||
<p>Ny mpividy dia manao famandrahana amin'ny alalan'ny fandoavambola mihoatra ny 25 isan-jato amin'ny vidin'entana rehetra (avances).</p>
|
||||
</div>
|
||||
|
||||
<div class="contract-article">
|
||||
<strong>Andininy faha-2: FANDOAVAM-BOLA REHEFA TONGA NY ENTANA (ARRIVAGE)</strong>
|
||||
<p>Rehefa tonga ny moto/pieces dia tsy maintsy mandoa ny 50 isan-jato ny vidin'entana ny mpamandrika.</p>
|
||||
<p>Manana 15 andro kosa adoavana ny 25 isan-jato raha misy tsy fahafahana alohan'ny famoahana ny entana.</p>
|
||||
</div>
|
||||
|
||||
<div class="contract-article">
|
||||
<strong>Andininy faha-3: FAMERENANA VOLA</strong>
|
||||
<p>Raha toa ka misy antony tsy hakana ny entana indray dia tsy mamerina ny vola efa voaloha (avance) ny société.</p>
|
||||
</div>
|
||||
|
||||
<div class="contract-article">
|
||||
<strong>Andininy faha-4: FEPETRA FANAMPINY</strong>
|
||||
<ul style="margin-left: 20px;">
|
||||
<li>Tsy misafidy raha toa ka mamafa no ifanarahana.</li>
|
||||
<li>Tsy azo atao ny mamerina ny entana efa nofandrahana.</li>
|
||||
<li>Tsy azo atao ny manakalo ny entana efa nofandrahana.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Additional space for notes -->
|
||||
<div style="margin-top: 20px; padding: 10px; border: 1px solid #000; flex: 1;">
|
||||
<strong>OBSERVATIONS / NOTES:</strong>
|
||||
<div style="height: 100px; margin-top: 10px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Signatures for verso -->
|
||||
<div class="signature-section">
|
||||
<div class="signature-box">
|
||||
<p>NY MPAMANDRIKA</p>
|
||||
<div style="border-top: 1px solid #000; padding-top: 5px;">Signature</div>
|
||||
</div>
|
||||
<div class="signature-box">
|
||||
<p>NY MPIVAROTRA</p>
|
||||
<div style="border-top: 1px solid #000; padding-top: 5px;">
|
||||
<strong>KELY SCOOTERS</strong><br>
|
||||
NIF: 401 840 5554
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ==================== VERSO ==================== -->
|
||||
<div class="page">
|
||||
<div class="verso-header">
|
||||
<strong>{$companyName}</strong><br>
|
||||
NIF : {$companyNIF}<br>
|
||||
STAT : {$companySTAT}<br>
|
||||
Contact : {$companyPhone} / {$companyPhone2}<br>
|
||||
{$companyAddress}
|
||||
</div>
|
||||
|
||||
<div class="verso-title">FIFANEKENA ARA-BAROTRA (Reservations)</div>
|
||||
|
||||
<div class="verso-intro">
|
||||
Ry mpanjifa hajaina,<br><br>
|
||||
Natao ity fifanekena ity mba hialana amin'ny fivadiahampitokisana amin'ny andaniny sy ankilany.
|
||||
</div>
|
||||
|
||||
<div class="article">
|
||||
<div class="article-title">• Andininy faha-1 : FAMANDRIHANA SY FANDOAVAM-BOLA</div>
|
||||
<p>Ny mpividy dia manao famandrihana amin'ny alalan'ny fandoavam-bola mihoatra ny 25 isan-jato amin'ny vidin'entana rehetra (avances).</p>
|
||||
</div>
|
||||
|
||||
<div class="article">
|
||||
<div class="article-title">• Andininy faha-2 : FANDOAVAM-BOLA REHEFA TONGA NY ENTANA (ARRIVAGE)</div>
|
||||
<p>Rehefa tonga ny moto/pieces dia tsy maintsy mandoha ny 50 isan-jato ny vidin'entana ny mpamandrika.</p>
|
||||
<p>Manana 15 andro kosa andoaovana ny 25 isan-jato raha misy tsy fahafahana alohan'ny famoahana ny entana.</p>
|
||||
</div>
|
||||
|
||||
<div class="article">
|
||||
<div class="article-title">• Andininy faha-3 : FAMERENANA VOLA</div>
|
||||
<p>Raha toa ka misy antony tsy hakana ny entana indray dia tsy mamerina ny vola efa voaloha (avance) ny société.</p>
|
||||
</div>
|
||||
|
||||
<div class="article">
|
||||
<div class="article-title">• Andininy faha-4 : FEPETRA FANAMPINY</div>
|
||||
<ul>
|
||||
<li>Tsy misafidy raha toa ka mamafa no ifanarahana.</li>
|
||||
<li>Tsy azo atao ny mamerina vola efa naloha.</li>
|
||||
<li>Tsy azo atao ny manakalo ny entana efa nofandrihana.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Signatures -->
|
||||
<div class="signature-section">
|
||||
<div class="signature-box">
|
||||
<div class="sig-title">NY MPAMANDRIKA</div>
|
||||
</div>
|
||||
<div class="signature-box" style="text-align: right;">
|
||||
<div class="sig-title">NY MPIVAROTRA</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- QR Code JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js"></script>
|
||||
<script>
|
||||
new QRious({
|
||||
element: document.getElementById('qrcode'),
|
||||
value: "FACTURE D'ACOMPTE DE RESERVATION\\nN° {$avanceNumber}\\nClient: {$customerName}\\nMontant Total: {$grossAmount} Ar\\nAvance: {$avanceAmount} Ar\\nReste: {$amountDue} Ar\\nDate: {$avanceDate}\\nFacebook: https://www.facebook.com/MOTORBIKESTORE2021/",
|
||||
size: 100,
|
||||
level: 'H'
|
||||
});
|
||||
window.onload = function() { window.print(); };
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
@ -44,7 +44,15 @@ class CompanyController extends AdminController
|
||||
'message' => $this->request->getPost('message'),
|
||||
'currency' => $this->request->getPost('currency'),
|
||||
];
|
||||
|
||||
|
||||
// Upload du logo
|
||||
$logoFile = $this->request->getFile('logo');
|
||||
if ($logoFile && $logoFile->isValid() && !$logoFile->hasMoved()) {
|
||||
$newName = 'company_logo.' . $logoFile->getExtension();
|
||||
$logoFile->move(FCPATH . 'assets/images/', $newName, true);
|
||||
$data['logo'] = 'assets/images/' . $newName;
|
||||
}
|
||||
|
||||
if ($Company->updateCompany($data, 1)) {
|
||||
session()->setFlashdata('success', 'Successfully updated');
|
||||
return redirect()->to('/company');
|
||||
|
||||
@ -284,4 +284,89 @@ class Dashboard extends AdminController
|
||||
return $this->render_template('dashboard', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX : Recalculer la trésorerie avec filtre de dates
|
||||
*/
|
||||
public function getTresorerieData()
|
||||
{
|
||||
$session = session();
|
||||
$user_id = $session->get('user');
|
||||
|
||||
$startDate = $this->request->getGet('start_date');
|
||||
$endDate = $this->request->getGet('end_date');
|
||||
|
||||
$orderModel = new Orders();
|
||||
$Avance = new Avance();
|
||||
$sortieCaisse = new SortieCaisse();
|
||||
$Recouvrement = new Recouvrement();
|
||||
$autresEncaissementsModel = new AutresEncaissements();
|
||||
|
||||
$isAdmin = in_array($user_id['group_name'], ['DAF', 'Direction', 'SuperAdmin']);
|
||||
$storeIdFilter = $isAdmin ? null : $user_id['store_id'];
|
||||
|
||||
// Données avec filtre de dates
|
||||
$paymentData = $orderModel->getPaymentModes($startDate, $endDate);
|
||||
$totalAvance = $Avance->getTotalAvance($startDate, $endDate);
|
||||
$paymentDataAvance = $Avance->getPaymentModesAvance($startDate, $endDate);
|
||||
$totalRecouvrement = $Recouvrement->getTotalRecouvrements(null, $startDate, $endDate);
|
||||
$total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse($startDate, $endDate);
|
||||
$totauxAutresEncaissements = $autresEncaissementsModel->getTotalEncaissementsByMode($storeIdFilter, $startDate, $endDate);
|
||||
|
||||
// Autres encaissements
|
||||
$total_autres_enc_espece = $totauxAutresEncaissements['total_espece'];
|
||||
$total_autres_enc_mvola = $totauxAutresEncaissements['total_mvola'];
|
||||
$total_autres_enc_virement = $totauxAutresEncaissements['total_virement'];
|
||||
$total_autres_encaissements = $total_autres_enc_espece + $total_autres_enc_mvola + $total_autres_enc_virement;
|
||||
|
||||
// Sorties
|
||||
$total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0;
|
||||
$total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0;
|
||||
$total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0;
|
||||
|
||||
// Recouvrements
|
||||
$me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0;
|
||||
$bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0;
|
||||
$be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0;
|
||||
$mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0;
|
||||
|
||||
// Orders
|
||||
$total_orders = isset($paymentData->total) ? (float) $paymentData->total : 0;
|
||||
$mv1_orders = isset($paymentData->total_mvola1) ? (float) $paymentData->total_mvola1 : 0;
|
||||
$mv2_orders = isset($paymentData->total_mvola2) ? (float) $paymentData->total_mvola2 : 0;
|
||||
$es1_orders = isset($paymentData->total_espece1) ? (float) $paymentData->total_espece1 : 0;
|
||||
$es2_orders = isset($paymentData->total_espece2) ? (float) $paymentData->total_espece2 : 0;
|
||||
$vb1_orders = isset($paymentData->total_virement_bancaire1) ? (float) $paymentData->total_virement_bancaire1 : 0;
|
||||
$vb2_orders = isset($paymentData->total_virement_bancaire2) ? (float) $paymentData->total_virement_bancaire2 : 0;
|
||||
|
||||
// Avances
|
||||
$total_avances = isset($paymentDataAvance->total) ? (float) $paymentDataAvance->total : 0;
|
||||
$mv_avances = isset($paymentDataAvance->total_mvola) ? (float) $paymentDataAvance->total_mvola : 0;
|
||||
$es_avances = isset($paymentDataAvance->total_espece) ? (float) $paymentDataAvance->total_espece : 0;
|
||||
$vb_avances = isset($paymentDataAvance->total_virement_bancaire) ? (float) $paymentDataAvance->total_virement_bancaire : 0;
|
||||
|
||||
// Brut
|
||||
$total_mvola_brut = $mv1_orders + $mv2_orders + $mv_avances + $total_autres_enc_mvola;
|
||||
$total_espece_brut = $es1_orders + $es2_orders + $es_avances + $total_autres_enc_espece;
|
||||
$total_vb_brut = $vb1_orders + $vb2_orders + $vb_avances + $total_autres_enc_virement;
|
||||
$total_brut = $total_orders + $total_avances + $total_autres_encaissements;
|
||||
|
||||
// Final
|
||||
$total_sortie_global = $total_sortie_espece + $total_sortie_mvola + $total_sortie_virement;
|
||||
$total_mvola_final = $total_mvola_brut - $me - $mb + $bm - $total_sortie_mvola;
|
||||
$total_espece_final = $total_espece_brut + $me + $be - $total_sortie_espece;
|
||||
$total_vb_final = $total_vb_brut - $be - $bm + $mb - $total_sortie_virement;
|
||||
$total_final = $total_brut - $total_sortie_global;
|
||||
|
||||
return $this->response->setJSON([
|
||||
'total_brut' => number_format($total_brut, 0, '.', ' '),
|
||||
'total_sorties' => number_format($total_sortie_global, 0, '.', ' '),
|
||||
'total_net' => number_format($total_final, 0, '.', ' '),
|
||||
'espece' => number_format($total_espece_final, 0, '.', ' '),
|
||||
'mvola' => number_format($total_mvola_final, 0, '.', ' '),
|
||||
'banque' => number_format($total_vb_final, 0, '.', ' '),
|
||||
'avance_mvola' => number_format($mv_avances, 0, '.', ' '),
|
||||
'avance_espece' => number_format($es_avances, 0, '.', ' '),
|
||||
'avance_banque' => number_format($vb_avances, 0, '.', ' '),
|
||||
]);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,11 @@ namespace App\Controllers;
|
||||
use App\Controllers\AdminController;
|
||||
use App\Models\Notification;
|
||||
use App\Models\Orders;
|
||||
use App\Models\OrderItems;
|
||||
use App\Models\Products;
|
||||
use App\Models\Remise;
|
||||
use App\Models\Stores;
|
||||
use App\Models\Historique;
|
||||
|
||||
class RemiseController extends AdminController
|
||||
{
|
||||
@ -149,6 +152,16 @@ class RemiseController extends AdminController
|
||||
if ($demande_status == 'Refusé') {
|
||||
if ($order_id) {
|
||||
$ordersModel->update($order_id, ['paid_status' => 0]);
|
||||
|
||||
// Libérer les produits de la commande (retour en stock)
|
||||
$orderItemsModel = new OrderItems();
|
||||
$productModel = new Products();
|
||||
$orderItems = $orderItemsModel->getOrdersItemData($order_id);
|
||||
foreach ($orderItems as $item) {
|
||||
if (!empty($item['product_id'])) {
|
||||
$productModel->update($item['product_id'], ['product_sold' => 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Message de refus
|
||||
@ -163,7 +176,11 @@ class RemiseController extends AdminController
|
||||
$Notification->notifyGroupsByPermissionAllStores('notifRemise', $messageRefus . "<br><em>Pour information</em>", 'remise/');
|
||||
|
||||
$message = 'La demande de remise a été refusée. Le commercial et les responsables ont été notifiés.';
|
||||
|
||||
|
||||
// Log refus remise
|
||||
$historique = new Historique();
|
||||
$historique->logAction('remise', 'REFUSE', $id_demande, "Refus de la demande de remise - Facture: {$bill_no}");
|
||||
|
||||
} elseif ($demande_status == 'Accepté' || $demande_status == 'Validé') {
|
||||
// ✅ SI ACCEPTÉ PAR LE SUPERADMIN
|
||||
if ($order_id) {
|
||||
@ -184,6 +201,10 @@ class RemiseController extends AdminController
|
||||
$Notification->notifyGroupsByPermissionAllStores('notifRemise', $messageAcceptation . "<br><em>Pour information</em>", 'remise/');
|
||||
|
||||
$message = 'La demande de remise a été acceptée. La caissière, le commercial et les responsables ont été notifiés.';
|
||||
|
||||
// Log validation remise
|
||||
$historique = new Historique();
|
||||
$historique->logAction('remise', 'VALIDATE', $id_demande, "Validation de la demande de remise - Facture: {$bill_no}");
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
|
||||
@ -6,6 +6,7 @@ use App\Models\Securite;
|
||||
use App\Models\Products;
|
||||
use App\Models\Orders;
|
||||
use App\Models\Stores;
|
||||
use App\Models\Historique;
|
||||
|
||||
class SecuriteController extends AdminController
|
||||
{
|
||||
@ -48,7 +49,7 @@ class SecuriteController extends AdminController
|
||||
|
||||
// Bouton d’action
|
||||
$buttons = in_array('validateCommande1', $this->permission)
|
||||
? '<button type="button" class="btn btn-success" onclick="editFunc(' . $securite['id'] . ')" data-toggle="modal" data-target="#editModal"><i class="fa fa-check"></i></button>'
|
||||
? '<button type="button" class="btn btn-success" onclick="editFunc(' . $securite['id'] . ')"><i class="fa fa-check"></i></button>'
|
||||
: '';
|
||||
|
||||
// Statut
|
||||
@ -102,7 +103,7 @@ class SecuriteController extends AdminController
|
||||
public function update(int $id)
|
||||
{
|
||||
$this->verifyRole('updateCommande1');
|
||||
$storeModel = new Securite();
|
||||
$securiteModel = new Securite();
|
||||
$post = $this->request->getPost();
|
||||
$response = [];
|
||||
|
||||
@ -113,12 +114,40 @@ class SecuriteController extends AdminController
|
||||
];
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$Notification = new NotificationController();
|
||||
if ($storeModel->updateSecurite($data, $id)) {
|
||||
$Notification = new NotificationController();
|
||||
|
||||
if ($securiteModel->updateSecurite($data, $id)) {
|
||||
if ($post['status'] === "Validé") {
|
||||
$Notification->notifyGroupsByPermission('notifRemise', 'Une commande a été validé', (int)$users['store_id'], 'orders');
|
||||
// ✅ Récupérer les infos de la ligne securite
|
||||
$securiteData = $securiteModel->getSecuriteData($id);
|
||||
|
||||
if ($securiteData) {
|
||||
// ✅ Marquer le produit comme vendu (product_sold = 1)
|
||||
$productModel = new Products();
|
||||
$productModel->update($securiteData['product_id'], ['product_sold' => 1]);
|
||||
|
||||
// ✅ Mettre à jour la commande liée (paid_status = 3 = livré)
|
||||
if (!empty($securiteData['bill_no'])) {
|
||||
$orderModel = new Orders();
|
||||
$order = $orderModel->getOrdersDataByBillNo($securiteData['bill_no']);
|
||||
if ($order) {
|
||||
$orderModel->update($order['id'], [
|
||||
'paid_status' => 3,
|
||||
'delivered_by' => $users['id'],
|
||||
'delivered_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Notification->notifyGroupsByPermission('notifRemise', 'Une commande a été validée et livrée', (int)$users['store_id'], 'orders');
|
||||
}
|
||||
$response = ['success' => true, 'messages' => 'Mise à jour réussie'];
|
||||
// Log de l'action livraison
|
||||
$historique = new Historique();
|
||||
$billNo = $securiteData['bill_no'] ?? 'N/A';
|
||||
$historique->logAction('securite', 'DELIVERY', $id, "Confirmation de livraison - Facture: {$billNo}");
|
||||
|
||||
$response = ['success' => true, 'messages' => 'Livraison confirmée avec succès'];
|
||||
} else {
|
||||
$response = ['success' => false, 'messages' => 'Erreur en base lors de la mise à jour'];
|
||||
}
|
||||
|
||||
@ -189,20 +189,26 @@ class Avance extends Model {
|
||||
}
|
||||
|
||||
// ✅ CORRECTION : getTotalAvance pour la caissière
|
||||
public function getTotalAvance() {
|
||||
public function getTotalAvance($startDate = null, $endDate = null) {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction','DAF']);
|
||||
|
||||
|
||||
try {
|
||||
$builder = $this->select('SUM(avance_amount) AS ta')
|
||||
->where('is_order', 0)
|
||||
->where('active', 1); // ✅ Ajout du filtre active
|
||||
|
||||
->where('active', 1);
|
||||
|
||||
if (!$isAdmin) {
|
||||
$builder->where('store_id', $users['store_id']); // ✅ Filtre par store pour caissière
|
||||
$builder->where('store_id', $users['store_id']);
|
||||
}
|
||||
|
||||
if ($startDate) {
|
||||
$builder->where('DATE(avance_date) >=', $startDate);
|
||||
}
|
||||
if ($endDate) {
|
||||
$builder->where('DATE(avance_date) <=', $endDate);
|
||||
}
|
||||
|
||||
return $builder->get()->getRowObject();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur lors du total du montant des avances : ' . $e->getMessage());
|
||||
@ -212,7 +218,7 @@ class Avance extends Model {
|
||||
|
||||
// ✅ CORRECTION PRINCIPALE : getPaymentModesAvance pour la caissière
|
||||
// ✅ MODIFICATION : Ne compter QUE les avances VALIDÉES
|
||||
public function getPaymentModesAvance()
|
||||
public function getPaymentModesAvance($startDate = null, $endDate = null)
|
||||
{
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
@ -226,14 +232,19 @@ public function getPaymentModesAvance()
|
||||
SUM(CASE WHEN LOWER(type_payment) = "en espèce" THEN avance_amount ELSE 0 END) AS total_espece,
|
||||
SUM(CASE WHEN LOWER(type_payment) = "virement bancaire" THEN avance_amount ELSE 0 END) AS total_virement_bancaire
|
||||
')
|
||||
->where('validated', 1) // ✅ AJOUT : Uniquement les avances validées
|
||||
->where('validated', 1)
|
||||
->where('active', 1)
|
||||
->where('is_order', 0);
|
||||
|
||||
// ✅ Filtre par store pour non-admin
|
||||
|
||||
if (!$isAdmin) {
|
||||
$builder->where('store_id', $users['store_id']);
|
||||
}
|
||||
if ($startDate) {
|
||||
$builder->where('DATE(avance_date) >=', $startDate);
|
||||
}
|
||||
if ($endDate) {
|
||||
$builder->where('DATE(avance_date) <=', $endDate);
|
||||
}
|
||||
|
||||
$result = $builder->get()->getRowObject();
|
||||
|
||||
@ -442,9 +453,11 @@ public function getPaymentModesAvance()
|
||||
$isCommercial = in_array($users['group_name'], ['COMMERCIALE']);
|
||||
$isCaissier = in_array($users['group_name'], ['Caissière']);
|
||||
|
||||
$builder = $this->where('is_order', 0)
|
||||
->where('active', 1)
|
||||
->where('amount_due', 0);
|
||||
$builder = $this->where('amount_due', 0)
|
||||
->groupStart()
|
||||
->where('active', 1)
|
||||
->orWhere('is_order', 1)
|
||||
->groupEnd();
|
||||
|
||||
if ($isCommercial) {
|
||||
$builder->where('user_id', $users['id']);
|
||||
|
||||
@ -13,7 +13,7 @@ class Company extends Model
|
||||
protected $table = 'company';
|
||||
// List all the fields that are allowed to be updated or inserted
|
||||
protected $allowedFields = [
|
||||
'company_name', 'service_charge_value', 'vat_charge_value', 'address', 'phone', 'phone2', 'NIF', 'STAT', 'country', 'message', 'currency',
|
||||
'company_name', 'service_charge_value', 'vat_charge_value', 'address', 'phone', 'phone2', 'NIF', 'STAT', 'country', 'message', 'currency', 'logo',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -16,6 +16,8 @@ class Historique extends Model
|
||||
'sku',
|
||||
'store_name',
|
||||
'description',
|
||||
'user_id',
|
||||
'user_name',
|
||||
'created_at'
|
||||
];
|
||||
protected $useTimestamps = false;
|
||||
@ -111,6 +113,32 @@ class Historique extends Model
|
||||
return $this->insert($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistrer une action utilisateur dans l'historique
|
||||
*/
|
||||
public function logAction($tableName, $action, $rowId, $description)
|
||||
{
|
||||
$session = session();
|
||||
$user = $session->get('user');
|
||||
|
||||
$data = [
|
||||
'table_name' => $tableName,
|
||||
'action' => $action,
|
||||
'row_id' => $rowId,
|
||||
'product_name' => null,
|
||||
'sku' => null,
|
||||
'store_name' => $user['store_name'] ?? null,
|
||||
'description' => $description,
|
||||
'user_id' => $user['id'] ?? null,
|
||||
'user_name' => isset($user['firstname'], $user['lastname'])
|
||||
? $user['firstname'] . ' ' . $user['lastname']
|
||||
: ($user['username'] ?? 'Système'),
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
return $this->insert($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoyer l'historique ancien (plus de X jours)
|
||||
*/
|
||||
|
||||
@ -387,12 +387,12 @@ class Orders extends Model
|
||||
->findAll();
|
||||
}
|
||||
|
||||
public function getPaymentModes()
|
||||
public function getPaymentModes($startDate = null, $endDate = null)
|
||||
{
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
|
||||
|
||||
|
||||
$baseQuery = $this->db->table('orders')
|
||||
->select('
|
||||
|
||||
@ -462,12 +462,19 @@ class Orders extends Model
|
||||
ELSE 0
|
||||
END) AS total_virement_bancaire2
|
||||
')
|
||||
->whereIn('orders.paid_status', [1, 3]);
|
||||
|
||||
->whereIn('orders.paid_status', [1, 3]);
|
||||
|
||||
if (!$isAdmin) {
|
||||
$baseQuery->where('orders.store_id', $users['store_id']);
|
||||
}
|
||||
|
||||
|
||||
if ($startDate) {
|
||||
$baseQuery->where('DATE(orders.date_time) >=', $startDate);
|
||||
}
|
||||
if ($endDate) {
|
||||
$baseQuery->where('DATE(orders.date_time) <=', $endDate);
|
||||
}
|
||||
|
||||
return $baseQuery->get()->getRowObject();
|
||||
}
|
||||
|
||||
|
||||
@ -21,8 +21,8 @@ class Products extends Model
|
||||
$isAdmin = in_array($user['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
|
||||
|
||||
$builder = $this->where('is_piece', 0)
|
||||
->where('product_sold', 0);
|
||||
|
||||
->whereIn('product_sold', [0, 2]);
|
||||
|
||||
if (!$isAdmin) {
|
||||
if (empty($user['store_id']) || $user['store_id'] == 0) {
|
||||
$builder->where('id', -1);
|
||||
@ -58,8 +58,7 @@ class Products extends Model
|
||||
|
||||
return $builder->join('brands', 'brands.id = products.marque')
|
||||
->orderBy('products.id', 'DESC')
|
||||
->select('brands.name as brand_name,COUNT( products.id) as total_product, products.store_id as store_id,products.*')
|
||||
->groupBy('store_id')
|
||||
->select('brands.name as brand_name, products.store_id as store_id, products.*')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
@ -75,7 +74,7 @@ class Products extends Model
|
||||
public function getProductDataStore(int $store_id, bool $excludeAvance = true, int $currentProductId = null)
|
||||
{
|
||||
$builder = $this->where('is_piece', 0)
|
||||
->where('product_sold', 0)
|
||||
->whereIn('product_sold', [0, 2])
|
||||
->where('availability', 1)
|
||||
->where('store_id', $store_id);
|
||||
|
||||
@ -92,13 +91,14 @@ class Products extends Model
|
||||
$builder->where("id NOT IN ($subQueryAvances)", null, false);
|
||||
}
|
||||
|
||||
// ✅ LISTE : Exclure TOUS les produits ayant une commande (statuts 1, 2, 3)
|
||||
// ✅ LISTE : Exclure uniquement les produits dont la commande est LIVRÉE (paid_status = 3)
|
||||
// Les produits commandés mais non livrés restent visibles dans les produits disponibles
|
||||
$subQueryOrders = $db->table('orders_item')
|
||||
->select('orders_item.product_id')
|
||||
->join('orders', 'orders.id = orders_item.order_id')
|
||||
->whereIn('orders.paid_status', [1, 2, 3]) // ✅ Disparaît de la liste dès qu'il y a une commande
|
||||
->where('orders.paid_status', 3)
|
||||
->getCompiledSelect();
|
||||
|
||||
|
||||
$builder->where("id NOT IN ($subQueryOrders)", null, false);
|
||||
|
||||
// Exception pour le produit actuel lors de modification de commande
|
||||
|
||||
@ -12,7 +12,7 @@ class Securite extends Model
|
||||
*/
|
||||
protected $table = 'securite';
|
||||
protected $primaryKey = 'id'; // Primary key column
|
||||
protected $allowedFields = ['product_id', 'status', 'date', 'active'];
|
||||
protected $allowedFields = ['product_id', 'bill_no', 'store_id', 'status', 'date', 'active'];
|
||||
|
||||
|
||||
|
||||
|
||||
@ -165,25 +165,24 @@ class SortieCaisse extends Model
|
||||
/**
|
||||
* ✅ MODIFICATION : DAF, Direction, SuperAdmin voient TOUS les totaux
|
||||
*/
|
||||
public function getTotalSortieCaisse() {
|
||||
public function getTotalSortieCaisse($startDate = null, $endDate = null) {
|
||||
$session = session();
|
||||
$users = $session->get('user');
|
||||
|
||||
// ✅ DAF, Direction, SuperAdmin : Voir TOUS les stores
|
||||
|
||||
$isAdmin = in_array($users['group_name'], ['Direction', 'DAF', 'SuperAdmin']);
|
||||
|
||||
|
||||
if ($isAdmin) {
|
||||
try {
|
||||
return $this->select('
|
||||
$builder = $this->select('
|
||||
SUM(CASE WHEN mode_paiement = "En espèce" AND statut = "Payé" THEN montant_retire ELSE 0 END) AS total_espece,
|
||||
SUM(CASE WHEN mode_paiement = "MVOLA" AND statut = "Payé" THEN montant_retire ELSE 0 END) AS total_mvola,
|
||||
SUM(CASE WHEN mode_paiement = "Virement Bancaire" AND statut = "Payé" THEN montant_retire ELSE 0 END) AS total_virement,
|
||||
SUM(CASE WHEN statut = "Payé" THEN montant_retire ELSE 0 END) AS mr
|
||||
')
|
||||
// ✅ CHANGEMENT : Uniquement statut = "Payé" (plus "Valider")
|
||||
->where('statut', 'Payé')
|
||||
->get()
|
||||
->getRowObject();
|
||||
->where('statut', 'Payé');
|
||||
if ($startDate) { $builder->where('DATE(created_at) >=', $startDate); }
|
||||
if ($endDate) { $builder->where('DATE(created_at) <=', $endDate); }
|
||||
return $builder->get()->getRowObject();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur getTotalSortieCaisse (Admin) : ' . $e->getMessage());
|
||||
return (object)[
|
||||
@ -194,19 +193,18 @@ class SortieCaisse extends Model
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// ✅ CAISSIÈRE : Uniquement son store ET statut "Payé"
|
||||
try {
|
||||
return $this->select('
|
||||
$builder = $this->select('
|
||||
SUM(CASE WHEN mode_paiement = "En espèce" AND statut = "Payé" THEN montant_retire ELSE 0 END) AS total_espece,
|
||||
SUM(CASE WHEN mode_paiement = "MVOLA" AND statut = "Payé" THEN montant_retire ELSE 0 END) AS total_mvola,
|
||||
SUM(CASE WHEN mode_paiement = "Virement Bancaire" AND statut = "Payé" THEN montant_retire ELSE 0 END) AS total_virement,
|
||||
SUM(CASE WHEN statut = "Payé" THEN montant_retire ELSE 0 END) AS mr
|
||||
')
|
||||
->where('store_id', $users['store_id'])
|
||||
// ✅ CHANGEMENT : Uniquement statut = "Payé" (plus "Valider")
|
||||
->where('statut', 'Payé')
|
||||
->get()
|
||||
->getRowObject();
|
||||
->where('statut', 'Payé');
|
||||
if ($startDate) { $builder->where('DATE(created_at) >=', $startDate); }
|
||||
if ($endDate) { $builder->where('DATE(created_at) <=', $endDate); }
|
||||
return $builder->get()->getRowObject();
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Erreur getTotalSortieCaisse (Store) : ' . $e->getMessage());
|
||||
return (object)[
|
||||
|
||||
178
app/Views/action_log/index.php
Normal file
178
app/Views/action_log/index.php
Normal file
@ -0,0 +1,178 @@
|
||||
<meta charset="UTF-8">
|
||||
<div class="content-wrapper">
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Historique des <small>Actions</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="<?= base_url('/') ?>"><i class="fa fa-dashboard"></i> Accueil</a></li>
|
||||
<li class="active">Historique des Actions</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<div id="messages"></div>
|
||||
|
||||
<!-- Filtres -->
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><i class="fa fa-filter"></i> Filtres</h3>
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label>Date de debut</label>
|
||||
<input type="date" id="filterDateFrom" class="form-control" value="<?= date('Y-m-d') ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Date de fin</label>
|
||||
<input type="date" id="filterDateTo" class="form-control" value="<?= date('Y-m-d') ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Action</label>
|
||||
<select id="filterAction" class="form-control">
|
||||
<option value="">Toutes</option>
|
||||
<option value="CREATE">Creation</option>
|
||||
<option value="UPDATE">Modification</option>
|
||||
<option value="DELETE">Suppression</option>
|
||||
<option value="PAYMENT">Paiement</option>
|
||||
<option value="VALIDATE">Validation</option>
|
||||
<option value="REFUSE">Refus</option>
|
||||
<option value="DELIVERY">Livraison</option>
|
||||
<option value="ASSIGN_STORE">Assignation</option>
|
||||
<option value="ENTRER">Entree</option>
|
||||
<option value="SORTIE">Sortie</option>
|
||||
<option value="LOGIN">Connexion</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label>Magasin</label>
|
||||
<select id="filterStore" class="form-control">
|
||||
<option value="">Tous</option>
|
||||
<?php if (isset($stores) && is_array($stores)): ?>
|
||||
<?php foreach ($stores as $store): ?>
|
||||
<option value="<?= $store['name'] ?>"><?= $store['name'] ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label> </label>
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary btn-block" id="btnFilter">
|
||||
<i class="fa fa-search"></i> Filtrer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label> </label>
|
||||
<div>
|
||||
<button type="button" class="btn btn-warning btn-block" id="btnReset">
|
||||
<i class="fa fa-refresh"></i> Reinitialiser
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tableau -->
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<div class="pull-right">
|
||||
<button type="button" class="btn btn-success" id="btnExport">
|
||||
<i class="fa fa-download"></i> Exporter CSV
|
||||
</button>
|
||||
</div>
|
||||
<h3 class="box-title"><i class="fa fa-history"></i> Toutes les actions</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="actionLogTable" class="table table-bordered table-striped" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Utilisateur</th>
|
||||
<th>Action</th>
|
||||
<th>Module</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#mainActionLogNav").addClass('active');
|
||||
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sLengthMenu: "Afficher _MENU_ elements",
|
||||
sInfo: "Affichage de l'element _START_ a _END_ sur _TOTAL_ elements",
|
||||
sInfoEmpty: "Affichage de l'element 0 a 0 sur 0 element",
|
||||
sInfoFiltered: "(filtre de _MAX_ elements au total)",
|
||||
sLoadingRecords: "Chargement en cours...",
|
||||
sZeroRecords: "Aucun element a afficher",
|
||||
sEmptyTable: "Aucune donnee disponible",
|
||||
oPaginate: {
|
||||
sFirst: "Premier",
|
||||
sPrevious: "Precedent",
|
||||
sNext: "Suivant",
|
||||
sLast: "Dernier"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var base_url = '<?= base_url() ?>';
|
||||
|
||||
function loadTable() {
|
||||
if ($.fn.DataTable.isDataTable('#actionLogTable')) {
|
||||
$('#actionLogTable').DataTable().destroy();
|
||||
}
|
||||
|
||||
$('#actionLogTable').DataTable({
|
||||
ajax: {
|
||||
url: base_url + '/action-log/fetchData',
|
||||
type: 'GET',
|
||||
data: function(d) {
|
||||
d.date_from = $('#filterDateFrom').val();
|
||||
d.date_to = $('#filterDateTo').val();
|
||||
d.action = $('#filterAction').val();
|
||||
d.store_name = $('#filterStore').val();
|
||||
}
|
||||
},
|
||||
order: [[0, 'desc']],
|
||||
pageLength: 25
|
||||
});
|
||||
}
|
||||
|
||||
loadTable();
|
||||
|
||||
$('#btnFilter').on('click', function() {
|
||||
loadTable();
|
||||
});
|
||||
|
||||
$('#btnReset').on('click', function() {
|
||||
$('#filterDateFrom').val('<?= date('Y-m-d') ?>');
|
||||
$('#filterDateTo').val('<?= date('Y-m-d') ?>');
|
||||
$('#filterAction').val('');
|
||||
$('#filterStore').val('');
|
||||
loadTable();
|
||||
});
|
||||
|
||||
$('#btnExport').on('click', function() {
|
||||
var params = '?date_from=' + $('#filterDateFrom').val()
|
||||
+ '&date_to=' + $('#filterDateTo').val()
|
||||
+ '&action=' + $('#filterAction').val()
|
||||
+ '&store_name=' + $('#filterStore').val();
|
||||
window.location.href = base_url + '/action-log/export' + params;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@ -547,9 +547,33 @@ $(document).ready(function() {
|
||||
manageTable = initAvanceTable(base_url + 'avances/fetchAvanceData', adminColumns);
|
||||
});
|
||||
|
||||
var adminCompletedColumns = [
|
||||
{ title: "Client" },
|
||||
{ title: "Téléphone" },
|
||||
{ title: "Adresse" },
|
||||
{ title: "Produit" },
|
||||
{
|
||||
title: "Prix",
|
||||
render: function(data, type, row) {
|
||||
return type === 'display' ? formatNumber(data) : data;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Avance",
|
||||
render: function(data, type, row) {
|
||||
return type === 'display' ? formatNumber(data) : data;
|
||||
}
|
||||
},
|
||||
{ title: "Statut" },
|
||||
{ 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() {
|
||||
$('#table-title').text('Avances Complètes');
|
||||
manageTable = initAvanceTable(base_url + 'avances/fetchAvanceBecameOrder', adminColumns);
|
||||
manageTable = initAvanceTable(base_url + 'avances/fetchAvanceBecameOrder', adminCompletedColumns);
|
||||
});
|
||||
|
||||
$('#avance_expired').on('click', function() {
|
||||
@ -627,21 +651,32 @@ $('#avance_order').on('click', function() {
|
||||
}
|
||||
|
||||
rebuildTableHeaders([
|
||||
'#',
|
||||
'Produit',
|
||||
'Avance',
|
||||
'Reste à payer',
|
||||
'#',
|
||||
'Produit',
|
||||
'Avance',
|
||||
'Statut',
|
||||
'Date',
|
||||
'Action'
|
||||
]);
|
||||
|
||||
|
||||
var completedUserColumns = [
|
||||
{ title: "#" },
|
||||
{ title: "Produit" },
|
||||
{ title: "Avance" },
|
||||
{ title: "Statut" },
|
||||
{ title: "Date" }
|
||||
<?php if (in_array('updateAvance', $user_permission) || in_array('deleteAvance', $user_permission)): ?>
|
||||
,{ title: "Action", orderable: false, searchable: false }
|
||||
<?php endif; ?>
|
||||
];
|
||||
|
||||
manageTable = $('#avanceTable').DataTable({
|
||||
ajax: {
|
||||
url: base_url + 'avances/fetchAvanceBecameOrder',
|
||||
type: 'GET',
|
||||
dataSrc: 'data'
|
||||
},
|
||||
columns: userColumns,
|
||||
columns: completedUserColumns,
|
||||
language: datatableLangFr
|
||||
});
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Gérer les informations sur l'entreprise</h3>
|
||||
</div>
|
||||
<form role="form" action="<?php base_url('company/update') ?>" method="post">
|
||||
<form role="form" action="<?php base_url('company/update') ?>" method="post" enctype="multipart/form-data">
|
||||
<div class="box-body">
|
||||
|
||||
<!-- Validation errors -->
|
||||
@ -48,6 +48,19 @@
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Logo de l'entreprise</label>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<?php if (!empty($company_data['logo'])): ?>
|
||||
<img src="<?= base_url($company_data['logo']) ?>?v=<?= time() ?>" alt="Logo" style="max-height: 80px; border: 1px solid #ddd; padding: 5px; border-radius: 4px;">
|
||||
<?php else: ?>
|
||||
<img src="<?= base_url('assets/images/company_logo.jpg') ?>?v=<?= time() ?>" alt="Logo par defaut" style="max-height: 80px; border: 1px solid #ddd; padding: 5px; border-radius: 4px; opacity: 0.4;">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<input type="file" name="logo" accept="image/*" class="form-control" style="line-height: normal; padding: 6px 12px; height: auto;">
|
||||
<p class="help-block">Formats acceptes : JPG, PNG, GIF. Laissez vide pour garder le logo actuel.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="company_name">Nom de l'entreprise</label>
|
||||
<input type="text" class="form-control" id="company_name" name="company_name" placeholder="Nom de l'entreprise" value="<?php echo $company_data['company_name'] ?>" autocomplete="off">
|
||||
|
||||
@ -311,6 +311,13 @@
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-calculator"></i> Résumé de Trésorerie
|
||||
</h3>
|
||||
<div class="pull-right" style="display: flex; align-items: center; gap: 8px;">
|
||||
<input type="date" id="tresoDateFrom" class="form-control input-sm" value="<?= date('Y-m-d') ?>" style="width: 140px; display: inline-block;">
|
||||
<span>au</span>
|
||||
<input type="date" id="tresoDateTo" class="form-control input-sm" value="<?= date('Y-m-d') ?>" style="width: 140px; display: inline-block;">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="btnFilterTreso"><i class="fa fa-search"></i> Filtrer</button>
|
||||
<button type="button" class="btn btn-warning btn-sm" id="btnResetTreso"><i class="fa fa-refresh"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-body">
|
||||
@ -320,7 +327,7 @@
|
||||
<div class="col-sm-3 col-xs-12">
|
||||
<div class="amount-block brut">
|
||||
<span class="amount-label" style="color: #388e3c;">💰 Total Brut</span>
|
||||
<div class="amount-header" style="color: #2e7d32;">
|
||||
<div class="amount-header" style="color: #2e7d32;" id="treso-brut">
|
||||
<?php echo number_format($total_brut, 0, '.', ' '); ?> Ar
|
||||
</div>
|
||||
<span class="amount-sublabel">Orders + Avances</span>
|
||||
@ -336,7 +343,7 @@
|
||||
<div class="col-sm-3 col-xs-12">
|
||||
<div class="amount-block sorties">
|
||||
<span class="amount-label" style="color: #c62828;">💸 Décaissements</span>
|
||||
<div class="amount-header" style="color: #d32f2f;">
|
||||
<div class="amount-header" style="color: #d32f2f;" id="treso-sorties">
|
||||
<?php echo number_format($total_sorties, 0, '.', ' '); ?> Ar
|
||||
</div>
|
||||
<span class="amount-sublabel">Espèce + MVOLA + Virement</span>
|
||||
@ -352,7 +359,7 @@
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<div class="amount-block net">
|
||||
<span class="amount-label" style="color: #1565c0;">✅ Solde Net</span>
|
||||
<div class="amount-header" style="color: #1976d2; font-size: 36px;">
|
||||
<div class="amount-header" style="color: #1976d2; font-size: 36px;" id="treso-net">
|
||||
<?php echo number_format($total_caisse, 0, '.', ' '); ?> Ar
|
||||
</div>
|
||||
<span class="amount-sublabel">Disponible en caisse</span>
|
||||
@ -371,10 +378,10 @@
|
||||
<div class="payment-icon espece">
|
||||
<i class="fa fa-money"></i>
|
||||
</div>
|
||||
<div class="payment-amount">
|
||||
<div class="payment-amount" id="treso-espece">
|
||||
<?php echo number_format($total_espece_caisse, 0, '.', ' '); ?> Ar
|
||||
</div>
|
||||
<div class="payment-label">💵 Espèce Disponible</div>
|
||||
<div class="payment-label">Espece Disponible</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -384,10 +391,10 @@
|
||||
<div class="payment-icon mvola">
|
||||
<i class="fa fa-mobile"></i>
|
||||
</div>
|
||||
<div class="payment-amount">
|
||||
<div class="payment-amount" id="treso-mvola">
|
||||
<?php echo number_format($total_mvola_caisse, 0, '.', ' '); ?> Ar
|
||||
</div>
|
||||
<div class="payment-label">📱 MVOLA Disponible</div>
|
||||
<div class="payment-label">MVOLA Disponible</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -397,10 +404,10 @@
|
||||
<div class="payment-icon banque">
|
||||
<i class="fa fa-bank"></i>
|
||||
</div>
|
||||
<div class="payment-amount">
|
||||
<div class="payment-amount" id="treso-banque">
|
||||
<?php echo number_format($total_vb_caisse, 0, '.', ' '); ?> Ar
|
||||
</div>
|
||||
<div class="payment-label">🏦 Banque Disponible</div>
|
||||
<div class="payment-label">Banque Disponible</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -408,6 +415,48 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var baseUrl = '<?= base_url() ?>';
|
||||
|
||||
function loadTresorerie() {
|
||||
var startDate = $('#tresoDateFrom').val();
|
||||
var endDate = $('#tresoDateTo').val();
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/dashboard/getTresorerieData',
|
||||
type: 'GET',
|
||||
data: { start_date: startDate, end_date: endDate },
|
||||
success: function(data) {
|
||||
$('#treso-brut').text(data.total_brut + ' Ar');
|
||||
$('#treso-sorties').text(data.total_sorties + ' Ar');
|
||||
$('#treso-net').text(data.total_net + ' Ar');
|
||||
$('#treso-espece').text(data.espece + ' Ar');
|
||||
$('#treso-mvola').text(data.mvola + ' Ar');
|
||||
$('#treso-banque').text(data.banque + ' Ar');
|
||||
$('#avance-mvola').text(data.avance_mvola + ' Ar');
|
||||
$('#avance-espece').text(data.avance_espece + ' Ar');
|
||||
$('#avance-banque').text(data.avance_banque + ' Ar');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Charger par défaut avec la date d'aujourd'hui
|
||||
loadTresorerie();
|
||||
|
||||
$('#btnFilterTreso').on('click', function() {
|
||||
loadTresorerie();
|
||||
});
|
||||
|
||||
$('#btnResetTreso').on('click', function() {
|
||||
$('#tresoDateFrom').val('<?= date('Y-m-d') ?>');
|
||||
$('#tresoDateTo').val('<?= date('Y-m-d') ?>');
|
||||
loadTresorerie();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Détail des avances par mode de paiement -->
|
||||
<div class="container-fluid row" style="margin-top: 10px; margin-bottom: 20px;">
|
||||
<div class="col-lg-12">
|
||||
@ -419,19 +468,19 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header"><?php echo number_format($total_avances_mvola, 0, '.', ' '); ?> Ar</h5>
|
||||
<h5 class="description-header" id="avance-mvola"><?php echo number_format($total_avances_mvola, 0, '.', ' '); ?> Ar</h5>
|
||||
<span class="description-text">MVOLA</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header"><?php echo number_format($total_avances_espece, 0, '.', ' '); ?> Ar</h5>
|
||||
<span class="description-text">ESPÈCE</span>
|
||||
<h5 class="description-header" id="avance-espece"><?php echo number_format($total_avances_espece, 0, '.', ' '); ?> Ar</h5>
|
||||
<span class="description-text">ESPECE</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header"><?php echo number_format($total_avances_virement, 0, '.', ' '); ?> Ar</h5>
|
||||
<h5 class="description-header" id="avance-banque"><?php echo number_format($total_avances_virement, 0, '.', ' '); ?> Ar</h5>
|
||||
<span class="description-text">BANQUE</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -149,28 +149,27 @@
|
||||
<tr>
|
||||
<tr>
|
||||
<td>Recouvrement</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewRecouvrement" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="createRecouvrement" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="updateRecouvrement" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewRecouvrement" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="deleteRecouvrement" class="minimal"></td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Décaissement</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewSortieCaisse" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="createSortieCaisse" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="updateSortieCaisse" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewSortieCaisse" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="deleteSortieCaisse" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="validateSortieCaisse" class="minimal"></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Remise</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewRemise" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="validateRemise" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="refusedRemise" class="minimal"></td>
|
||||
|
||||
<td>-</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="validateRemise" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewRemise" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="refusedRemise" class="minimal"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Validation commande</td>
|
||||
@ -181,11 +180,11 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Securité</td>
|
||||
<td> - </td>
|
||||
<td> <input type="checkbox" name="permission[]" id="permission" value="viewSecurite" class="minimal"> </td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="createSecurite" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="updateSecurite" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewSecurite" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="deleteSecurite" class="minimal"></td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rapports</td>
|
||||
@ -197,11 +196,11 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Avances</td>
|
||||
<td> - </td>
|
||||
<td> <input type="checkbox" name="permission[]" id="permission" value="viewAvance" class="minimal"> </td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="createAvance" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="updateAvance" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="viewAvance" class="minimal"></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" value="deleteAvance" class="minimal"></td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entreprise</td>
|
||||
|
||||
@ -420,12 +420,6 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Recouvrement</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="viewRecouvrement"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('viewRecouvrement', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="createRecouvrement"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('createRecouvrement', $serialize_permission)) {
|
||||
@ -438,6 +432,12 @@
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="viewRecouvrement"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('viewRecouvrement', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="deleteRecouvrement"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('deleteRecouvrement', $serialize_permission)) {
|
||||
@ -455,12 +455,6 @@
|
||||
|
||||
<tr>
|
||||
<td>Sortie caisse</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="viewSortieCaisse"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('viewSortieCaisse', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="createSortieCaisse"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('createSortieCaisse', $serialize_permission)) {
|
||||
@ -473,6 +467,12 @@
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="viewSortieCaisse"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('viewSortieCaisse', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="deleteSortieCaisse"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('deleteSortieCaisse', $serialize_permission)) {
|
||||
@ -528,18 +528,19 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Remise</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="viewRemise"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('viewRemise', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td>-</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="validateRemise"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('validateRemise', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="viewRemise"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('viewRemise', $serialize_permission)) {
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="refusedRemise"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('refusedRemise', $serialize_permission)) {
|
||||
@ -547,7 +548,6 @@
|
||||
}
|
||||
} ?>></td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="notifRemise"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('notifRemise', $serialize_permission)) {
|
||||
@ -569,6 +569,7 @@
|
||||
echo "checked";
|
||||
}
|
||||
} ?>></td>
|
||||
<td>-</td>
|
||||
<td><input type="checkbox" name="permission[]" id="permission" class="minimal" value="deleteCommande1"
|
||||
<?php if ($serialize_permission) {
|
||||
if (in_array('deleteCommande1', $serialize_permission)) {
|
||||
@ -577,7 +578,6 @@
|
||||
} ?>></td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sécurité</td>
|
||||
|
||||
@ -273,7 +273,30 @@
|
||||
'<i class="glyphicon glyphicon-tag"></i>' +
|
||||
'</button>';
|
||||
|
||||
// Add new row in the table
|
||||
// Gérer la visibilité du bouton "+" selon le type de client
|
||||
function toggleAddRowButton() {
|
||||
var customerType = $("#customer_type").val();
|
||||
var rowCount = $("#product_info_table tbody tr").length;
|
||||
|
||||
if (customerType === 'particulier' && rowCount >= 1) {
|
||||
$("#add_row").hide();
|
||||
} else {
|
||||
$("#add_row").show();
|
||||
}
|
||||
}
|
||||
|
||||
// Écouter le changement de type de client
|
||||
$("#customer_type").on('change', function () {
|
||||
var customerType = $(this).val();
|
||||
if (customerType === 'particulier') {
|
||||
// Supprimer toutes les lignes sauf la première
|
||||
$("#product_info_table tbody tr:gt(0)").remove();
|
||||
subAmount();
|
||||
}
|
||||
toggleAddRowButton();
|
||||
});
|
||||
|
||||
// Add new row in the table
|
||||
$("#add_row").unbind('click').bind('click', function () {
|
||||
var table = $("#product_info_table");
|
||||
var count_table_tbody_tr = $("#product_info_table tbody tr").length;
|
||||
@ -285,7 +308,6 @@
|
||||
dataType: 'json',
|
||||
success: function (response) {
|
||||
|
||||
// console.log(reponse.x);
|
||||
var html = '<tr id="row_' + row_id + '">' +
|
||||
'<td>' +
|
||||
'<select class="form-control select_group product" data-row-id="' + row_id + '" id="product_' + row_id + '" name="product[]" style="width:100%;" onchange="getProductData(' + row_id + ')">' +
|
||||
@ -313,6 +335,9 @@
|
||||
|
||||
$(".product").select2();
|
||||
|
||||
// Cacher le bouton "+" si particulier et déjà 2 lignes
|
||||
toggleAddRowButton();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@ -430,6 +455,7 @@
|
||||
function removeRow(tr_id) {
|
||||
$("#product_info_table tbody tr#row_" + tr_id).remove();
|
||||
subAmount();
|
||||
toggleAddRowButton();
|
||||
}
|
||||
function formatAllNumbers() {
|
||||
// Sélecteur pour tous les inputs que tu veux formater
|
||||
|
||||
@ -365,7 +365,7 @@
|
||||
<input type="hidden" name="vat_charge_rate" value="<?php echo $company_data['vat_charge_value'] ?>" autocomplete="off">
|
||||
|
||||
<a target="__blank" id="Imprimente" href="<?php echo base_url() . 'orders/printDiv/' . $order_data['order']['id'] ?>" class="btn btn-default">Imprimer</a>
|
||||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||
<button type="submit" class="btn btn-primary">Payer</button>
|
||||
<a href="<?php echo base_url('orders/') ?>" class="btn btn-warning">Retour</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -186,6 +186,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ✅ COMMANDES EN ATTENTE DE VALIDATION -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-clock-o"></i> Commandes en attente de livraison
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="pendingTable" class="table table-bordered table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Image</th>
|
||||
<th>UGS</th>
|
||||
<th>Désignation</th>
|
||||
<th>Statut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filtres -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@ -266,6 +293,38 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Modal de validation livraison -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header" style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white;">
|
||||
<button type="button" class="close" data-dismiss="modal" style="color: white;">×</button>
|
||||
<h4 class="modal-title"><i class="fa fa-check-circle"></i> Valider la livraison</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center">
|
||||
<img id="editImage" class="img-responsive img-thumbnail" src="" alt="Produit" style="max-height: 200px;">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<p><strong>Produit :</strong> <span id="editNom"></span></p>
|
||||
<p><strong>UGS :</strong> <span id="editUgs"></span></p>
|
||||
<p><strong>N° Facture :</strong> <span id="editBillNo"></span></p>
|
||||
<p><strong>Client :</strong> <span id="editClient"></span></p>
|
||||
<p><strong>Adresse :</strong> <span id="editAddress"></span></p>
|
||||
<p><strong>Téléphone :</strong> <span id="editPhone"></span></p>
|
||||
<p><strong>CIN :</strong> <span id="editCin"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button>
|
||||
<button type="button" class="btn btn-success" id="btnValidate"><i class="fa fa-check"></i> Confirmer la livraison</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Détails -->
|
||||
<div class="modal fade" id="detailsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
@ -361,7 +420,95 @@
|
||||
<script>
|
||||
$(function() {
|
||||
$("#securiteNav").addClass('active');
|
||||
|
||||
|
||||
// ✅ TABLEAU DES COMMANDES EN ATTENTE
|
||||
var pendingTable = $('#pendingTable').DataTable({
|
||||
ajax: {
|
||||
url: '<?= base_url('validateSecurite/fetchSecuriteData') ?>',
|
||||
type: 'GET',
|
||||
dataSrc: 'data'
|
||||
},
|
||||
columns: [
|
||||
{ data: 'image', orderable: false },
|
||||
{ data: 'ugs' },
|
||||
{ data: 'designation' },
|
||||
{ data: 'statut' },
|
||||
{ data: 'action', orderable: false }
|
||||
],
|
||||
order: [],
|
||||
language: {
|
||||
sProcessing: "Traitement en cours...",
|
||||
sSearch: "Rechercher :",
|
||||
sZeroRecords: "Aucune commande en attente",
|
||||
sEmptyTable: "Aucune commande en attente de livraison",
|
||||
sInfo: "Affichage de _START_ à _END_ sur _TOTAL_ éléments",
|
||||
sInfoEmpty: "Aucun élément",
|
||||
oPaginate: { sPrevious: "Précédent", sNext: "Suivant" }
|
||||
}
|
||||
});
|
||||
|
||||
// Rafraîchir les commandes en attente toutes les 10 secondes
|
||||
setInterval(function() { pendingTable.ajax.reload(null, false); }, 10000);
|
||||
|
||||
// ✅ GESTION DE LA VALIDATION
|
||||
var currentSecuriteId = null;
|
||||
|
||||
window.editFunc = function(id) {
|
||||
currentSecuriteId = id;
|
||||
$.ajax({
|
||||
url: '<?= base_url('validateSecurite/fetchSecuriteDataById') ?>/' + id,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
$('#editImage').attr('src', response.image);
|
||||
$('#editNom').text(response.nom);
|
||||
$('#editUgs').text(response.ugs);
|
||||
$('#editBillNo').text(response.bill_no);
|
||||
$('#editClient').text(response.customer_name);
|
||||
$('#editAddress').text(response.customer_address);
|
||||
$('#editPhone').text(response.customer_phone);
|
||||
$('#editCin').text(response.customer_cin);
|
||||
$('#editModal').modal('show');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#btnValidate').on('click', function() {
|
||||
if (!currentSecuriteId) return;
|
||||
var btn = $(this);
|
||||
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Validation...');
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('validateSecurite/update') ?>/' + currentSecuriteId,
|
||||
type: 'POST',
|
||||
data: { status: 'Validé' },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
$('#editModal').modal('hide');
|
||||
btn.prop('disabled', false).html('<i class="fa fa-check"></i> Confirmer la livraison');
|
||||
|
||||
if (response.success) {
|
||||
// Aussi marquer comme livré côté commande
|
||||
pendingTable.ajax.reload(null, false);
|
||||
if (typeof historyTable !== 'undefined') historyTable.ajax.reload(null, false);
|
||||
|
||||
$('#messages').html('<div class="alert alert-success alert-dismissible">' +
|
||||
'<button type="button" class="close" data-dismiss="alert">×</button>' +
|
||||
'<i class="fa fa-check"></i> ' + response.messages + '</div>');
|
||||
setTimeout(function() { $('#messages').fadeOut('slow', function() { $(this).html('').show(); }); }, 3000);
|
||||
} else {
|
||||
$('#messages').html('<div class="alert alert-danger alert-dismissible">' +
|
||||
'<button type="button" class="close" data-dismiss="alert">×</button>' +
|
||||
response.messages + '</div>');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
btn.prop('disabled', false).html('<i class="fa fa-check"></i> Confirmer la livraison');
|
||||
$('#editModal').modal('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Configuration DataTable en français
|
||||
$.extend(true, $.fn.dataTable.defaults, {
|
||||
language: {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div>
|
||||
<strong>Copyright ©<?php echo date('Y') ?>.</strong> All rights reserved.
|
||||
</div>
|
||||
<div class="pull-right hidden-xs"><h5 style="font-family: tahoma;">Designed and Managed by <strong>MYDEVUP</strong>
|
||||
<div class="pull-right hidden-xs"><h5 style="font-family: tahoma;">Designed and Managed by <strong>COMPANY FOR MADAGASCAR</strong>
|
||||
</footer>
|
||||
|
||||
<!-- Add the sidebar's background. This div must be placed
|
||||
@ -17,26 +17,87 @@ function toggleSidebar() {
|
||||
document.body.classList.toggle('sidebar-collapsed');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const treeviews = document.querySelectorAll('.sidebar-menu .treeview');
|
||||
$(document).ready(function() {
|
||||
// Supprimer TOUS les handlers AdminLTE sur le menu
|
||||
$('.sidebar-menu').off();
|
||||
$(document).off('click', '.sidebar-menu li a');
|
||||
$(document).off('click', '.treeview > a');
|
||||
|
||||
treeviews.forEach(item => {
|
||||
// Quand la souris entre dans un menu
|
||||
item.addEventListener('mouseenter', function () {
|
||||
if (document.body.classList.contains('sidebar-collapsed')) {
|
||||
this.classList.add('menu-open');
|
||||
}
|
||||
});
|
||||
// Aussi tenter de détruire le widget tree AdminLTE
|
||||
try { $('.sidebar-menu').tree('destroy'); } catch(e) {}
|
||||
|
||||
// Quand la souris sort du menu
|
||||
item.addEventListener('mouseleave', function () {
|
||||
if (document.body.classList.contains('sidebar-collapsed')) {
|
||||
this.classList.remove('menu-open');
|
||||
// Re-supprimer après un court délai (AdminLTE peut rebind)
|
||||
setTimeout(function() {
|
||||
$('.sidebar-menu').off();
|
||||
bindSidebarMenu();
|
||||
}, 100);
|
||||
|
||||
function bindSidebarMenu() {
|
||||
// Clic sur un treeview
|
||||
$('.sidebar-menu .treeview > a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
var $li = $(this).parent();
|
||||
var menuId = $li.attr('id') || $('.sidebar-menu .treeview').index($li);
|
||||
|
||||
// Fermer les autres
|
||||
$('.sidebar-menu .treeview').not($li).removeClass('menu-open')
|
||||
.children('.treeview-menu').slideUp(200);
|
||||
|
||||
// Toggle
|
||||
if ($li.hasClass('menu-open')) {
|
||||
$li.removeClass('menu-open');
|
||||
$li.children('.treeview-menu').slideUp(200);
|
||||
localStorage.removeItem('openMenu');
|
||||
} else {
|
||||
$li.addClass('menu-open');
|
||||
$li.children('.treeview-menu').slideDown(200);
|
||||
localStorage.setItem('openMenu', String(menuId));
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Restaurer le menu ouvert au survol de la sidebar
|
||||
var restored = false;
|
||||
$('.main-sidebar').on('mouseenter', function() {
|
||||
if (!restored && $('body').hasClass('sidebar-collapsed')) {
|
||||
var openMenu = localStorage.getItem('openMenu');
|
||||
if (openMenu !== null) {
|
||||
var $target = $('#' + openMenu);
|
||||
if (!$target.length) {
|
||||
$target = $('.sidebar-menu .treeview').eq(parseInt(openMenu));
|
||||
}
|
||||
if ($target.length) {
|
||||
$target.addClass('menu-open');
|
||||
$target.children('.treeview-menu').slideDown(200);
|
||||
}
|
||||
}
|
||||
restored = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Quand la souris quitte : fermer visuellement mais garder en mémoire
|
||||
var closeTimer = null;
|
||||
$('.main-sidebar').on('mouseleave', function() {
|
||||
if ($('body').hasClass('sidebar-collapsed')) {
|
||||
closeTimer = setTimeout(function() {
|
||||
$('.sidebar-menu .treeview').removeClass('menu-open');
|
||||
$('.sidebar-menu .treeview-menu').slideUp(200);
|
||||
restored = false;
|
||||
}, 400);
|
||||
}
|
||||
});
|
||||
$('.main-sidebar').on('mouseenter', function() {
|
||||
if (closeTimer) {
|
||||
clearTimeout(closeTimer);
|
||||
closeTimer = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@ -5,6 +5,13 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title><?php echo $page_title; ?></title>
|
||||
<?php if (!empty($company_logo)): ?>
|
||||
<link rel="icon" type="image/x-icon" href="<?= base_url($company_logo) ?>?v=<?= time() ?>">
|
||||
<link rel="shortcut icon" href="<?= base_url($company_logo) ?>?v=<?= time() ?>">
|
||||
<?php else: ?>
|
||||
<link rel="icon" type="image/x-icon" href="<?= base_url('assets/images/company_logo.jpg') ?>?v=<?= time() ?>">
|
||||
<link rel="shortcut icon" href="<?= base_url('assets/images/company_logo.jpg') ?>?v=<?= time() ?>">
|
||||
<?php endif; ?>
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
@ -118,7 +125,7 @@
|
||||
|
||||
</head>
|
||||
|
||||
<body class="hold-transition skin-blue sidebar-mini">
|
||||
<body class="hold-transition skin-blue sidebar-mini sidebar-collapsed">
|
||||
<div class="wrapper">
|
||||
<style>
|
||||
/* ============================================
|
||||
@ -127,6 +134,26 @@
|
||||
/* ============================================
|
||||
VARIABLES CSS - Design System Couleurs Unies
|
||||
============================================ */
|
||||
/* Fix select dropdowns */
|
||||
select.form-control {
|
||||
width: 100% !important;
|
||||
min-width: 0;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-appearance: menulist;
|
||||
-moz-appearance: menulist;
|
||||
appearance: menulist;
|
||||
padding-right: 30px !important;
|
||||
color: #333 !important;
|
||||
background-color: #fff !important;
|
||||
height: auto !important;
|
||||
min-height: 40px;
|
||||
}
|
||||
select.form-control option {
|
||||
color: #333;
|
||||
background: #fff;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--success-color: #2ecc71;
|
||||
@ -146,13 +173,12 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Inter', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Source Sans Pro', sans-serif !important;
|
||||
overflow-x: hidden;
|
||||
min-height: 100vh;
|
||||
background: #3498db !important;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin-top:30px;
|
||||
margin-top:50px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
@ -165,14 +191,14 @@ body {
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 60px;
|
||||
overflow-x: auto;
|
||||
margin-left: 230px;
|
||||
padding-top: 50px;
|
||||
transition: margin-left 0.3s ease;
|
||||
width: calc(100% - 230px);
|
||||
transition: margin-left 0.3s ease, width 0.3s ease;
|
||||
background: #ecf0f1 !important;
|
||||
box-shadow: -5px 0 20px rgba(0, 0, 0, 0.1);
|
||||
min-height: 100vh;
|
||||
padding: 30px !important;
|
||||
padding: 75px 30px 80px 30px !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
@ -188,6 +214,8 @@ body {
|
||||
height: 50px;
|
||||
transition: margin-left 0.3s ease;
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
@ -240,21 +268,30 @@ body {
|
||||
============================================ */
|
||||
.main-sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
z-index: 810;
|
||||
width: 230px;
|
||||
background: #2c3e50 !important;
|
||||
transition: width 0.3s ease;
|
||||
box-shadow: 2px 0 20px rgba(0, 0, 0, 0.3);
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.main-sidebar .logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.logo {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
flex-shrink: 0;
|
||||
width: 230px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 15px;
|
||||
text-align: center;
|
||||
background: #3498db !important;
|
||||
color: white;
|
||||
@ -262,8 +299,11 @@ body {
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
transition: padding 0.3s ease;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
transition: width 0.3s ease, padding 0.3s ease;
|
||||
}
|
||||
|
||||
.main-header .navbar {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.logo-mini {
|
||||
@ -696,7 +736,7 @@ body {
|
||||
left: 230px;
|
||||
margin-left: 2px;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
z-index: 800;
|
||||
background: #ffffff !important;
|
||||
padding: 12px 20px;
|
||||
border-top: 2px solid #3498db;
|
||||
@ -713,20 +753,30 @@ body {
|
||||
============================================ */
|
||||
.sidebar-toggle {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 230px;
|
||||
height: 50px;
|
||||
line-height: 30px;
|
||||
z-index: 2000;
|
||||
background: #3c8dbc;
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 3px;
|
||||
padding: 0 15px;
|
||||
cursor: pointer;
|
||||
transition: left 0.3s ease;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-toggle {
|
||||
left: 70px;
|
||||
}
|
||||
|
||||
.sidebar-toggle::before {
|
||||
content: "\f0c9";
|
||||
font-family: FontAwesome;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
NOTIFICATIONS - Badge modernisé
|
||||
============================================ */
|
||||
@ -791,16 +841,69 @@ body {
|
||||
.sidebar-collapsed .main-sidebar {
|
||||
width: 70px;
|
||||
overflow-y: auto;
|
||||
overflow-x: visible;
|
||||
overflow-x: hidden;
|
||||
direction: rtl;
|
||||
transition: width 0.35s ease;
|
||||
}
|
||||
|
||||
/* Ouvrir la sidebar au survol */
|
||||
.sidebar-collapsed .main-sidebar:hover {
|
||||
width: 230px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .sidebar-menu > li > a > span {
|
||||
opacity: 1;
|
||||
max-width: 180px;
|
||||
white-space: nowrap;
|
||||
transition: opacity 0.2s ease 0.15s, max-width 0.35s ease;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .sidebar-menu > li > a .pull-right-container {
|
||||
opacity: 1;
|
||||
max-width: 20px;
|
||||
transition: opacity 0.2s ease 0.15s, max-width 0.35s ease;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .sidebar-menu > li > a {
|
||||
text-align: left;
|
||||
padding: 12px 15px;
|
||||
width: auto;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .sidebar-menu i {
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .logo {
|
||||
width: 230px;
|
||||
padding: 15px;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .logo-mini {
|
||||
display: none !important;
|
||||
opacity: 0;
|
||||
max-width: 0;
|
||||
}
|
||||
.sidebar-collapsed .main-sidebar:hover .logo-lg {
|
||||
display: inline !important;
|
||||
opacity: 1 !important;
|
||||
max-width: 200px !important;
|
||||
overflow: visible !important;
|
||||
transition: opacity 0.25s ease 0.15s, max-width 0.35s ease;
|
||||
}
|
||||
/* Sous-menus gérés plus bas */
|
||||
|
||||
.sidebar-collapsed .main-sidebar .sidebar-menu {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .content-wrapper {
|
||||
margin-left: 70px;
|
||||
width: calc(100% - 70px);
|
||||
}
|
||||
|
||||
.sidebar-collapsed .main-sidebar:hover ~ .content-wrapper {
|
||||
margin-left: 230px;
|
||||
width: calc(100% - 230px);
|
||||
transition: margin-left 0.35s ease, width 0.35s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .main-header {
|
||||
@ -813,11 +916,35 @@ body {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .main-sidebar:hover ~ .main-footer {
|
||||
left: 230px;
|
||||
transition: left 0.35s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-toggle {
|
||||
left: 70px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .logo {
|
||||
.sidebar-collapsed .main-header .logo {
|
||||
width: 70px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .main-header .logo-mini {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .main-header .logo-lg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.logo-mini img {
|
||||
max-height: 40px;
|
||||
max-width: 50px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .main-sidebar .logo {
|
||||
padding: 15px 5px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
@ -827,22 +954,37 @@ body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .logo-mini {
|
||||
.sidebar-collapsed .main-sidebar .logo-mini {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
max-width: 60px;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.25s ease 0.1s, max-width 0.35s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .logo-lg {
|
||||
display: none;
|
||||
.sidebar-collapsed .main-sidebar .logo-lg {
|
||||
opacity: 0;
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
transition: opacity 0.25s ease, max-width 0.35s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu > li > a > span {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
transition: opacity 0.2s ease, max-width 0.35s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu > li > a .pull-right-container {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.2s ease, max-width 0.35s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu > li > a {
|
||||
@ -854,7 +996,17 @@ body {
|
||||
justify-content: center;
|
||||
width: 70px;
|
||||
box-sizing: border-box;
|
||||
margin: 5px auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu > li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu > li.treeview > .treeview-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu i {
|
||||
@ -865,114 +1017,31 @@ body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Pas de tooltip en mode réduit */
|
||||
.sidebar-collapsed .sidebar-menu > li > a::before {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
left: 70px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: #3498db;
|
||||
color: #fff;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 9998;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
font-weight: 600;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .sidebar-menu > li:hover > a::before {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
left: 80px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu {
|
||||
position: fixed;
|
||||
left: 80px;
|
||||
top: auto;
|
||||
width: 200px;
|
||||
max-width: calc(100vw - 100px);
|
||||
z-index: 9999;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 4px;
|
||||
border-left: 2px solid #3c8dbc;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
|
||||
display: block !important;
|
||||
background: #fff;
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(210, 214, 222, 0.8);
|
||||
pointer-events: none;
|
||||
transform: translateX(-20px) scale(0.95);
|
||||
transform-origin: left center;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview:hover .treeview-menu {
|
||||
pointer-events: auto !important;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu:hover {
|
||||
/* Sidebar hover ouverte : sous-menus par clic (jQuery gère display) */
|
||||
.sidebar-collapsed .main-sidebar:hover .treeview-menu {
|
||||
position: static !important;
|
||||
width: auto !important;
|
||||
background: rgba(0,0,0,0.15) !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
transform: none !important;
|
||||
pointer-events: auto !important;
|
||||
transform: translateX(0) scale(1) !important;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu > li > a {
|
||||
padding: 8px 12px;
|
||||
color: #495057;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid rgba(210, 214, 222, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
.sidebar-collapsed .main-sidebar:hover .treeview-menu > li > a {
|
||||
padding: 8px 15px 8px 30px;
|
||||
color: #b8c7ce;
|
||||
display: block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
pointer-events: auto !important;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu > li:last-child > a {
|
||||
border-bottom: none;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu > li:first-child > a {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu > li > a:hover {
|
||||
background: #3c8dbc;
|
||||
.sidebar-collapsed .main-sidebar:hover .treeview-menu > li > a:hover {
|
||||
color: #fff;
|
||||
padding-left: 15px;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu > li > a i {
|
||||
margin-right: 6px;
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
opacity: 0.7;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-collapsed .treeview-menu > li > a:hover i {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
@ -1167,12 +1236,7 @@ body {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
/* Treeview menu sur mobile */
|
||||
.sidebar-collapsed .treeview-menu {
|
||||
left: 70px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
|
||||
/* Charts containers */
|
||||
.col-lg-6[style*="background-color: white"] {
|
||||
padding: 15px !important;
|
||||
@ -1563,9 +1627,7 @@ body {
|
||||
.small-box:nth-child(4) { animation-delay: 0.4s; }
|
||||
.small-box:nth-child(5) { animation-delay: 0.5s; }
|
||||
|
||||
.sidebar-collapsed .treeview:hover .treeview-menu {
|
||||
animation: slideInFromSidebar 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
/* animation removed - using click-based submenus */
|
||||
|
||||
/* Désactiver les animations sur mobile pour de meilleures performances */
|
||||
@media (max-width: 768px) {
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
<header class="main-header">
|
||||
<!-- Logo -->
|
||||
<a href="#" class="logo">
|
||||
<span class="logo-mini"><b><img src="<?= base_url('assets/images/company_logo.jpg') ?>"></b></span>
|
||||
<span class="logo-lg"><b>MotorBike</b></span>
|
||||
<span class="logo-mini"><b><?php if (!empty($company_logo)): ?><img src="<?= base_url($company_logo) ?>?v=<?= time() ?>"><?php else: ?><img src="<?= base_url('assets/images/company_logo.jpg') ?>?v=<?= time() ?>" style="opacity:0.4;"><?php endif; ?></b></span>
|
||||
<span class="logo-lg"><b><?= $company_name ?? 'MotorBike' ?></b></span>
|
||||
</a>
|
||||
|
||||
<!-- Header Navbar -->
|
||||
<nav class="navbar navbar-static-top" style="position: relative; height: 50px; background: #3c8dbc;">
|
||||
<!-- Sidebar toggle button -->
|
||||
<a href="#" class="sidebar-toggle" onclick="toggleSidebar()">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
</a>
|
||||
<a href="#" class="sidebar-toggle" onclick="toggleSidebar()"></a>
|
||||
|
||||
<!-- Navbar Right Menu -->
|
||||
<ul class="navbar-nav" style="position: absolute; right: 15px; top: 50%; transform: translateY(-50%); margin: 0; padding: 0; list-style: none; display: flex; align-items: center; gap: 20px;">
|
||||
|
||||
@ -273,7 +273,6 @@
|
||||
<li id="reportNav">
|
||||
<a href="<?php echo base_url('reports/') ?>">
|
||||
<i class="glyphicon glyphicon-stats"></i> <span>Rapports</span>
|
||||
<span class="tooltip">Rapports</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
@ -286,6 +285,18 @@
|
||||
</li>
|
||||
<?php endif; ?> -->
|
||||
|
||||
<?php
|
||||
$session = session();
|
||||
$currentUser = $session->get('user');
|
||||
if (isset($currentUser['group_name']) && $currentUser['group_name'] === 'SuperAdmin'):
|
||||
?>
|
||||
<li id="mainActionLogNav">
|
||||
<a href="<?php echo base_url('action-log') ?>">
|
||||
<i class="fa fa-history"></i> <span>Historique des Actions</span>
|
||||
</a>
|
||||
</li>
|
||||
<?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>
|
||||
<?php endif; ?>
|
||||
|
||||
@ -53,9 +53,7 @@
|
||||
<th>Phone</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; ?>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user