Sarobidy22 1 month ago
parent
commit
10f3dae939
  1. 66
      app/Controllers/AvanceController.php
  2. 4
      app/Controllers/Dashboard.php
  3. 2
      app/Controllers/MecanicienController.php
  4. 558
      app/Controllers/OrderController.php
  5. 171
      app/Controllers/RecouvrementController.php
  6. 131
      app/Controllers/RemiseController.php
  7. 2
      app/Controllers/ReportController.php
  8. 341
      app/Controllers/SortieCaisseController.php
  9. 2
      app/Controllers/StatistiqueController.php
  10. 95
      app/Models/Avance.php
  11. 2
      app/Models/Mecanicien.php
  12. 8
      app/Models/Orders.php
  13. 4
      app/Models/Products.php
  14. 4
      app/Models/Recouvrement.php
  15. 28
      app/Models/Remise.php
  16. 2
      app/Models/Securite.php
  17. 293
      app/Models/SortieCaisse.php
  18. 8
      app/Views/avances/avance.php
  19. 2
      app/Views/dashboard.php
  20. 137
      app/Views/login.php
  21. 4
      app/Views/orders/avance.php
  22. 2
      app/Views/orders/edit.php
  23. 84
      app/Views/orders/index.php
  24. 144
      app/Views/reports/index.php
  25. 16
      app/Views/sortieCaisse/index.php
  26. 241
      app/Views/statistic/index.php
  27. 1
      app/Views/templates/header.php

66
app/Controllers/AvanceController.php

@ -31,7 +31,7 @@ class AvanceController extends AdminController
private function isAdmin($user)
{
return in_array($user['group_name'], ['Conseil', 'Direction']);
return in_array($user['group_name'], ['SuperAdmin','DAF','Direction']);
}
private function isCommerciale($user)
@ -50,7 +50,7 @@ private function buildActionButtons($value, $isAdmin, $isOwner, $isCaissier = fa
{
$session = session();
$users = $session->get('user');
$isDirection = in_array($users['group_name'], ['Direction', 'Conseil']);
$isDirection = in_array($users['group_name'], ['Direction', 'SuperAdmin','DAF']);
$buttons = '';
@ -468,15 +468,49 @@ public function fetchExpiredAvance()
$Products->update($data['product_id'], ['product_sold' => 1]);
}
// ✅ NOUVELLE FONCTIONNALITÉ : Envoyer notification au Conseil
$Notification->createNotification(
'Une nouvelle avance a été créée',
"DAF",
(int)$users['store_id'],
'avances'
);
// ✅ MODIFICATION PRINCIPALE : Envoyer notifications à TOUS les stores
$db = \Config\Database::connect();
// Récupérer tous les stores
$storesQuery = $db->table('stores')->select('id')->get();
$allStores = $storesQuery->getResultArray();
$customerName = $this->request->getPost('customer_name_avance');
$avanceAmount = number_format((float)$this->request->getPost('avance_amount'), 0, ',', ' ');
$typeAvanceLabel = $type_avance === 'terre' ? 'SUR TERRE' : 'SUR MER';
$notificationMessage = "Nouvelle avance {$typeAvanceLabel} créée par {$users['firstname']} {$users['lastname']} - Client: {$customerName} - Montant: {$avanceAmount} Ar";
// ✅ Envoyer notification à DAF, Direction et SuperAdmin de TOUS les stores
foreach ($allStores as $store) {
$storeId = (int)$store['id'];
// Notification pour DAF
$Notification->createNotification(
$notificationMessage,
"DAF",
$storeId,
'avances'
);
// Notification pour Direction
$Notification->createNotification(
$notificationMessage,
"Direction",
$storeId,
'avances'
);
// Notification pour SuperAdmin
$Notification->createNotification(
$notificationMessage,
"SuperAdmin",
$storeId,
'avances'
);
}
// ✅ NOUVELLE FONCTIONNALITÉ : Envoyer notification à la Caissière si l'utilisateur est COMMERCIALE
// ✅ Notification à la Caissière UNIQUEMENT du store concerné (si l'utilisateur est COMMERCIALE)
if ($this->isCommerciale($users)) {
$Notification->createNotification(
'Une nouvelle avance a été créée par un commercial',
@ -486,6 +520,8 @@ public function fetchExpiredAvance()
);
}
log_message('info', "✅ Avance {$avance_id} créée - Notifications envoyées à DAF/Direction/SuperAdmin de tous les stores");
return $this->response->setJSON([
'success' => true,
'messages' => 'Avance créée avec succès !',
@ -505,7 +541,6 @@ public function fetchExpiredAvance()
]);
}
}
public function updateAvance()
{
@ -1139,7 +1174,7 @@ public function getInvoicePreview($avance_id)
$users = $session->get('user');
$isCaissier = $this->isCaissier($users);
$isDirection = in_array($users['group_name'], ['Direction', 'Conseil']);
$isDirection = in_array($users['group_name'], ['Direction', 'SuperAdmin','DAF']);
if (!$isCaissier && !$isDirection) {
return $this->response->setJSON([
@ -1500,6 +1535,13 @@ public function notifyPrintInvoice()
(int)$users['store_id'],
'avances'
);
$Notification->createNotification(
"Il y a une avance N°{$avanceNumber} pour le client {$customerName}",
"SuperAdmin",
(int)$users['store_id'],
'avances'
);
return $this->response->setJSON([
'success' => true,

4
app/Controllers/Dashboard.php

@ -136,7 +136,7 @@ class Dashboard extends AdminController
$data['total_products'] = $productModel->countProductsByUserStore();
// === ✅ Récupérer le nom du store pour l'affichage ===
$isAdmin = in_array($user_id['group_name'], ['DAF', 'Direction']);
$isAdmin = in_array($user_id['group_name'], ['DAF', 'Direction','SuperAdmin']);
if (!$isAdmin && !empty($user_id['store_id']) && $user_id['store_id'] != 0) {
$store = $storeModel->getStoresData($user_id['store_id']);
@ -219,7 +219,7 @@ class Dashboard extends AdminController
$data['isMecanicien'] = false;
$data['isSecurite'] = false;
if ($user_id['group_name'] == "Direction" || $user_id['group_name'] == "DAF") {
if ($user_id['group_name'] == "Direction" || $user_id['group_name'] == "DAF" || $user_id['group_name'] == "SuperAdmin") {
$data['is_admin'] = true;
}

2
app/Controllers/MecanicienController.php

@ -319,7 +319,7 @@ class MecanicienController extends AdminController
// Iterate through the data
if($users['group_name'] == "Conseil" || $users['group_name'] == "Direction"){
if($users['group_name'] == "SuperAdmin" || $users['group_name'] == "Direction"){
foreach ($reparation as $key => $repa) {
$image = '<img src="' . base_url('assets/images/product_image/' . $repa['image']) . '" alt="' . $repa['name'] . '" class="img-circle" width="50" height="50" />';
$produit = esc($repa['name']);

558
app/Controllers/OrderController.php

@ -107,11 +107,11 @@ class OrderController extends AdminController
// Statut de paiement
if ($value['paid_status'] == 1) {
$paid_status = '<span class="label label-success">Validé</span>';
$paid_status = '<span class="label label-success">payé</span>';
} elseif ($value['paid_status'] == 2) {
$paid_status = '<span class="label label-warning">En Attente</span>';
} elseif ($value['paid_status'] == 3) {
$paid_status = '<span class="label label-info">Validé et Livré</span>';
$paid_status = '<span class="label label-info">payé et Livré</span>';
} else {
$paid_status = '<span class="label label-danger">Refusé</span>';
}
@ -149,7 +149,7 @@ class OrderController extends AdminController
// ========================================
// POUR DIRECTION OU DAF
// ========================================
elseif($users['group_name'] == "Direction" || $users['group_name'] == "DAF"){
elseif($users['group_name'] == "Direction" || $users['group_name'] == "DAF" || $users['group_name'] == "SuperAdmin" ){
foreach ($data as $key => $value) {
$date_time = date('d-m-Y h:i a', strtotime($value['date_time']));
@ -172,11 +172,11 @@ class OrderController extends AdminController
// Statut de paiement
if ($value['paid_status'] == 1) {
$paid_status = '<span class="label label-success">Validé</span>';
$paid_status = '<span class="label label-success">payé</span>';
} elseif ($value['paid_status'] == 2) {
$paid_status = '<span class="label label-warning">En Attente</span>';
} elseif ($value['paid_status'] == 3) {
$paid_status = '<span class="label label-info">Validé et Livré</span>';
$paid_status = '<span class="label label-info">Payé et Livré</span>';
} else {
$paid_status = '<span class="label label-danger">Refusé</span>';
}
@ -255,11 +255,11 @@ class OrderController extends AdminController
// Statut de paiement
if ($value['paid_status'] == 1) {
$paid_status = '<span class="label label-success">Validé</span>';
$paid_status = '<span class="label label-success">Payé</span>';
} elseif ($value['paid_status'] == 2) {
$paid_status = '<span class="label label-warning">En Attente</span>';
} elseif ($value['paid_status'] == 3) {
$paid_status = '<span class="label label-info">Validé et Livré</span>';
$paid_status = '<span class="label label-info">Payé et Livré</span>';
} else {
$paid_status = '<span class="label label-danger">Refusé</span>';
}
@ -331,211 +331,281 @@ class OrderController extends AdminController
public function create()
{
$this->verifyRole('createOrder');
$data['page_title'] = $this->pageTitle;
$validation = \Config\Services::validation();
$products = $this->request->getPost('product[]');
if ($products !== null && (count($products) !== count(array_unique($products)))) {
return redirect()->back()->withInput()->with('errors', ['product' => 'Chaque produit sélectionné doit être unique.']);
/**
* ✅ AMÉLIORATION : Notifications centralisées pour Direction/DAF/SuperAdmin (tous stores)
*/
public function create()
{
$this->verifyRole('createOrder');
$data['page_title'] = $this->pageTitle;
$validation = \Config\Services::validation();
$products = $this->request->getPost('product[]');
if ($products !== null && (count($products) !== count(array_unique($products)))) {
return redirect()->back()->withInput()->with('errors', ['product' => 'Chaque produit sélectionné doit être unique.']);
}
$validation->setRules([
'product[]' => 'required',
'customer_type' => 'required',
'source' => 'required'
]);
$validationData = [
'product[]' => $this->request->getPost('product[]'),
'customer_type' => $this->request->getPost('customer_type'),
'source' => $this->request->getPost('source')
];
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
if ($this->request->getMethod() === 'post' && $validation->run($validationData)) {
$session = session();
$users = $session->get('user');
$user_id = $users['id'];
$bill_no = $this->generateBillNo($users['store_id']);
$posts = $this->request->getPost('product[]');
$rates = $this->request->getPost('rate_value[]');
$amounts = $this->request->getPost('amount_value[]');
$puissances = $this->request->getPost('puissance[]');
$discount = (float)$this->request->getPost('discount') ?? 0;
$gross_amount = $this->calculGross($amounts);
// Vérification prix minimal SI rabais existe
if ($discount > 0) {
$FourchettePrix = new \App\Models\FourchettePrix();
foreach ($posts as $index => $productId) {
$productId = (int)$productId;
$productData = $Products->getProductData($productId);
$fourchette = $FourchettePrix->getFourchettePrixByProductId($productId);
if ($fourchette) {
$prixMinimal = (float)$fourchette['prix_minimal'];
if ($discount < $prixMinimal) {
$prixMinimalFormatted = number_format($prixMinimal, 0, ',', ' ');
$discountFormatted = number_format($discount, 0, ',', ' ');
return redirect()->back()
->withInput()
->with('errors', [
"⚠️ Commande bloquée : Le rabais de {$discountFormatted} Ar pour « {$productData['name']} » est trop élevé."
]);
}
}
}
}
// ✅ AJOUT DES RÈGLES DE VALIDATION
$validation->setRules([
'product[]' => 'required',
'customer_type' => 'required',
'source' => 'required'
]);
// ✅ AJOUT DES DONNÉES DE VALIDATION
$validationData = [
'product[]' => $this->request->getPost('product[]'),
'customer_type' => $this->request->getPost('customer_type'),
'source' => $this->request->getPost('source')
$montant_a_payer = ($discount > 0) ? $discount : $gross_amount;
$tranche_1 = (float)$this->request->getPost('tranche_1') ?? 0;
$tranche_2 = (float)$this->request->getPost('tranche_2') ?? 0;
if ($tranche_1 > 0 && $tranche_2 > 0) {
$net_amount = $tranche_1 + $tranche_2;
} else {
$net_amount = $montant_a_payer;
}
$data = [
'bill_no' => $bill_no,
'customer_name' => $this->request->getPost('customer_name'),
'customer_address' => $this->request->getPost('customer_address'),
'customer_phone' => $this->request->getPost('customer_phone'),
'customer_cin' => $this->request->getPost('customer_cin'),
'customer_type' => $this->request->getPost('customer_type'),
'source' => $this->request->getPost('source'),
'date_time' => date('Y-m-d H:i:s'),
'service_charge_rate' => 0,
'vat_charge_rate' => 0,
'vat_charge' => 0,
'net_amount' => $net_amount,
'discount' => $discount,
'paid_status' => 2,
'user_id' => $user_id,
'amount_value' => $amounts,
'gross_amount' => $gross_amount,
'rate_value' => $rates,
'puissance' => $puissances,
'store_id' => $users['store_id'],
'tranche_1' => $tranche_1,
'tranche_2' => $tranche_2,
'order_payment_mode' => $this->request->getPost('order_payment_mode_1'),
'order_payment_mode_1' => $this->request->getPost('order_payment_mode_2')
];
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
if ($this->request->getMethod() === 'post' && $validation->run($validationData)) {
$session = session();
$users = $session->get('user');
$user_id = $users['id'];
$bill_no = $this->generateBillNo($users['store_id']);
// Récupération des produits
$posts = $this->request->getPost('product[]');
$rates = $this->request->getPost('rate_value[]');
$amounts = $this->request->getPost('amount_value[]');
$puissances = $this->request->getPost('puissance[]');
$discount = (float)$this->request->getPost('discount') ?? 0;
$gross_amount = $this->calculGross($amounts);
// Vérification prix minimal SI rabais existe
$order_id = $Orders->create($data, $posts);
if ($order_id) {
session()->setFlashdata('success', 'Créé avec succès');
$Notification = new NotificationController();
$Stores = new Stores();
if ($discount > 0) {
$FourchettePrix = new \App\Models\FourchettePrix();
foreach ($posts as $index => $productId) {
$productId = (int)$productId;
$productData = $Products->getProductData($productId);
$fourchette = $FourchettePrix->getFourchettePrixByProductId($productId);
if ($fourchette) {
$prixMinimal = (float)$fourchette['prix_minimal'];
if ($discount < $prixMinimal) {
$prixMinimalFormatted = number_format($prixMinimal, 0, ',', ' ');
$discountFormatted = number_format($discount, 0, ',', ' ');
return redirect()->back()
->withInput()
->with('errors', [
"⚠️ Commande bloquée : Le rabais de {$discountFormatted} Ar pour « {$productData['name']} » est trop élevé."
]);
}
}
// ✅ DEMANDE DE REMISE : NOTIFIER TOUS LES STORES
$Order_item1 = new OrderItems();
$order_item_data = $Order_item1->getOrdersItemData($order_id);
$product_ids = array_column($order_item_data, 'product_id');
$productData = new Products();
$product_data_results = [];
foreach ($product_ids as $prod_id) {
$id = (int) $prod_id;
$product_data_results[] = $productData->getProductData($id);
}
}
// Calculer le montant à payer et net_amount
$montant_a_payer = ($discount > 0) ? $discount : $gross_amount;
$tranche_1 = (float)$this->request->getPost('tranche_1') ?? 0;
$tranche_2 = (float)$this->request->getPost('tranche_2') ?? 0;
if ($tranche_1 > 0 && $tranche_2 > 0) {
$net_amount = $tranche_1 + $tranche_2;
} else {
$net_amount = $montant_a_payer;
}
// ✅ AJOUT DES NOUVEAUX CHAMPS ICI
$data = [
'bill_no' => $bill_no,
'customer_name' => $this->request->getPost('customer_name'),
'customer_address' => $this->request->getPost('customer_address'),
'customer_phone' => $this->request->getPost('customer_phone'),
'customer_cin' => $this->request->getPost('customer_cin'),
'customer_type' => $this->request->getPost('customer_type'), // ✅ NOUVEAU
'source' => $this->request->getPost('source'), // ✅ NOUVEAU
'date_time' => date('Y-m-d H:i:s'),
'service_charge_rate' => 0,
'vat_charge_rate' => 0,
'vat_charge' => 0,
'net_amount' => $net_amount,
'discount' => $discount,
'paid_status' => 2,
'user_id' => $user_id,
'amount_value' => $amounts,
'gross_amount' => $gross_amount,
'rate_value' => $rates,
'puissance' => $puissances,
'store_id' => $users['store_id'],
'tranche_1' => $tranche_1,
'tranche_2' => $tranche_2,
'order_payment_mode' => $this->request->getPost('order_payment_mode_1'),
'order_payment_mode_1' => $this->request->getPost('order_payment_mode_2')
];
$order_id = $Orders->create($data, $posts);
if ($order_id) {
session()->setFlashdata('success', 'Créé avec succès');
$Notification = new NotificationController();
if ($discount > 0) {
// Logique demande de remise...
$Order_item1 = new OrderItems();
$order_item_data = $Order_item1->getOrdersItemData($order_id);
$product_ids = array_column($order_item_data, 'product_id');
$productData = new Products();
$product_data_results = [];
foreach ($product_ids as $prod_id) {
$id = (int) $prod_id;
$product_data_results[] = $productData->getProductData($id);
}
$product_lines = [];
foreach ($product_data_results as $product) {
if (isset($product['sku'], $product['price'])) {
$sku = $product['sku'];
$price = $product['price'];
$product_lines[] = "{$sku}:{$price}";
}
$product_lines = [];
foreach ($product_data_results as $product) {
if (isset($product['sku'], $product['price'])) {
$sku = $product['sku'];
$price = $product['price'];
$product_lines[] = "{$sku}:{$price}";
}
$product_output = implode("\n", $product_lines);
$data1 = [
'date_demande' => date('Y-m-d H:i:s'),
'montant_demande' => $discount,
'total_price' => $amounts,
'id_store' => $users['store_id'],
'id_order' => $order_id,
'product' => $product_output,
'demande_status' => 'En attente'
];
$Remise = new Remise();
$id_remise = $Remise->addDemande($data1);
$Notification->createNotification(
"Nouvelle demande de remise à valider - Commande " . $bill_no,
"Direction",
(int)$users['store_id'],
"remise/"
);
} else {
$Notification->createNotification(
"Nouvelle commande à valider - " . $bill_no,
"Caissière",
(int)$users['store_id'],
"orders"
);
}
if ($users["group_name"] != "COMMERCIALE") {
$this->checkProductisNull($posts, $users['store_id']);
$product_output = implode("\n", $product_lines);
$data1 = [
'date_demande' => date('Y-m-d H:i:s'),
'montant_demande' => $discount,
'total_price' => $amounts,
'id_store' => $users['store_id'],
'id_order' => $order_id,
'product' => $product_output,
'demande_status' => 'En attente'
];
$Remise = new Remise();
$id_remise = $Remise->addDemande($data1);
// ✅ RÉCUPÉRER TOUS LES STORES
$allStores = $Stores->getActiveStore();
$montantFormatted = number_format($discount, 0, ',', ' ');
$message = "💰 Nouvelle demande de remise : {$montantFormatted} Ar<br>" .
"Commande : {$bill_no}<br>" .
"Store : " . $this->returnStore($users['store_id']) . "<br>" .
"Demandeur : {$users['firstname']} {$users['lastname']}";
// ✅ NOTIFIER SUPERADMIN, DIRECTION, DAF DE TOUS LES STORES
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
// SuperAdmin (validation)
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'remise/'
);
// Direction (consultation)
$Notification->createNotification(
$message . "<br><em>Pour information</em>",
"Direction",
(int)$store['id'],
'remise/'
);
// DAF (consultation)
$Notification->createNotification(
$message . "<br><em>Pour information</em>",
"DAF",
(int)$store['id'],
'remise/'
);
}
}
return redirect()->to('orders/');
} else {
session()->setFlashdata('errors', 'Error occurred!!');
return redirect()->to('orders/create/');
// ✅ COMMANDE SANS REMISE : NOTIFIER CAISSIÈRE DU STORE + TOUS LES CENTRAUX
// Caissière du store concerné
$Notification->createNotification(
"📦 Nouvelle commande à valider : {$bill_no}<br>" .
"Client : {$data['customer_name']}<br>" .
"Montant : " . number_format($gross_amount, 0, ',', ' ') . " Ar",
"Caissière",
(int)$users['store_id'],
"orders"
);
// ✅ RÉCUPÉRER TOUS LES STORES
$allStores = $Stores->getActiveStore();
$messageGlobal = "📋 Nouvelle commande créée : {$bill_no}<br>" .
"Store : " . $this->returnStore($users['store_id']) . "<br>" .
"Client : {$data['customer_name']}<br>" .
"Montant : " . number_format($gross_amount, 0, ',', ' ') . " Ar<br>" .
"Créée par : {$users['firstname']} {$users['lastname']}";
// ✅ NOTIFIER DIRECTION, DAF, SUPERADMIN DE TOUS LES STORES
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$messageGlobal,
"Direction",
(int)$store['id'],
'orders'
);
$Notification->createNotification(
$messageGlobal,
"DAF",
(int)$store['id'],
'orders'
);
$Notification->createNotification(
$messageGlobal,
"SuperAdmin",
(int)$store['id'],
'orders'
);
}
}
}
if ($users["group_name"] != "COMMERCIALE") {
$this->checkProductisNull($posts, $users['store_id']);
}
return redirect()->to('orders/');
} else {
// Affichage du formulaire
$company = $Company->getCompanyData(1);
$session = session();
$users = $session->get('user');
$store_id = $users['store_id'];
$data = [
'paid_status' => 2,
'company_data' => $company,
'is_vat_enabled' => ($company['vat_charge_value'] > 0),
'is_service_enabled' => ($company['service_charge_value'] > 0),
'products' => $Products->getProductData2($store_id),
'validation' => $validation,
'page_title' => $this->pageTitle,
];
return $this->render_template('orders/create', $data);
session()->setFlashdata('errors', 'Error occurred!!');
return redirect()->to('orders/create/');
}
} else {
// Affichage du formulaire
$company = $Company->getCompanyData(1);
$session = session();
$users = $session->get('user');
$store_id = $users['store_id'];
$data = [
'paid_status' => 2,
'company_data' => $company,
'is_vat_enabled' => ($company['vat_charge_value'] > 0),
'is_service_enabled' => ($company['service_charge_value'] > 0),
'products' => $Products->getProductData2($store_id),
'validation' => $validation,
'page_title' => $this->pageTitle,
];
return $this->render_template('orders/create', $data);
}
}
/**
* Marquer une commande comme livrée
* Accessible uniquement par le rôle SECURITE
@ -583,15 +653,42 @@ public function markAsDelivered()
$updated = $Orders->update((int)$order_id, ['paid_status' => 3]);
if ($updated) {
// Créer une notification
// Créer une notification centralisée pour tous les stores
try {
$Notification = new NotificationController();
$Notification->createNotification(
"Commande " . $current_order['bill_no'] . " livrée avec succès",
"Direction",
(int)$current_order['store_id'],
"orders"
);
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
$messageGlobal = "📦 Commande livrée : {$current_order['bill_no']}<br>" .
"Store : " . $this->returnStore($current_order['store_id']) . "<br>" .
"Client : {$current_order['customer_name']}<br>" .
"Livrée par : {$users['firstname']} {$users['lastname']}";
// ✅ NOTIFIER DIRECTION, DAF, SUPERADMIN DE TOUS LES STORES
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$messageGlobal,
"Direction",
(int)$store['id'],
'orders'
);
$Notification->createNotification(
$messageGlobal,
"DAF",
(int)$store['id'],
'orders'
);
$Notification->createNotification(
$messageGlobal,
"SuperAdmin",
(int)$store['id'],
'orders'
);
}
}
} catch (\Exception $e) {
// Si la notification échoue, on continue quand même
log_message('error', 'Erreur notification: ' . $e->getMessage());
@ -611,7 +708,6 @@ public function markAsDelivered()
return $this->response->setJSON($response);
}
public function getProductValueById()
{
$product_id = $this->request->getPost('product_id');
@ -793,10 +889,12 @@ public function update(int $id)
$Notification = new NotificationController();
// ✅ NOTIFICATION CENTRALISÉE POUR VALIDATION
if ($old_paid_status == 2 && $paid_status == 1) {
$customer_name = $this->request->getPost('customer_name');
$bill_no = $current_order['bill_no'];
// ✅ Notification SECURITE du store concerné
$Notification->createNotification(
"Commande validée: {$bill_no} - Client: {$customer_name}",
"SECURITE",
@ -804,13 +902,39 @@ public function update(int $id)
'orders'
);
if ($role === 'Caissière') {
$Notification->createNotification(
"Commande validée par la caisse: {$bill_no}",
"Direction",
(int)$user['store_id'],
'orders'
);
// ✅ RÉCUPÉRER TOUS LES STORES
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
$messageGlobal = "✅ Commande validée : {$bill_no}<br>" .
"Store : " . $this->returnStore($user['store_id']) . "<br>" .
"Client : {$customer_name}<br>" .
"Validée par : {$user['firstname']} {$user['lastname']}";
// ✅ NOTIFIER DIRECTION, DAF, SUPERADMIN DE TOUS LES STORES
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$messageGlobal,
"Direction",
(int)$store['id'],
'orders'
);
$Notification->createNotification(
$messageGlobal,
"DAF",
(int)$store['id'],
'orders'
);
$Notification->createNotification(
$messageGlobal,
"SuperAdmin",
(int)$store['id'],
'orders'
);
}
}
}
@ -973,7 +1097,7 @@ public function update(int $id)
$html = '';
// Vérifier si l'utilisateur a payé
if ($order_data['paid_status'] == 1) {
$paid_status = "<span style='color: green; font-weight: bold;'>Validé</span>";
$paid_status = "<span style='color: green; font-weight: bold;'>Payé</span>";
} elseif ($order_data['paid_status'] == 2) {
$paid_status = "<span style='color: orange; font-weight: bold;'>En Attente</span>";
} else {
@ -1460,7 +1584,7 @@ public function update(int $id)
$company_info = $Company->getCompanyData(1);
$paid_status = $order_data['paid_status'] == 1
? "<span style='color: green; font-weight: bold;'>Validé</span>"
? "<span style='color: green; font-weight: bold;'>Payé</span>"
: "<span style='color: red; font-weight: bold;'>Refusé</span>";
// STYLE COMMUN
@ -2484,7 +2608,7 @@ public function print31(int $id)
}
$paid_status = $order_data['paid_status'] === 1
? "<span style='color: green; font-weight: bold;'>Validé</span>"
? "<span style='color: green; font-weight: bold;'>Payé</span>"
: "<span style='color: red; font-weight: bold;'>Refusé</span>";
// Calculs globaux
@ -2773,4 +2897,4 @@ public function print31(int $id)
echo '</body></html>';
}
}
}

171
app/Controllers/RecouvrementController.php

@ -7,6 +7,7 @@ use App\Models\Orders;
use App\Models\Recouvrement;
use App\Models\SortieCaisse;
use App\Models\Avance;
use App\Models\Stores;
class RecouvrementController extends AdminController
{
@ -197,14 +198,61 @@ class RecouvrementController extends AdminController
$this->verifyRole('deleteRecouvrement');
$recouvrement_id = $this->request->getPost('recouvrement_id');
$response = [];
if ($recouvrement_id) {
$Recouvrement = new Recouvrement();
// ✅ Récupérer les infos du recouvrement avant suppression
$recouvrementData = $Recouvrement->getRecouvrementSingle($recouvrement_id);
if ($Recouvrement->deleteRecouvrement($recouvrement_id)) {
// ✅ Notification pour TOUS les Direction, DAF et SuperAdmin de TOUS les stores
try {
if (class_exists('App\Controllers\NotificationController') && $recouvrementData) {
$Notification = new NotificationController();
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
$session = session();
$users = $session->get('user');
$message = "🗑️ Recouvrement supprimé<br>" .
"Montant: " . number_format($recouvrementData['recouvrement_montant'], 0, ',', ' ') . " Ar<br>" .
"Par: " . $users['firstname'] . ' ' . $users['lastname'];
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'recouvrement'
);
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'recouvrement'
);
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'recouvrement'
);
}
}
}
} catch (\Exception $e) {
log_message('error', 'Erreur notification removeRecouvrement: ' . $e->getMessage());
}
$response['success'] = true;
$response['messages'] = "Recouvrement supprimé avec succès !";
$response['messages'] = "Recouvrement supprimé avec succès !<br><em>Notification envoyée à tous les Direction, DAF et SuperAdmin</em>";
} else {
$response['success'] = false;
$response['messages'] = "Erreur lors de la suppression du recouvrement.";
@ -216,6 +264,7 @@ class RecouvrementController extends AdminController
return $this->response->setJSON($response);
}
public function createRecouvrement()
{
$this->verifyRole('createRecouvrement');
@ -272,9 +321,57 @@ class RecouvrementController extends AdminController
];
if ($Recouvrement->addRecouvrement($data)) {
$Notification->createNotification("Un nouveau recouvrement a été crée", "Direction", (int)$users["store_id"], 'recouvrement');
// ✅ Notification pour TOUS les Direction, DAF et SuperAdmin de TOUS les stores
try {
if (class_exists('App\Controllers\NotificationController')) {
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
$message = "💱 Nouveau recouvrement créé<br>" .
"Montant: " . number_format($amount, 0, ',', ' ') . " Ar<br>" .
"De: " . $send_mode . " → Vers: " . $get_mode . "<br>" .
"Store: " . $this->returnStoreName($users['store_id']) . "<br>" .
"Par: " . $fullname;
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
// Notifier Direction
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'recouvrement'
);
// Notifier DAF
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'recouvrement'
);
// Notifier SuperAdmin
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'recouvrement'
);
}
}
}
} catch (\Exception $e) {
log_message('error', 'Erreur notification createRecouvrement: ' . $e->getMessage());
// Continue même si la notification échoue
}
$response['success'] = true;
$response['messages'] = 'Recouvrement créé avec succès';
$response['messages'] = 'Recouvrement créé avec succès<br>' .
'Montant: ' . number_format($amount, 0, ',', ' ') . ' Ar<br>' .
$send_mode . ' → ' . $get_mode . '<br>' .
'<em>Notification envoyée à tous les Direction, DAF et SuperAdmin</em>';
} else {
$response['success'] = false;
$response['messages'] = 'Erreur lors de la création du recouvrement.';
@ -288,7 +385,6 @@ class RecouvrementController extends AdminController
return $this->response->setJSON($response);
}
public function updateRecouvrement($recouvrement_id)
{
$this->verifyRole('updateRecouvrement');
@ -308,18 +404,61 @@ class RecouvrementController extends AdminController
];
$Recouvrement = new Recouvrement();
// $recouvrement_id = (int) $this->request->getPost('recouvrement_id');
if ($this->request->getMethod() === 'post') {
$data = [
'recouvrement_montant' => (int) $this->request->getPost('recouvrement_montant_edit'),
'recouvrement_date' => $this->request->getPost('recouvrement_date_edit')
];
if ($Recouvrement->updateRecouvrement($recouvrement_id, $data)) {
// $Notification->createNotification("Un nouveau recouvrement crée", "Conseil", $users['store_id'], 'recouvrement');
// ✅ Notification pour TOUS les Direction, DAF et SuperAdmin de TOUS les stores
try {
if (class_exists('App\Controllers\NotificationController')) {
$Notification = new NotificationController();
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
$session = session();
$users = $session->get('user');
$message = "✏️ Recouvrement modifié<br>" .
"Nouveau montant: " . number_format($data['recouvrement_montant'], 0, ',', ' ') . " Ar<br>" .
"Par: " . $users['firstname'] . ' ' . $users['lastname'];
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'recouvrement'
);
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'recouvrement'
);
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'recouvrement'
);
}
}
}
} catch (\Exception $e) {
log_message('error', 'Erreur notification updateRecouvrement: ' . $e->getMessage());
}
return $this->response->setJSON([
'success' => true,
'messages' => 'Recouvrement modifié avec succès !'
'messages' => 'Recouvrement modifié avec succès !<br><em>Notification envoyée à tous les Direction, DAF et SuperAdmin</em>'
]);
} else {
return $this->response->setJSON([
@ -340,6 +479,20 @@ class RecouvrementController extends AdminController
echo json_encode($data);
}
}
private function returnStoreName(int $id): string
{
$Stores = new Stores();
$stor = $Stores->getActiveStore();
$Storename = "";
foreach ($stor as $key => $value) {
if ($value['id'] == $id) {
$Storename = $value['name'];
}
}
return $Storename;
}
private function canMakeRecouvrement($send_mode, $get_mode, $amount): bool
{
$orders = new Orders();

131
app/Controllers/RemiseController.php

@ -6,6 +6,7 @@ use App\Controllers\AdminController;
use App\Models\Notification;
use App\Models\Orders;
use App\Models\Remise;
use App\Models\Stores;
class RemiseController extends AdminController
{
@ -25,7 +26,6 @@ class RemiseController extends AdminController
$this->render_template('demande/index', $data);
}
public function fetchTotal(){
$data = [
'user_permission' => $this->permission,
@ -34,7 +34,6 @@ class RemiseController extends AdminController
return json_encode($data);
}
public function fetchRemiseData()
{
helper(['url', 'form']);
@ -50,21 +49,31 @@ class RemiseController extends AdminController
"recordsFiltered" => $totalRecords,
"data" => []
];
// ✅ RÉCUPÉRER LE RÔLE DE L'UTILISATEUR
$session = session();
$user = $session->get('user');
$role = $user['group_name'] ?? '';
foreach ($data as $key => $value) {
$buttons = '';
if (in_array('validateRemise', $this->permission) && $value['demande_status'] == 'En attente') {
// ✅ SEUL LE SUPERADMIN PEUT VALIDER/REFUSER
if ($role === 'SuperAdmin' && $value['demande_status'] == 'En attente') {
$buttons .= '<button type="submit" class="btn btn-success" onclick="valideFunc(' . $value['id_demande'] . ')">';
$buttons .= '<i class="fa fa-check-circle"></i>';
$buttons .= '</button>';
}
if (in_array('refusedRemise', $this->permission) && $value['demande_status'] == 'En attente') {
$buttons .= ' <button type="button" class="btn btn-danger" onclick="refuseFunc(' . $value['id_demande'] . ')">';
$buttons .= '<i class="fa fa-times-circle"></i>';
$buttons .= '</button>';
}
// ✅ DIRECTION ET DAF VOIENT SEULEMENT (pas de boutons d'action)
if (in_array($role, ['Direction', 'DAF'])) {
$buttons = '<span class="label label-info">Consultation uniquement</span>';
}
$result['data'][$key] = [
$value['id_demande'],
$value['product'],
@ -78,9 +87,23 @@ class RemiseController extends AdminController
return $this->response->setJSON($result);
}
/**
* ✅ AMÉLIORATION : Notifications centralisées lors de la validation/refus des remises
*/
public function updateRemise($id_demande)
{
// ✅ VÉRIFIER QUE SEUL LE SUPERADMIN PEUT VALIDER
$session = session();
$user = $session->get('user');
$role = $user['group_name'] ?? '';
if ($role !== 'SuperAdmin') {
return $this->response->setJSON([
'success' => false,
'messages' => 'Seul le SuperAdmin peut valider ou refuser les demandes de remise.'
]);
}
$this->verifyRole('validateRemise');
$validation = \Config\Services::validation();
@ -108,44 +131,104 @@ class RemiseController extends AdminController
if ($Remise->updateRemise($id_demande, $data)) {
$remise_product = $Remise->getProductByDemandeId($id_demande);
$Notification = new NotificationController();
$session = session();
$users = $session->get('user');
$ordersModel = new Orders();
$order_id = $Remise->getOrderIdByDemandeId($id_demande);
// Logique de notification selon le statut
// ✅ Récupérer les infos de la commande
$order_info = $ordersModel->getOrdersData($order_id);
$bill_no = $order_info['bill_no'] ?? '';
$store_id = $order_info['store_id'] ?? 0;
// ✅ RÉCUPÉRER TOUS LES STORES
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
// ✅ SI REFUSÉ PAR LE SUPERADMIN
if ($demande_status == 'Refusé') {
// Mettre à jour le paid_status de la commande à 0 (Refusé)
if ($order_id) {
$ordersModel->update($order_id, ['paid_status' => 0]);
}
// Si refusé par le Conseil : notification retourne au Commercial
// Message de refus
$messageRefus = "❌ Demande de remise refusée : {$bill_no}<br>" .
"Produit : {$remise_product}<br>" .
"Décision : SuperAdmin";
// Notifier le commercial du store concerné
$Notification->createNotification(
"Votre demande de remise a été refusée pour le produit: " . $remise_product . ". Veuillez modifier la commande.",
$messageRefus . "<br><em>Veuillez modifier la commande.</em>",
"COMMERCIALE",
(int)$users['store_id'],
"orders" // Retour à l'ajout de commande chez le commercial
(int)$store_id,
"orders"
);
$message = 'La demande de remise a été refusée. Une notification a été envoyée au commercial.';
// ✅ NOTIFIER DIRECTION ET DAF DE TOUS LES STORES (POUR INFORMATION)
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$messageRefus . "<br><em>Pour information</em>",
"Direction",
(int)$store['id'],
'remise/'
);
$Notification->createNotification(
$messageRefus . "<br><em>Pour information</em>",
"DAF",
(int)$store['id'],
'remise/'
);
}
}
$message = 'La demande de remise a été refusée. Le commercial et les responsables ont été notifiés.';
} elseif ($demande_status == 'Accepté' || $demande_status == 'Validé') {
// Si accepté par le Conseil : la commande reste "En Attente" (paid_status = 2)
// La caissière devra la valider manuellement
// ✅ SI ACCEPTÉ PAR LE SUPERADMIN
if ($order_id) {
$ordersModel->update($order_id, ['paid_status' => 2]); // 2 = En Attente
$ordersModel->update($order_id, ['paid_status' => 2]);
}
// Notification à la Caissière pour qu'elle valide la commande
$messageAcceptation = "✅ Demande de remise acceptée : {$bill_no}<br>" .
"Produit : {$remise_product}<br>" .
"Décision : SuperAdmin";
// Notifier la Caissière du store concerné
$Notification->createNotification(
"Une commande avec remise acceptée est prête pour validation. Produit: " . $remise_product,
$messageAcceptation . "<br><em>Commande prête à être traitée</em>",
"Caissière",
(int)$users['store_id'],
"orders" // Va chez la caissière pour validation finale
(int)$store_id,
"orders"
);
$message = 'La demande de remise a été acceptée. La commande a été envoyée à la caissière pour validation.';
// Notifier le commercial du store concerné
$Notification->createNotification(
$messageAcceptation,
"COMMERCIALE",
(int)$store_id,
"orders"
);
// ✅ NOTIFIER DIRECTION ET DAF DE TOUS LES STORES (POUR INFORMATION)
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$messageAcceptation . "<br><em>Pour information</em>",
"Direction",
(int)$store['id'],
'remise/'
);
$Notification->createNotification(
$messageAcceptation . "<br><em>Pour information</em>",
"DAF",
(int)$store['id'],
'remise/'
);
}
}
$message = 'La demande de remise a été acceptée. La caissière, le commercial et les responsables ont été notifiés.';
}
return $this->response->setJSON([

2
app/Controllers/ReportController.php

@ -262,7 +262,7 @@ class ReportController extends AdminController
$users = $session->get('user');
// Pour Direction et Conseil : afficher TOUTES les performances
if ($users['group_name'] === "DAF" || $users['group_name'] === "Direction") {
if ($users['group_name'] === "DAF" || $users['group_name'] === "Direction" ||$users['group_name'] === "SuperAdmin" ) {
$orderPaid = $Orders->getPerformanceByOrders();
foreach ($orderPaid as $key => $value) {
// Déterminer le prix de vente réel

341
app/Controllers/SortieCaisseController.php

@ -22,6 +22,87 @@ class SortieCaisseController extends AdminController
}
private $mapping = [
// superadmin
"Achat de matériel informatique" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Achat équipement de sécurité" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Achat mobilier de bureau" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement salaire des collaborateurs" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Cotisation sociales" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Remboursement d'avance moto" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Payement prime ou endemnité" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement sous-traitant" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Frais de formation" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement loyer" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Frais de formation externe" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Abonnement internet" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Entretien locaux" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement fournisseur" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Remboursement de frais" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement assurance" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Réparation immobilisation" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"DVD" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Déclaration fiscale - Déclaration d'impôts" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Enregistrement des contrats de bail au centre fiscal" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
// ----- Raisons Admin -----
"Achat de matériel informatique" => [
'source_fond' => 'Budget Directionnel',
@ -243,7 +324,7 @@ class SortieCaisseController extends AdminController
$users = $session->get('user');
// ✅ Vérifier si l'utilisateur est DAF ou Direction UNIQUEMENT
$canManage = in_array($users['group_name'], ['Direction', 'DAF']);
$canManage = in_array($users['group_name'], ['Direction', 'DAF','SuperAdmin']);
$isCaissiere = $users['group_name'] === 'Caissière';
$result = [
@ -289,7 +370,7 @@ class SortieCaisseController extends AdminController
$buttons // Les caissières ne verront PAS les boutons (car $canManage = false)
];
}
elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF") {
elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF" ||$users["group_name"] === "SuperAdmin" ) {
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
@ -328,50 +409,55 @@ class SortieCaisseController extends AdminController
* ✅ NOUVELLE MÉTHODE : Marquer un décaissement comme payé
* Accessible uniquement par les caissières
*/
public function markAsPaid($id_sortie)
{
// Vérifier que l'utilisateur est une caissière
$session = session();
$users = $session->get('user');
/**
* ✅ CORRECTION COMPLÈTE : Marquer un décaissement comme payé
* Accessible uniquement par les caissières
*/
public function markAsPaid($id_sortie)
{
// Vérifier que l'utilisateur est une caissière
$session = session();
$users = $session->get('user');
if ($users['group_name'] !== 'Caissière') {
return $this->response->setJSON([
'success' => false,
'messages' => 'Accès refusé. Seules les caissières peuvent marquer un décaissement comme payé.'
]);
}
try {
$SortieCaisse = new SortieCaisse();
// Récupérer le décaissement
$decaissement = $SortieCaisse->getSortieCaisseSingle($id_sortie);
if ($users['group_name'] !== 'Caissière') {
if (!$decaissement) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Accès refusé. Seules les caissières peuvent marquer un décaissement comme payé.'
'messages' => 'Décaissement introuvable.'
]);
}
try {
$SortieCaisse = new SortieCaisse();
// Récupérer le décaissement
$decaissement = $SortieCaisse->getSortieCaisseSingle($id_sortie);
if (!$decaissement) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Décaissement introuvable.'
]);
}
// Vérifier que le statut est "Valider"
if ($decaissement['statut'] !== 'Valider') {
return $this->response->setJSON([
'success' => false,
'messages' => 'Ce décaissement ne peut pas être marqué comme payé.<br>Statut actuel : ' . $decaissement['statut']
]);
}
// Mettre à jour le statut
$data = [
'statut' => 'Payé',
'date_paiement_effectif' => date('Y-m-d H:i:s')
];
$result = $SortieCaisse->updateSortieCaisse($id_sortie, $data);
if ($result) {
// Créer une notification pour DAF et Direction DU MÊME STORE
// Vérifier que le statut est "Valider"
if ($decaissement['statut'] !== 'Valider') {
return $this->response->setJSON([
'success' => false,
'messages' => 'Ce décaissement ne peut pas être marqué comme payé.<br>Statut actuel : ' . $decaissement['statut']
]);
}
// Mettre à jour le statut
$data = [
'statut' => 'Payé',
'date_paiement_effectif' => date('Y-m-d H:i:s')
];
$result = $SortieCaisse->updateSortieCaisse($id_sortie, $data);
if ($result) {
// ✅ Créer une notification pour TOUS les DAF, Direction et SuperAdmin
try {
if (class_exists('App\Controllers\NotificationController')) {
$Notification = new NotificationController();
@ -379,48 +465,68 @@ class SortieCaisseController extends AdminController
$message = "💰 Décaissement payé - " . $montant . " Ar<br>" .
"Motif: " . $decaissement['motif'] . "<br>" .
"Caissière: " . $users['firstname'] . ' ' . $users['lastname'] . "<br>" .
"Store: " . $this->returnStoreName($users['store_id']);
"Store: " . $this->returnStoreName($decaissement['store_id']);
// ✅ Notifier la Direction DU MÊME STORE
$Notification->createNotification(
$message,
"Direction",
(int)$users['store_id'], // ✅ Store ID de la caissière
'sortieCaisse'
);
// ✅ Récupérer TOUS les stores
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
// ✅ Notifier le DAF DU MÊME STORE
$Notification->createNotification(
$message,
"DAF",
(int)$users['store_id'], // ✅ Store ID de la caissière
'sortieCaisse'
);
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
// Notifier Direction
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'sortieCaisse'
);
// Notifier DAF
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'sortieCaisse'
);
// Notifier SuperAdmin
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'sortieCaisse'
);
}
}
}
return $this->response->setJSON([
'success' => true,
'messages' => '✅ Décaissement marqué comme <strong>PAYÉ</strong><br>' .
'Direction et DAF de ' . $this->returnStoreName($users['store_id']) . ' ont été notifiés.<br>' .
'Montant: ' . number_format($decaissement['montant_retire'], 0, ',', ' ') . ' Ar'
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de la mise à jour du statut.'
]);
} catch (\Exception $e) {
// Logger l'erreur mais continuer
log_message('error', 'Erreur notification markAsPaid: ' . $e->getMessage());
}
} catch (\Exception $e) {
log_message('error', 'Erreur markAsPaid: ' . $e->getMessage());
return $this->response->setJSON([
'success' => true,
'messages' => '✅ Décaissement marqué comme <strong>PAYÉ</strong><br>' .
'Tous les Direction, DAF et SuperAdmin ont été notifiés.<br>' .
'Montant: ' . number_format($decaissement['montant_retire'], 0, ',', ' ') . ' Ar'
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur serveur: ' . $e->getMessage()
'messages' => 'Erreur lors de la mise à jour du statut.'
]);
}
} catch (\Exception $e) {
log_message('error', 'Erreur markAsPaid: ' . $e->getMessage() . ' | Ligne: ' . $e->getLine());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur serveur: ' . $e->getMessage()
]);
}
}
public function fetchSortieCaisseData1()
{
helper(['url', 'form']);
@ -436,7 +542,7 @@ class SortieCaisseController extends AdminController
$users = $session->get('user');
// ✅ Vérifier si l'utilisateur est DAF ou Direction UNIQUEMENT
$canManage = in_array($users['group_name'], ['Direction', 'DAF']);
$canManage = in_array($users['group_name'], ['Direction', 'DAF','SuperAdmin']);
$isCaissiere = $users['group_name'] === 'Caissière';
$result = [
@ -481,7 +587,7 @@ class SortieCaisseController extends AdminController
$buttons // Les caissières ne verront PAS les boutons
];
}
elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF") {
elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF" || $users["group_name"] === "SuperAdmin") {
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
@ -591,10 +697,10 @@ class SortieCaisseController extends AdminController
}
// Recouvrements
$total_recouvrement_me = 0; // Mvola -> Espèce
$total_recouvrement_be = 0; // Banque -> Espèce
$total_recouvrement_bm = 0; // Banque -> Mvola
$total_recouvrement_mb = 0; // Mvola -> Banque
$total_recouvrement_me = 0;
$total_recouvrement_be = 0;
$total_recouvrement_bm = 0;
$total_recouvrement_mb = 0;
if (is_object($totalRecouvrement)) {
$total_recouvrement_me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0;
@ -621,14 +727,12 @@ class SortieCaisseController extends AdminController
}
// CALCUL DES SOLDES DISPONIBLES PAR MODE DE PAIEMENT
// Espèce: Orders + Recouvrements entrants - Sorties en espèce
$total_espece_disponible = $total_espece1 +
$total_espece2 +
$total_recouvrement_me +
$total_recouvrement_be -
$total_sortie_espece;
// MVOLA: Orders - Recouvrements sortants + Recouvrements entrants - Sorties MVOLA
$total_mvola_disponible = $total_mvola1 +
$total_mvola2 -
$total_recouvrement_me -
@ -636,7 +740,6 @@ class SortieCaisseController extends AdminController
$total_recouvrement_bm -
$total_sortie_mvola;
// Virement: Orders - Recouvrements sortants + Recouvrements entrants - Sorties virement
$total_virement_disponible = $total_virement1 +
$total_virement2 -
$total_recouvrement_be -
@ -742,8 +845,8 @@ class SortieCaisseController extends AdminController
$data['date_visa_chef_service'] = $this->request->getPost('date_visa_chef_service') ?? null;
$data['visa_direction'] = $this->request->getPost('visa_direction') ?? '';
$data['date_visa_direction'] = $this->request->getPost('date_visa_direction') ?? null;
$data['visa_conseil'] = $this->request->getPost('visa_conseil') ?? '';
$data['date_visa_conseil'] = $this->request->getPost('date_visa_conseil') ?? null;
$data['visa_superAdmin'] = $this->request->getPost('visa_superAdmin') ?? '';
$data['date_visa_superAdmin'] = $this->request->getPost('date_visa_superAdmin') ?? null;
$data['observations'] = $this->request->getPost('observations') ?? '';
}
@ -767,25 +870,49 @@ class SortieCaisseController extends AdminController
$result = $model->addSortieCaisse($data);
if ($result) {
// Notification UNIQUEMENT pour la Direction du même store
if (class_exists('App\Controllers\NotificationController')) {
$Notification = new NotificationController();
// ✅ Notifier UNIQUEMENT la Direction du store concerné
$Notification->createNotification(
"Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")",
"Direction",
(int)$user['store_id'], // ✅ Store ID du créateur
'sortieCaisse'
);
// ✅ Notifier aussi le DAF du même store (si vous avez des DAF par store)
$Notification->createNotification(
"Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")",
"DAF",
(int)$user['store_id'], // ✅ Store ID du créateur
'sortieCaisse'
);
// ✅ Notification pour TOUS les Direction, DAF et SuperAdmin
try {
if (class_exists('App\Controllers\NotificationController')) {
$Notification = new NotificationController();
$message = "Nouvelle demande de décaissement de " .
number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")<br>" .
"Store: " . $this->returnStoreName($user['store_id']) . "<br>" .
"Demandeur: " . $user['firstname'] . ' ' . $user['lastname'];
// ✅ Récupérer TOUS les stores
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'sortieCaisse'
);
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'sortieCaisse'
);
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'sortieCaisse'
);
}
}
}
} catch (\Exception $e) {
log_message('error', 'Erreur notification createSortieCaisse: ' . $e->getMessage());
// Continue même si la notification échoue
}
return $this->response->setJSON([
@ -793,7 +920,7 @@ class SortieCaisseController extends AdminController
'messages' => 'Décaissement de ' . number_format($montant_retire, 0, ',', ' ') . ' Ar créé avec succès<br>' .
'Mode de paiement: ' . $mode_paiement . '<br>' .
'Nouveau solde ' . $mode_paiement_label . ': ' . number_format($fonds_disponible - $montant_retire, 0, ',', ' ') . ' Ar<br>' .
'<em>Notification envoyée à la Direction de ' . $this->returnStoreName($user['store_id']) . '</em>'
'<em>Notification envoyée à tous les Direction, DAF et SuperAdmin</em>'
]);
} else {
@ -1096,15 +1223,15 @@ class SortieCaisseController extends AdminController
switch ($statut) {
case "Valider":
$message = "✅ Votre décaissement a été validé par la Direction de " . $this->returnStoreName($store_id);
$message = "✅ Votre décaissement a été validé par la Direction<br>Store: " . $this->returnStoreName($store_id);
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
break;
case "Refuser":
$message = "❌ Votre décaissement a été refusé par la Direction de " . $this->returnStoreName($store_id) . "<br>Raison: " . $this->request->getPost('admin_raison');
$message = "❌ Votre décaissement a été refusé par la Direction<br>Store: " . $this->returnStoreName($store_id) . "<br>Raison: " . $this->request->getPost('admin_raison');
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
break;
case "En attente":
$message = "⏳ Votre décaissement a été mis en attente par la Direction de " . $this->returnStoreName($store_id);
$message = "⏳ Votre décaissement a été mis en attente par la Direction<br>Store: " . $this->returnStoreName($store_id);
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
break;
}

2
app/Controllers/StatistiqueController.php

@ -78,7 +78,7 @@ class StatistiqueController extends AdminController
// echo '</pre>';
$data['is_admin'] = false;
if ($user_id['group_name'] == "Direction" || $user_id['group_name'] == "Conseil") {
if ($user_id['group_name'] == "Direction" || $user_id['group_name'] == "SuperAdmin") {
$data['is_admin'] = true;
}

95
app/Models/Avance.php

@ -58,7 +58,7 @@ class Avance extends Model {
public function getAllAvanceData(int $id=null) {
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction','DAF']);
if($isAdmin) {
if($id){
try {
@ -125,7 +125,7 @@ class Avance extends Model {
public function getTotalAvance() {
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction','DAF']);
try {
$builder = $this->select('SUM(avance_amount) AS ta')
@ -148,7 +148,7 @@ class Avance extends Model {
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction','DAF']);
try {
$builder = $this->db->table('avances')
@ -194,7 +194,7 @@ class Avance extends Model {
public function getAllAvanceData1(int $id=null) {
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction','DAF']);
if($isAdmin) {
if($id){
try {
@ -249,7 +249,7 @@ class Avance extends Model {
public function getAllAvanceData2(int $id=null) {
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
if($isAdmin) {
if($id){
try {
@ -336,7 +336,7 @@ class Avance extends Model {
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$builder = $this->where('is_order', 0)
->where('active', 1)
@ -357,7 +357,7 @@ class Avance extends Model {
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$builder = $this->where('is_order', 0)
->where('active', 1)
@ -451,7 +451,7 @@ public function convertToOrder(int $avance_id)
return false;
}
// ✅ MODIFICATION PRINCIPALE : Vérifier que c'est bien une avance sur TERRE
// ✅ Vérifier que c'est bien une avance sur TERRE
if ($avance['type_avance'] !== 'terre') {
log_message('info', "Avance {$avance_id} de type '{$avance['type_avance']}' - Conversion ignorée (seules les avances TERRE sont converties)");
return false;
@ -528,9 +528,6 @@ public function convertToOrder(int $avance_id)
]);
log_message('info', "Item ajouté : produit {$avance['product_id']} (TERRE)");
// ✅ Le produit reste marqué comme vendu (product_sold = 1)
// Il sera géré dans la commande maintenant
} else {
log_message('warning', "Produit {$avance['product_id']} introuvable pour avance TERRE {$avance_id}");
}
@ -538,7 +535,7 @@ public function convertToOrder(int $avance_id)
// ✅ 3. Marquer l'avance comme convertie
$this->update($avance_id, [
'is_order' => 1,
'active' => 0, // Désactiver l'avance (elle devient commande)
'active' => 0,
]);
$db->transComplete();
@ -548,7 +545,10 @@ public function convertToOrder(int $avance_id)
return false;
}
// ✅ 4. Notification à la caissière
// ✅ 4. NOUVEAU : Envoyer notifications à TOUS les stores
$this->sendConversionNotifications($avance, $order_id, $bill_no);
// ✅ 5. Notification à la caissière du store concerné
$notificationController = new \App\Controllers\NotificationController();
$notificationController->createNotification(
"Nouvelle commande issue d'une avance TERRE complète - {$bill_no}",
@ -567,6 +567,70 @@ public function convertToOrder(int $avance_id)
}
}
private function sendConversionNotifications($avance, $order_id, $bill_no)
{
try {
$Notification = new \App\Controllers\NotificationController();
$db = \Config\Database::connect();
// Récupérer tous les stores
$storesQuery = $db->table('stores')->select('id')->get();
$allStores = $storesQuery->getResultArray();
// Récupérer les infos de l'utilisateur
$userQuery = $db->table('users')
->select('firstname, lastname')
->where('id', $avance['user_id'])
->get();
$user = $userQuery->getRowArray();
$userName = $user ? "{$user['firstname']} {$user['lastname']}" : 'Utilisateur inconnu';
// Préparer le message
$customerName = $avance['customer_name'];
$avanceNumber = str_pad($avance['avance_id'], 5, '0', STR_PAD_LEFT);
$avanceAmount = number_format((float)$avance['gross_amount'], 0, ',', ' ');
$typeAvance = strtoupper($avance['type_avance']);
$notificationMessage = "🎉 Avance {$typeAvance} N°{$avanceNumber} convertie en COMMANDE {$bill_no} - Client: {$customerName} - Montant: {$avanceAmount} Ar - Par: {$userName}";
// ✅ Envoyer notification à DAF, Direction et SuperAdmin de TOUS les stores
foreach ($allStores as $store) {
$storeId = (int)$store['id'];
// Notification pour DAF
$Notification->createNotification(
$notificationMessage,
"DAF",
$storeId,
'orders'
);
// Notification pour Direction
$Notification->createNotification(
$notificationMessage,
"Direction",
$storeId,
'orders'
);
// Notification pour SuperAdmin
$Notification->createNotification(
$notificationMessage,
"SuperAdmin",
$storeId,
'orders'
);
}
log_message('info', "✅ Notifications conversion envoyées pour avance {$avance['avance_id']} → commande {$order_id} à tous les stores");
} catch (\Exception $e) {
log_message('error', "Erreur envoi notifications conversion: " . $e->getMessage());
}
}
/**
* ✅ Hook appelé automatiquement lors du paiement d'une avance
* Intégrer ceci dans votre fonction de paiement existante
@ -630,6 +694,7 @@ public function afterPayment(int $avance_id)
->where('type_avance', 'terre') // ✅ Uniquement TERRE à convertir
->findAll();
}
/**
* ✅ NOUVELLE MÉTHODE : Récupérer les avances MER complètes (pour statistiques)
@ -638,7 +703,7 @@ public function afterPayment(int $avance_id)
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$builder = $this->where('amount_due', 0)
->where('active', 1)
@ -650,5 +715,7 @@ public function afterPayment(int $avance_id)
return $builder->orderBy('avance_date', 'DESC')->findAll();
}
}

2
app/Models/Mecanicien.php

@ -24,7 +24,7 @@ class Mecanicien extends Model
{
$session = session();
$user = $session->get('user');
if ($user['group_name'] == "Conseil" || $user['group_name'] == "Direction") {
if ($user['group_name'] == "SuperAdmin" || $user['group_name'] == "Direction") {
$reparation = $this->select('reparations.reparation_id as reparationsID, reparations.user_id, reparations.reparation_statut, reparations.produit_id, reparations.reparation_observation, reparations.reparation_debut, reparations.reparation_fin, users.*, products.*')
->join('users', 'reparations.user_id = users.id')
->join('products', 'reparations.produit_id = products.id')

8
app/Models/Orders.php

@ -130,7 +130,7 @@ class Orders extends Model
$groupName = $group['group_name'] ?? '';
// Selon le rôle
if (in_array($groupName, ['Direction', 'Conseil'], true)) {
if (in_array($groupName, ['Direction', 'SuperAdmin', 'DAF'], true)) {
return $builder
->orderBy('orders.id', 'DESC')
->get()
@ -163,7 +163,7 @@ class Orders extends Model
$group = $this->getUserGroupNameByUserId($users['id']);
$groupName = $group['group_name'] ?? ''; // Évite une erreur si 'group_name' est null
if ($groupName === "Direction" || $groupName === "Conseil") {
if ($groupName === "Direction" || $groupName === "SuperAdmin" || $groupName === "DAF") {
return $builder->orderBy('orders.id', 'DESC')->get()->getResultArray();
} else {
return $builder->where('orders.store_id', $users['store_id'])
@ -369,7 +369,7 @@ class Orders extends Model
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
if($isAdmin) {
return $this->whereIn('paid_status', [1, 3]) // ← Ajoutez 3 ici
@ -396,7 +396,7 @@ class Orders extends Model
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$baseQuery = $this->db->table('orders')
->select('

4
app/Models/Products.php

@ -21,7 +21,7 @@ class Products extends Model
$user = $session->get('user');
// Vérifier si l'utilisateur est admin (Conseil ou Direction)
$isAdmin = in_array($user['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($user['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$builder = $this->where('is_piece', 0)
->where('product_sold', 0);
@ -206,7 +206,7 @@ public function countProductsByUserStore()
$user = $session->get('user');
// Vérifier si l'utilisateur est admin
$isAdmin = in_array($user['group_name'], ['DAF', 'Direction']);
$isAdmin = in_array($user['group_name'], ['DAF', 'Direction', 'SuperAdmin']);
$db = \Config\Database::connect();

4
app/Models/Recouvrement.php

@ -73,7 +73,7 @@ class Recouvrement extends Model{
public function getTotalRecouvrements(int $id = null, $start_date = null, $end_date = null) {
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['DAF', 'Direction']);
$isAdmin = in_array($users['group_name'], ['DAF', 'Direction', 'SuperAdmin']);
if($isAdmin){
if ($id) {
return $this->db->table('recouvrement')
@ -190,7 +190,7 @@ class Recouvrement extends Model{
{
$session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['DAF', 'Direction']);
$isAdmin = in_array($users['group_name'], ['DAF', 'Direction', 'SuperAdmin']);
$builder = $this->db->table('recouvrement');

28
app/Models/Remise.php

@ -6,10 +6,6 @@ use DateTime;
class Remise extends Model
{
/**
* table name
* @var string
*/
protected $table = 'demande_remise';
protected $primaryKey = 'id_demande';
protected $allowedFields = ['id_order', 'montant_demande', 'date_demande','product','id_store','demande_status','total_price'];
@ -20,26 +16,26 @@ class Remise extends Model
$session = session();
$users = $session->get('user');
// Si le rôle est CONSEIL → voir toutes les demandes "En attente"
if ($users["group_name"] === "Conseil") {
// ✅ SUPERADMIN VOIT TOUTES LES DEMANDES "EN ATTENTE" (POUR VALIDATION)
if ($users["group_name"] === "SuperAdmin") {
return $this->where('demande_status', 'En attente')
->orderBy('date_demande', 'DESC')
->findAll();
}
// Si le rôle est DIRECTION → voir toutes les demandes aussi (pas filtrées par magasin)
if ($users["group_name"] === "Direction") {
// ✅ DIRECTION ET DAF VOIENT TOUTES LES DEMANDES (TOUS STATUTS) POUR CONSULTATION
if ($users["group_name"] === "Direction" || $users["group_name"] === "DAF") {
return $this->orderBy('date_demande', 'DESC')
->findAll();
}
// Autres rôles (Caissière, etc.) → voir uniquement celles de son magasin
// ✅ AUTRES RÔLES : VOIR UNIQUEMENT CELLES DE LEUR MAGASIN
return $this->where('id_store', $users['store_id'])
->orderBy('date_demande', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des demandes du jour : ' . $e->getMessage());
log_message('error', 'Erreur lors de la récupération des demandes : ' . $e->getMessage());
return [];
}
}
@ -61,11 +57,6 @@ class Remise extends Model
->first();
}
/**
* Récupère l'ID de la commande associée à une demande de remise
* @param int $id_demande
* @return int|null
*/
public function getOrderIdByDemandeId(int $id_demande): ?int
{
$result = $this->select('id_order')
@ -83,10 +74,7 @@ class Remise extends Model
}
try {
// Mettre à jour uniquement les données de la demande de remise
// On ne touche PAS aux montants de la commande ici
$updateResult = $this->update($id, $data);
return $updateResult;
} catch (\Exception $e) {
@ -109,13 +97,11 @@ class Remise extends Model
$existing = $this->where('id_order', $id)->first();
if ($existing) {
// Mise à jour
$this->update($existing['id_demande'], $data);
return "Remise mise à jour avec succès.";
} else {
// Création
$this->insert($data);
return "Nouvelle remise créée avec succès.";
}
}
}
}

2
app/Models/Securite.php

@ -35,7 +35,7 @@ class Securite extends Model
{
$session = session();
$users = $session->get('user');
if ($users["group_name"] == "Direction") {
if ($users["group_name"] == "Direction" || $users["group_name"] == "SuperAdmin" || $users["group_name"] == "DAF") {
return $this->where('active', true)
->findAll();
}

293
app/Models/SortieCaisse.php

@ -5,173 +5,133 @@ use CodeIgniter\Model;
class SortieCaisse extends Model
{
/**
* table name
* @var string
*/
protected $table = 'sortie_caisse';
protected $primaryKey = 'id_sortie'; // Primary key column
// Dans le fichier App/Models/SortieCaisse.php
// Mettre à jour le tableau $allowedFields
protected $primaryKey = 'id_sortie';
protected $allowedFields = [
'montant_retire',
'date_retrait',
'motif',
'commentaire',
'fournisseur',
'nif_cin',
'statistique',
'telephone',
'code_postal',
'preuve_achat',
'sortie_personnel',
'source_fond',
'initiateur_demande',
'statut',
'user_id',
'store_id',
'mime_type',
'admin_raison',
// Champs pour le formulaire IM1
'nom_demandeur',
'fonction_demandeur',
'mode_paiement',
'montant_lettre',
'date_demande',
'reference',
// ✅ NOUVEAU CHAMP
'date_paiement_effectif', // Date à laquelle la caissière a effectué le paiement
// Champs pour le formulaire IM2/IM3
'numero_fiche',
'date_fiche',
'service_demandeur',
'objet_depense',
'nature_depense',
'montant_estime',
'mode_reglement',
'date_paiement_prevue',
'justificatifs',
'visa_demandeur',
'date_visa_demandeur',
'visa_chef_service',
'date_visa_chef_service',
'visa_direction',
'date_visa_direction',
'visa_conseil',
'date_visa_conseil',
'observations'
];
public function getAllSortieCaisse()
{
try {
$session = session();
$users = $session->get('user');
// ✅ DIRECTION : Voir uniquement les décaissements de SON store
if ($users['group_name'] === 'Direction') {
return $this
->select('*')
->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ DAF : Voir uniquement les décaissements de SON store
if ($users['group_name'] === 'DAF') {
return $this
->select('*')
->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ CONSEIL : Voir TOUS les décaissements (multi-stores)
// if ($users['group_name'] === 'Conseil') {
// return $this
// ->select('*')
// ->orderBy('date_retrait', 'DESC')
// ->findAll();
// }
// ✅ CAISSIÈRE : Voir uniquement SES décaissements
if($users["group_name"]==="Caissière"){
protected $allowedFields = [
'montant_retire',
'date_retrait',
'motif',
'commentaire',
'fournisseur',
'nif_cin',
'statistique',
'telephone',
'code_postal',
'preuve_achat',
'sortie_personnel',
'source_fond',
'initiateur_demande',
'statut',
'user_id',
'store_id',
'mime_type',
'admin_raison',
'nom_demandeur',
'fonction_demandeur',
'mode_paiement',
'montant_lettre',
'date_demande',
'reference',
'date_paiement_effectif',
'numero_fiche',
'date_fiche',
'service_demandeur',
'objet_depense',
'nature_depense',
'montant_estime',
'mode_reglement',
'date_paiement_prevue',
'justificatifs',
'visa_demandeur',
'date_visa_demandeur',
'visa_chef_service',
'date_visa_chef_service',
'visa_direction',
'date_visa_direction',
'visa_conseil',
'date_visa_conseil',
'observations'
];
/**
* ✅ MODIFICATION : DAF, Direction, SuperAdmin voient TOUS les stores
* Caissière voit uniquement SES décaissements
*/
public function getAllSortieCaisse()
{
try {
$session = session();
$users = $session->get('user');
// ✅ DAF, Direction, SuperAdmin : Voir TOUS les stores
if (in_array($users['group_name'], ['Direction', 'DAF', 'SuperAdmin'])) {
return $this
->select('*')
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ CAISSIÈRE : Voir uniquement SES décaissements
if($users["group_name"] === "Caissière"){
return $this
->select('*')
->where('user_id', $users['id'])
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ AUTRES : Par défaut, voir uniquement leurs décaissements
return $this
->select('*')
->where('user_id', $users['id'])
->orderBy('date_retrait', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage());
return [];
}
// ✅ AUTRES : Par défaut, voir uniquement leurs décaissements
return $this
->select('*')
->where('user_id', $users['id'])
->orderBy('date_retrait', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage());
return [];
}
}
public function getAllSortieCaisse1()
{
try {
$session = session();
$users = $session->get('user');
// ✅ DIRECTION : Voir uniquement les décaissements de SON store
if ($users['group_name'] === 'Direction') {
return $this
->select('*')
->join('user_group', 'user_group.user_id = sortie_caisse.user_id')
->where('user_group.group_id', 7)
->where('sortie_caisse.store_id', $users['store_id']) // ✅ FILTRE PAR STORE
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ DAF : Voir uniquement les décaissements de SON store
if ($users['group_name'] === 'DAF') {
return $this
->select('*')
->join('user_group', 'user_group.user_id = sortie_caisse.user_id')
->where('user_group.group_id', 7)
->where('sortie_caisse.store_id', $users['store_id']) // ✅ FILTRE PAR STORE
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ CONSEIL : Voir TOUS les stores
// if ($users['group_name'] === 'Conseil') {
// return $this
// ->select('*')
// ->join('user_group', 'user_group.user_id = sortie_caisse.user_id')
// ->where('user_group.group_id', 6)
// ->orderBy('date_retrait', 'DESC')
// ->findAll();
// }
// ✅ CAISSIÈRE : Voir uniquement SES décaissements
if($users["group_name"]==="Caissière"){
/**
* ✅ MODIFICATION : Même logique pour les décaissements filtrés
*/
public function getAllSortieCaisse1()
{
try {
$session = session();
$users = $session->get('user');
// ✅ DAF, Direction, SuperAdmin : Voir TOUS les stores
if (in_array($users['group_name'], ['Direction', 'DAF', 'SuperAdmin'])) {
return $this
->select('*')
->join('user_group', 'user_group.user_id = sortie_caisse.user_id')
->where('user_group.group_id', 7)
->orderBy('date_retrait', 'DESC')
->findAll();
}
// ✅ CAISSIÈRE : Voir uniquement SES décaissements
if($users["group_name"] === "Caissière"){
return $this
->select('*')
->where('user_id', $users['id'])
->orderBy('date_retrait', 'DESC')
->findAll();
}
return $this
->select('*')
->where('user_id', $users['id'])
->orderBy('date_retrait', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage());
return [];
}
return $this
->select('*')
->where('user_id', $users['id'])
->orderBy('date_retrait', 'DESC')
->findAll();
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage());
return [];
}
}
public function addSortieCaisse(array $data) {
try {
return $this->insert($data);
@ -203,15 +163,15 @@ public function getAllSortieCaisse1()
return $reparation;
}
/**
* ✅ MODIFICATION : DAF, Direction, SuperAdmin voient TOUS les totaux
*/
public function getTotalSortieCaisse() {
$session = session();
$users = $session->get('user');
// ✅ DIRECTION et DAF : Voir uniquement leur store
$isAdmin = in_array($users['group_name'], ['Direction', 'DAF']);
// ✅ CONSEIL : Voir TOUS les stores
$isConseil = $users['group_name'] === 'Conseil';
// ✅ DAF, Direction, SuperAdmin : Voir TOUS les stores
$isAdmin = in_array($users['group_name'], ['Direction', 'DAF', 'SuperAdmin']);
if ($isAdmin) {
try {
@ -221,7 +181,7 @@ public function getAllSortieCaisse1()
SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant_retire ELSE 0 END) AS total_virement,
SUM(montant_retire) AS mr
')
->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE
// ✅ SUPPRESSION DU FILTRE PAR STORE
->whereIn('statut', ['Valider', 'Payé'])
->get()
->getRowObject();
@ -234,27 +194,6 @@ public function getAllSortieCaisse1()
'mr' => 0
];
}
} elseif ($isConseil) {
// ✅ CONSEIL voit TOUS les stores
try {
return $this->select('
SUM(CASE WHEN mode_paiement = "En espèce" THEN montant_retire ELSE 0 END) AS total_espece,
SUM(CASE WHEN mode_paiement = "MVOLA" THEN montant_retire ELSE 0 END) AS total_mvola,
SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant_retire ELSE 0 END) AS total_virement,
SUM(montant_retire) AS mr
')
->whereIn('statut', ['Valider', 'Payé'])
->get()
->getRowObject();
} catch (\Exception $e) {
log_message('error', 'Erreur getTotalSortieCaisse (Conseil) : ' . $e->getMessage());
return (object)[
'total_espece' => 0,
'total_mvola' => 0,
'total_virement' => 0,
'mr' => 0
];
}
} else {
// ✅ CAISSIÈRE : Uniquement son store
try {

8
app/Views/avances/avance.php

@ -44,13 +44,13 @@
<?php
$session = session();
$users = $session->get('user');
$isAdmin = isset($users['group_name']) && in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = isset($users['group_name']) && in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$isCommerciale = isset($users['group_name']) && in_array($users['group_name'], ['COMMERCIALE']);
$isCaissier = isset($users['group_name']) && in_array($users['group_name'], ['Caissière']);
?>
<!-- Bouton de vérification des avances expirées -->
<?php if (isset($users['group_name']) && in_array($users['group_name'], ['Conseil', 'Direction', 'DAF'])): ?>
<?php if (isset($users['group_name']) && in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF'])): ?>
<div class="row mb-3">
<div class="col-md-12 text-right">
<button
@ -70,7 +70,7 @@ $isCaissier = isset($users['group_name']) && in_array($users['group_name'], ['Ca
<thead>
<?php $session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction','DAF']);
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']);
$isCaissier = in_array($users['group_name'], ['Caissière']);
if ($isAdmin): ?>
@ -90,7 +90,7 @@ $isCaissier = isset($users['group_name']) && in_array($users['group_name'], ['Ca
<?php endif;?>
<?php $session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']);
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']);
$isCaissier = in_array($users['group_name'], ['Caissière']);
if ($isCommerciale || $isCaissier): ?>

2
app/Views/dashboard.php

@ -325,7 +325,7 @@
</div>
<?php $session = session();
$users = $session->get('user');
if ($users["group_name"] === "Direction") :
if ($users["group_name"] === "Direction" || $users["group_name"] === "SuperAdmin" || $users["group_name"] === "DAF" ) :
?>
<div class="col-md-3">

137
app/Views/login.php

@ -12,7 +12,7 @@
<link rel="stylesheet" href="<?php echo base_url('assets/dist/css/AdminLTE.min.css') ?>">
<!-- Google Font -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
body {
@ -22,6 +22,7 @@
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.login-card {
@ -39,10 +40,6 @@
.login-image {
flex: 1;
background: url('https://lh3.googleusercontent.com/p/AF1QipN4iewRbD9iIfbsvyPTD2SGUkxyi952uG30pHD9=s1360-w1360-h1020') center/cover no-repeat;
/* Alternative possible :
https://images.unsplash.com/photo-1520975661595-6453be3f7070?auto=format&fit=crop&w=1000&q=80
(casque de moto)
*/
position: relative;
display: flex;
justify-content: center;
@ -138,6 +135,96 @@
to { opacity: 1; transform: translateY(0); }
}
/* Animation de chargement */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #007bff, #0056b3);
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
animation: overlayFadeIn 0.5s ease;
}
.loading-overlay.active {
display: flex;
}
.loading-content {
text-align: center;
color: white;
}
.loading-logo {
font-size: 3.5em;
font-weight: 700;
color: #ffcc00;
margin-bottom: 30px;
animation: logoReveal 1.2s ease-in-out;
letter-spacing: 3px;
}
.motorcycle-icon {
font-size: 4em;
margin-bottom: 20px;
animation: bikeRide 1.5s ease-in-out infinite;
}
.loading-text {
font-size: 1.2em;
margin-top: 20px;
opacity: 0;
animation: textFadeIn 0.8s ease 0.5s forwards;
}
.loading-spinner {
width: 60px;
height: 60px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid #ffcc00;
border-radius: 50%;
margin: 30px auto;
animation: spin 1s linear infinite;
}
@keyframes overlayFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes logoReveal {
0% {
opacity: 0;
transform: scale(0.5) rotateY(-90deg);
}
50% {
transform: scale(1.1) rotateY(0deg);
}
100% {
opacity: 1;
transform: scale(1) rotateY(0deg);
}
}
@keyframes bikeRide {
0%, 100% { transform: translateX(-20px) rotate(-2deg); }
50% { transform: translateX(20px) rotate(2deg); }
}
@keyframes textFadeIn {
to {
opacity: 1;
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (max-width: 768px) {
.login-card {
flex-direction: column;
@ -149,11 +236,27 @@
.login-form {
padding: 25px;
}
.loading-logo {
font-size: 2.5em;
}
.motorcycle-icon {
font-size: 3em;
}
}
</style>
</head>
<body>
<!-- Overlay de chargement -->
<div class="loading-overlay" id="loadingOverlay">
<div class="loading-content">
<div class="motorcycle-icon">🏍️</div>
<div class="loading-logo">MOTORBIKE</div>
<div class="loading-spinner"></div>
<div class="loading-text">Démarrage en cours...</div>
</div>
</div>
<div class="login-card">
<!-- Image + message -->
@ -174,7 +277,7 @@
</div>
<?php endif; ?>
<form action="<?= base_url('login') ?>" method="post">
<form action="<?= base_url('login') ?>" method="post" id="loginForm">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-envelope"></i></span>
<input type="email" class="form-control" name="email" placeholder="Email" required>
@ -197,5 +300,25 @@
<script src="<?php echo base_url('assets/bower_components/jquery/dist/jquery.min.js') ?>"></script>
<script src="<?php echo base_url('assets/bower_components/bootstrap/dist/js/bootstrap.min.js') ?>"></script>
<script>
document.getElementById('loginForm').addEventListener('submit', function(e) {
// Afficher l'animation de chargement
document.getElementById('loadingOverlay').classList.add('active');
// Le formulaire se soumettra normalement
// L'animation reste visible pendant le traitement côté serveur
});
// Si vous voulez tester l'animation en local sans backend
// décommentez cette fonction et appelez testAnimation()
function testAnimation() {
document.getElementById('loadingOverlay').classList.add('active');
setTimeout(function() {
alert('Animation terminée ! En production, vous seriez redirigé vers le dashboard.');
document.getElementById('loadingOverlay').classList.remove('active');
}, 2500);
}
</script>
</body>
</html>
</html>

4
app/Views/orders/avance.php

@ -26,7 +26,7 @@
<thead>
<?php $session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Direction', 'Conseil']);
$isAdmin = in_array($users['group_name'], ['Direction', 'SuperAdmin']);
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']);
$isCaissier = in_array($users['group_name'], ['Caissier']);
if ($isAdmin): ?>
@ -46,7 +46,7 @@
<?php endif;?>
<?php $session = session();
$users = $session->get('user');
$isAdmin = in_array($users['group_name'], ['Direction', 'Conseil']);
$isAdmin = in_array($users['group_name'], ['Direction', 'SuperAdmin']);
$isCommerciale = in_array($users['group_name'], ['COMMERCIALE']);
$isCaissier = in_array($users['group_name'], ['Caissier']);
if ($isCommerciale || $isCaissier): ?>

2
app/Views/orders/edit.php

@ -306,7 +306,7 @@
<label for="paid_status" class="col-sm-5 control-label">Statut payant</label>
<div class="col-sm-7">
<select type="text" class="form-control" id="paid_status" name="paid_status">
<option value="1">Validé</option>
<option value="1">Payé</option>
<option value="2">Refusé</option>
</select>
</div>

84
app/Views/orders/index.php

@ -50,7 +50,7 @@
<?php
$session = session();
$users = $session->get('user');
if ($users['group_name'] === 'Conseil' || $users['group_name'] === "Direction") {
if ($users['group_name'] === 'SuperAdmin' || $users['group_name'] === "Direction" || $users['group_name'] === "DAF" ) {
?>
<th>Facture n°</th>
<th>Nom du client</th>
@ -58,7 +58,7 @@
<th>Date et Heure</th>
<th>Prix demandé</th>
<th>Prix de vente</th>
<th>Validation</th>
<th>Status</th>
<?php if (
in_array('updateOrder', $user_permission)
@ -79,7 +79,7 @@
<th>Date et Heure</th>
<th>Prix demandé</th>
<th>Prix de vente</th>
<th>Validation</th>
<th>Status</th>
<?php if (
in_array('viewOrder', $user_permission)
|| in_array('updateOrder', $user_permission)
@ -135,6 +135,7 @@
<?php endif; ?>
<!-- Modal -->
<!-- Modal -->
<div class="modal fade" id="viewOrderModal" tabindex="-1" role="dialog" aria-labelledby="viewOrderModalLabel">
<div class="modal-dialog modal-lg" role="document">
@ -181,20 +182,22 @@
</div>
</div>
<!-- Tableau produits -->
<h4>Produits</h4>
<!-- Tableau produits avec toutes les infos sur une ligne -->
<h4>Informations de la commande</h4>
<table class="table table-bordered table-striped" id="view_products_table">
<thead>
<tr><th>Marque</th><th>Designation</th><th>Numéro de Série</th><th>Prix Unitaire</th></tr>
<tr>
<th>Marque</th>
<th>Désignation</th>
<th>Numéro de Série</th>
<th>N° de Moteur</th>
<th>Châssis</th>
<th>N° Facture</th>
<th>Statut</th>
</tr>
</thead>
<tbody></tbody>
</table>
<!-- Totaux -->
<table class="table table-bordered table-striped" id="view_totals">
<tr><th>Montant brut</th><th>Rabais</th><th>Statut</th></tr>
<tr><td id="gross_amount"></td><td id="discount"></td><td id="paid_status"></td></tr>
</table>
</div>
<div class="modal-footer">
@ -284,8 +287,10 @@
'</div>'
);
// Mettre à jour le statut dans la modal
$('#paid_status').html('<span class="label label-info">Validé et Livré</span>');
// Mettre à jour le statut dans toutes les lignes du tableau
$('#view_products_table tbody tr').each(function() {
$(this).find('td:last').html('<span class="label label-info">Payé et Livré</span>');
});
// Cacher le bouton "Livré"
$('#btn-mark-delivered').hide();
@ -364,6 +369,7 @@
}
}
// ✅ FONCTION MODIFIÉE POUR AFFICHER LES NOUVELLES COLONNES
$(document).on('click', '.btn-view', function(e) {
e.preventDefault();
var orderId = $(this).data('order-id');
@ -384,7 +390,25 @@
$('#customer_phone').text(d.customer_phone);
$('#customer_cin').text(d.customer_cin);
// Produits
// Déterminer le statut une seule fois
var statutHtml = '';
if (d.paid_status == 1) {
statutHtml = '<span class="label label-success">Payé</span>';
<?php if ($users['group_name'] === 'SECURITE'): ?>
$('#btn-mark-delivered').show();
<?php endif; ?>
} else if (d.paid_status == 2) {
statutHtml = '<span class="label label-warning">En Attente</span>';
$('#btn-mark-delivered').hide();
} else if (d.paid_status == 3) {
statutHtml = '<span class="label label-info">Payé et Livré</span>';
$('#btn-mark-delivered').hide();
} else {
statutHtml = '<span class="label label-danger">Refusé</span>';
$('#btn-mark-delivered').hide();
}
// Produits avec les nouvelles colonnes
var $tb = $('#view_products_table tbody');
$tb.empty();
$.each(response.order_data.order_item, function(_, item) {
@ -405,41 +429,21 @@
}
});
// ✅ AJOUT DES NOUVELLES COLONNES : N° Moteur, Châssis, N° Facture, Statut
$tb.append(
'<tr>' +
'<td>' + (brandName || 'Aucune marque') + '</td>' +
'<td>' + (product.name || '') + '</td>' +
'<td>' + (product.sku || '') + '</td>' +
'<td>' + parseInt(item.amount).toLocaleString('fr-MG', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + ' Ar</td>' +
'<td>' + (product.numero_de_moteur || 'N/A') + '</td>' +
'<td>' + (product.chasis || 'N/A') + '</td>' +
'<td>' + (d.bill_no || 'N/A') + '</td>' +
'<td>' + statutHtml + '</td>' +
'</tr>'
);
}
});
// Totaux
$('#gross_amount').text(parseInt(d.gross_amount).toLocaleString('fr-MG', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + ' Ar');
$('#discount').text(parseInt(d.discount).toLocaleString('fr-MG', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + ' Ar');
// Statut de paiement avec gestion du statut "Livré"
var statutHtml = '';
if (d.paid_status == 1) {
statutHtml = '<span class="label label-success">Validé</span>';
// Afficher le bouton "Livré" pour la sécurité si statut = Validé
<?php if ($users['group_name'] === 'SECURITE'): ?>
$('#btn-mark-delivered').show();
<?php endif; ?>
} else if (d.paid_status == 2) {
statutHtml = '<span class="label label-warning">En Attente</span>';
$('#btn-mark-delivered').hide();
} else if (d.paid_status == 3) {
statutHtml = '<span class="label label-info">Validé et Livré</span>';
$('#btn-mark-delivered').hide();
} else {
statutHtml = '<span class="label label-danger">Refusé</span>';
$('#btn-mark-delivered').hide();
}
$('#paid_status').html(statutHtml);
// Afficher la modal
$('#viewOrderModal').modal('show');
});

144
app/Views/reports/index.php

@ -303,4 +303,146 @@
}
}
});
</script> -->
</script>
<style>
/* --- Style global --- */
body {
background: #f4f6f9;
font-family: 'Poppins', sans-serif;
}
h1, h3 {
font-weight: 600;
color: #2c3e50;
}
/* --- En-tête --- */
.content-header {
background: linear-gradient(135deg, #3498db, #2ecc71);
color: white;
padding: 25px;
border-radius: 10px;
margin-bottom: 30px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.content-header h1 {
margin: 0;
font-size: 26px;
}
.breadcrumb {
background: transparent;
margin-top: 10px;
}
.breadcrumb li a {
color: #ecf0f1;
}
/* --- Formulaire --- */
.form-inline {
margin-bottom: 25px;
}
.form-inline .form-control,
.form-inline button,
.form-inline a {
border-radius: 8px !important;
}
.btn-default {
background-color: #2980b9;
color: white;
transition: all 0.3s ease;
}
.btn-default:hover {
background-color: #1f618d;
color: white;
}
.btn-success {
transition: all 0.3s ease;
}
.btn-success:hover {
background-color: #27ae60;
}
/* --- Boîtes de statistiques --- */
.box {
background: white;
border-radius: 15px;
box-shadow: 0 6px 15px rgba(0,0,0,0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease;
margin-bottom: 25px;
}
.box:hover {
transform: translateY(-5px);
box-shadow: 0 12px 25px rgba(0,0,0,0.15);
}
.box-header {
border-bottom: 2px solid #f1f1f1;
padding: 15px 20px;
background: #fafafa;
border-radius: 15px 15px 0 0;
}
.box-title {
font-size: 18px;
color: #34495e;
}
.box-body {
padding: 20px;
}
/* --- Table --- */
table {
background: white;
border-radius: 12px;
overflow: hidden;
}
th {
background: #3498db;
color: white;
text-align: center;
}
td {
text-align: center;
vertical-align: middle !important;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
/* --- Alertes --- */
.alert {
border-radius: 10px;
animation: fadeIn 0.6s ease;
}
@keyframes fadeIn {
from {opacity: 0; transform: translateY(-10px);}
to {opacity: 1; transform: translateY(0);}
}
/* --- Charts --- */
canvas {
width: 100% !important;
height: 300px !important;
}
/* --- Responsive --- */
@media (max-width: 768px) {
.box {
margin-bottom: 20px;
}
}
</style>

16
app/Views/sortieCaisse/index.php

@ -125,7 +125,7 @@ input[readonly], select[disabled], textarea[readonly] {
<br>
<?php $session = session();
$users = $session->get('user');
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "SuperAdmin" || $users['group_name'] == "DAF";
$isCaissier = $users['group_name'] == "Caissière";
if($isAdmin): ?>
<button id="validate_filtered" class="btn btn-primary w-100">A valider</button>
@ -159,7 +159,7 @@ input[readonly], select[disabled], textarea[readonly] {
<th>Action</th>
<?php } ?>
<?php
} elseif ($users['group_name'] === 'Direction' || $users['group_name'] === 'DAF') {
} elseif ($users['group_name'] === 'Direction' || $users['group_name'] === 'DAF' || $users['group_name'] === 'SuperAdmin') {
?>
<th>#</th>
<th>Montant</th>
@ -296,7 +296,7 @@ input[readonly], select[disabled], textarea[readonly] {
<?php
$session = session();
$users = $session->get('user');
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "SuperAdmin" || $users['group_name'] == "DAF";
$isCaissier = $users['group_name'] == "Caissière";
$options = $isAdmin ? $admin_options : $caissier_options;
@ -522,9 +522,9 @@ input[readonly], select[disabled], textarea[readonly] {
<td><input type="date" class="form-control" name="date_visa_direction"></td>
</tr>
<tr>
<td>Conseil d'Administration</td>
<td><input type="text" class="form-control" name="visa_conseil" placeholder="Nom et signature"></td>
<td><input type="date" class="form-control" name="date_visa_conseil"></td>
<td>SuperAdmin</td>
<td><input type="text" class="form-control" name="visa_superAdmin" placeholder="Nom et signature"></td>
<td><input type="date" class="form-control" name="date_superAdmin"></td>
</tr>
</tbody>
</table>
@ -605,7 +605,7 @@ input[readonly], select[disabled], textarea[readonly] {
<?php
$session = session();
$users = $session->get('user');
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "SuperAdmin" || $users['group_name'] == "DAF";
$options = $isAdmin ? $admin_options : $caissier_options;
echo "<option value=''>Veuillez sélectionner une raison</option>\n";
@ -1038,7 +1038,7 @@ $(document).ready(function() {
<?php
$session = session();
$users = $session->get('user');
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "SuperAdmin" || $users['group_name'] == "DAF";
?>
<?php if($isAdmin): ?>

241
app/Views/statistic/index.php

@ -1,61 +1,190 @@
<style>
/* --- Style général --- */
body {
background: #f4f6f9;
font-family: 'Poppins', sans-serif;
}
h1 {
font-weight: 600;
color: #2c3e50;
}
small {
color: #7f8c8d;
}
/* --- En-tête --- */
.content-header {
background: linear-gradient(135deg, #3498db, #2ecc71);
color: white;
padding: 25px;
border-radius: 12px;
margin-bottom: 25px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.breadcrumb {
background: transparent;
margin-top: 10px;
}
.breadcrumb a {
color: #ecf0f1;
}
/* --- Boîte principale --- */
.box {
background: white;
border-radius: 16px;
box-shadow: 0 6px 15px rgba(0,0,0,0.08);
padding: 20px;
animation: fadeIn 0.6s ease;
}
@keyframes fadeIn {
from {opacity: 0; transform: translateY(15px);}
to {opacity: 1; transform: translateY(0);}
}
/* --- Cartes utilisateurs --- */
.small-box {
position: relative;
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
border-radius: 15px;
box-shadow: 0 8px 18px rgba(0,0,0,0.15);
transition: transform 0.3s ease, box-shadow 0.3s ease;
overflow: hidden;
}
.small-box:hover {
transform: translateY(-6px);
box-shadow: 0 12px 24px rgba(0,0,0,0.25);
}
.small-box .inner {
padding: 20px;
text-align: center;
}
.small-box h3 {
font-size: 22px;
margin-bottom: 10px;
font-weight: 600;
text-transform: capitalize;
}
.small-box p {
font-size: 15px;
}
.small-box b {
color: #f1c40f;
}
.small-box .icon {
position: absolute;
top: 10px;
right: 15px;
font-size: 60px;
opacity: 0.15;
transition: opacity 0.3s ease;
}
.small-box:hover .icon {
opacity: 0.3;
}
.small-box-footer {
display: block;
text-align: center;
padding: 10px;
color: #ecf0f1;
background: rgba(0,0,0,0.15);
border-radius: 0 0 15px 15px;
text-decoration: none;
font-weight: 500;
transition: background 0.3s ease;
}
.small-box-footer:hover {
background: rgba(0,0,0,0.3);
}
/* --- Couleurs aléatoires dynamiques pour chaque carte --- */
.small-box:nth-child(4n+1) {
background: linear-gradient(135deg, #3498db, #2980b9);
}
.small-box:nth-child(4n+2) {
background: linear-gradient(135deg, #2ecc71, #27ae60);
}
.small-box:nth-child(4n+3) {
background: linear-gradient(135deg, #e67e22, #d35400);
}
.small-box:nth-child(4n+4) {
background: linear-gradient(135deg, #9b59b6, #8e44ad);
}
/* --- Responsive --- */
@media (max-width: 768px) {
.small-box {
margin-bottom: 15px;
}
}
</style>
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Tableau de bord
<small>Statistiques des utilisateurs</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Accueil</a></li>
<li class="active"><a href="../">Tableau de bord</a></li>
</ol>
</section>
<section class="content">
<div class="box" style="padding-top: 5px; padding-left: 5px; padding-right: 5px">
<?php if ($is_admin == true): ?>
<div class="row" id="dataToMap"></div>
<!-- /.row -->
<?php endif; ?>
</div>
</section>
<section class="content-header">
<h1>
Tableau de bord
<small>Statistiques des utilisateurs</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Accueil</a></li>
<li class="active"><a href="../">Tableau de bord</a></li>
</ol>
</section>
<section class="content">
<div class="box">
<?php if ($is_admin == true): ?>
<div class="row" id="dataToMap"></div>
<?php endif; ?>
</div>
</section>
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#dashboardMainMenu").addClass('active');
});
$(document).ready(function() {
$("#dashboardMainMenu").addClass('active');
});
// Convert PHP array to JavaScript array
const allUsers = <?php echo json_encode($allUsers); ?>;
console.log(allUsers);
// Function to render users
function renderUsers(users) {
const container = document.getElementById('dataToMap');
container.innerHTML = ''; // Clear existing content
users.forEach(user => {
const userHtml = `
<div class="col-lg-3 col-xs-6">
<div class="small-box" style="background-color: rgba(54, 162, 235, 0.5);">
<div class="inner">
<h3>${user.username}</h3>
<p>Produits vendues <b style="font-size: 20px;">${user.totalVente}</b></p>
</div>
<div class="icon">
<i class="ion ion-stats-bars"></i>
</div>
<a href="<?php echo base_url('statistic/') ?>${user.id}" class="small-box-footer">
Plus d'information <i class="fa fa-arrow-circle-right"></i>
</a>
</div>
</div>
`;
container.innerHTML += userHtml; // Append the user HTML to the container
});
}
const allUsers = <?php echo json_encode($allUsers); ?>;
function renderUsers(users) {
const container = document.getElementById('dataToMap');
container.innerHTML = '';
users.forEach(user => {
const userHtml = `
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
<div class="small-box">
<div class="inner">
<h3>${user.username}</h3>
<p>Produits vendus : <b>${user.totalVente}</b></p>
</div>
<div class="icon">
<i class="ion ion-stats-bars"></i>
</div>
<a href="<?php echo base_url('statistic/') ?>${user.id}" class="small-box-footer">
Plus d'informations <i class="fa fa-arrow-circle-right"></i>
</a>
</div>
</div>`;
container.innerHTML += userHtml;
});
}
// Render all users initially
renderUsers(allUsers);
</script>
renderUsers(allUsers);
</script>

1
app/Views/templates/header.php

@ -908,5 +908,6 @@
}
</style>
Loading…
Cancel
Save