diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 064f6389..728d45b5 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -166,6 +166,15 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
/**
* route for the products
*/
+ $routes->get('/product', function() {
+ return redirect()->to('/products');
+ });
+
+ // Route pour /product/(:any) qui redirige vers /products/(:any)
+ $routes->get('/product/(:any)', function($segment) {
+ return redirect()->to('/products/'.$segment);
+ });
+
$routes->group('/products', function ($routes) {
$routes->get('/', [ProductCOntroller::class, 'index']);
$routes->get('fetchProductData', [ProductCOntroller::class, 'fetchProductData']);
@@ -174,11 +183,11 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
$routes->get('update/(:num)', [ProductCOntroller::class, 'update']);
$routes->post('update/(:num)', [ProductCOntroller::class, 'update']);
$routes->post('remove', [ProductCOntroller::class, 'remove']);
+ $routes->get('generateqrcode/(:num)', [QrCodeCOntroller::class, 'generate']);
$routes->post('assign_store', [ProductCOntroller::class, 'assign_store']);
$routes->post('createByExcel', [ProductCOntroller::class, 'createByExcel']);
$routes->post('checkProductAvailability', [ProductCOntroller::class, 'checkProductAvailability']);
});
-
/**
* route for the orders
@@ -331,6 +340,8 @@ $routes->group('/avances', function ($routes) {
// Route CRON (optionnel)
$routes->get('checkDeadlineAlerts', [AvanceController::class, 'checkDeadlineAlerts']);
+
+
});
// historique
diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php
index 982004f5..17117f3a 100644
--- a/app/Controllers/Auth.php
+++ b/app/Controllers/Auth.php
@@ -46,32 +46,46 @@ class Auth extends AdminController
return $file ? $file->getErrorString() : 'No file was uploaded.';
}
- /**
- * function used to login
- * @return \CodeIgniter\HTTP\RedirectResponse
- */
- public function loginPost()
- {
- $email = $this->request->getPost('email');
- $password = $this->request->getPost('password');
-
- // Load the model and attempt login
- $userModel = new Users();
-
- $user = $userModel->attempt($email, $password);
-
- if ($user) {
- // Set user session
- session()->set('user', $user);
-
- // Redirect to dashboard
- return redirect()->to('/');
- }
-
- // If login fails, redirect back with an error
- return redirect()->to('/login')->with('error', 'Invalid email or password.');
+/**
+ * function used to login
+ * @return \CodeIgniter\HTTP\RedirectResponse
+ */
+public function loginPost()
+{
+ $email = $this->request->getPost('email');
+ $password = $this->request->getPost('password');
+
+ // Load the model and attempt login
+ $userModel = new Users();
+ $user = $userModel->attempt($email, $password);
+
+ if ($user) {
+ // Ajouter le nom complet et le rôle dans le tableau $user
+ $user['username'] = trim(($user['firstname'] ?? '') . ' ' . ($user['lastname'] ?? ''));
+ $user['role'] = $user['group_name'] ?? 'Aucun groupe';
+
+ // Set user session (garde la structure originale qui fonctionne)
+ session()->set('user', $user);
+
+ // Ajouter aussi les clés individuelles pour le header
+ session()->set([
+ 'user_id' => $user['id'],
+ 'username' => $user['username'],
+ 'role' => $user['role'],
+ 'email' => $user['email'] ?? '',
+ 'group_id' => $user['group_id'] ?? null,
+ 'store_id' => $user['store_id'] ?? null,
+ 'permission' => $user['permission'] ?? '',
+ 'logged_in' => true
+ ]);
+
+ // Redirect to dashboard
+ return redirect()->to('/');
}
+ // If login fails, redirect back with an error
+ return redirect()->to('/login')->with('error', 'Invalid email or password.');
+}
public function logout()
{
session()->destroy();
diff --git a/app/Controllers/AvanceController.php b/app/Controllers/AvanceController.php
index 7579badd..2e886ede 100644
--- a/app/Controllers/AvanceController.php
+++ b/app/Controllers/AvanceController.php
@@ -563,6 +563,7 @@ public function fetchExpiredAvance()
$type_avance = $this->request->getPost('type_avance_edit');
+ // ✅ VALIDATION
$validation = \Config\Services::validation();
$baseRules = [
@@ -602,11 +603,11 @@ public function fetchExpiredAvance()
]);
}
+ // ✅ VÉRIFICATIONS PERMISSIONS
$isAdmin = $this->isAdmin($users);
$isOwner = $users['id'] === $existingAvance['user_id'];
$isCaissier = $this->isCaissier($users);
- // ✅ MODIFIÉ : Le caissier peut maintenant modifier toutes les avances
if (!$isAdmin && !$isOwner && !$isCaissier) {
return $this->response->setJSON([
'success' => false,
@@ -614,6 +615,7 @@ public function fetchExpiredAvance()
]);
}
+ // ✅ GESTION DEADLINE
$current_deadline = $existingAvance['deadline'];
if ($type_avance !== $existingAvance['type_avance']) {
@@ -626,6 +628,7 @@ public function fetchExpiredAvance()
$old_product_id = $existingAvance['product_id'];
+ // ✅ PRÉPARER LES DONNÉES
$data = [
'type_avance' => $type_avance,
'type_payment' => $this->request->getPost('type_payment_edit'),
@@ -638,7 +641,8 @@ public function fetchExpiredAvance()
'avance_amount' => (float)$this->request->getPost('avance_amount_edit'),
'amount_due' => (float)$this->request->getPost('amount_due_edit')
];
-
+
+ // ✅ GESTION PRODUIT SELON TYPE
if ($type_avance === 'mere') {
$data['product_name'] = $this->request->getPost('product_name_text_edit');
$data['product_id'] = null;
@@ -651,22 +655,61 @@ public function fetchExpiredAvance()
$data['commentaire'] = null;
}
- if ($Avance->updateAvance($avance_id, $data)) {
+ // ✅ MISE À JOUR (qui déclenchera automatiquement la conversion si nécessaire)
+ $updateResult = $Avance->updateAvance($avance_id, $data);
+
+ if ($updateResult) {
+ // ✅ GESTION DES PRODUITS
if ($type_avance === 'terre') {
+ // Libérer l'ancien produit si changement
if ($old_product_id && $old_product_id !== $new_product_id) {
$Products->update($old_product_id, ['product_sold' => 0]);
}
+ // Marquer le nouveau produit comme vendu
if ($new_product_id) {
$Products->update($new_product_id, ['product_sold' => 1]);
}
} else {
+ // Si passage de terre à mer, libérer le produit
if ($old_product_id && $existingAvance['type_avance'] === 'terre') {
$Products->update($old_product_id, ['product_sold' => 0]);
}
}
- // ✅ Notification simplifiée
+ // ✅ VÉRIFIER SI CONVERSION AUTOMATIQUE
+ $updatedAvance = $Avance->find($avance_id);
+
+ if ($updatedAvance && $updatedAvance['is_order'] == 1) {
+ // ✅ L'avance a été convertie automatiquement !
+
+ // Trouver l'ID de la commande créée
+ $db = \Config\Database::connect();
+ $order = $db->table('orders')
+ ->select('id, bill_no')
+ ->where('customer_name', $updatedAvance['customer_name'])
+ ->where('customer_phone', $updatedAvance['customer_phone'])
+ ->where('source', 'Avance convertie')
+ ->orderBy('id', 'DESC')
+ ->limit(1)
+ ->get()
+ ->getRowArray();
+
+ if ($order) {
+ log_message('info', "✅ Avance {$avance_id} convertie automatiquement en commande {$order['id']}");
+
+ return $this->response->setJSON([
+ 'success' => true,
+ 'messages' => '✅ Avance modifiée et convertie automatiquement en commande !',
+ 'converted' => true,
+ 'order_id' => $order['id'],
+ 'bill_no' => $order['bill_no'],
+ 'redirect_url' => site_url('orders/update/' . $order['id'])
+ ]);
+ }
+ }
+
+ // ✅ NOTIFICATION (modification simple)
$Notification->createNotification(
'Une avance a été modifiée',
"Caissière",
@@ -685,7 +728,7 @@ public function fetchExpiredAvance()
'messages' => 'Erreur lors de la modification de l\'avance'
]);
}
-
+
} catch (\Exception $e) {
log_message('error', "Erreur modification avance: " . $e->getMessage());
return $this->response->setJSON([
@@ -694,7 +737,7 @@ public function fetchExpiredAvance()
]);
}
}
-
+
public function removeAvance()
{
@@ -2131,7 +2174,7 @@ public function payAvance()
]);
}
- // ✅ Vérifier si déjà convertie
+ // Vérifier si déjà convertie
if ($avance['is_order'] == 1) {
return $this->response->setJSON([
'success' => false,
@@ -2139,59 +2182,68 @@ public function payAvance()
]);
}
- // ✅ Calcul nouveau montant dû
+ // Calcul nouveau montant dû
$amount_due = max(0, (float)$avance['amount_due'] - $montant_paye);
- // ✅ Mise à jour avance
- $avanceModel->update($avance_id, [
+ // ✅ Utiliser updateAvance() qui va automatiquement vérifier la conversion
+ $updateResult = $avanceModel->updateAvance($avance_id, [
'avance_amount' => (float)$avance['avance_amount'] + $montant_paye,
'amount_due' => $amount_due,
]);
+ if (!$updateResult) {
+ return $this->response->setJSON([
+ 'success' => false,
+ 'message' => 'Erreur lors de la mise à jour'
+ ]);
+ }
+
log_message('info', "💰 Paiement {$montant_paye} Ar sur avance {$avance_id} (Type: {$avance['type_avance']})");
- // ✅ CONVERSION si paiement complet
- if ($amount_due <= 0) {
+ // ✅ Vérifier si l'avance a été convertie automatiquement
+ $updatedAvance = $avanceModel->find($avance_id);
+
+ if ($updatedAvance && $updatedAvance['is_order'] == 1) {
+ // ✅ Conversion automatique effectuée !
- if ($avance['type_avance'] === 'terre') {
- log_message('info', "🔄 Avance TERRE {$avance_id} complétée → Conversion en commande...");
-
- $order_id = $avanceModel->convertToOrder($avance_id);
-
- if ($order_id) {
- log_message('info', "✅ Conversion réussie → Commande {$order_id}");
-
- return $this->response->setJSON([
- 'success' => true,
- 'message' => '✅ Paiement effectué ! L\'avance a été convertie en commande.',
- 'converted' => true,
- 'type' => 'terre',
- 'order_id' => $order_id,
- 'redirect_url' => site_url('orders/update/' . $order_id)
- ]);
- } else {
- log_message('error', "❌ Échec conversion avance {$avance_id}");
-
- return $this->response->setJSON([
- 'success' => false,
- 'message' => '⚠️ Paiement enregistré mais erreur lors de la conversion. Contactez l\'administrateur.'
- ]);
- }
-
- } else {
- // ✅ Avance MER complète
- log_message('info', "✅ Avance MER {$avance_id} complétée (pas de conversion)");
+ // Trouver la commande
+ $db = \Config\Database::connect();
+ $order = $db->table('orders')
+ ->select('id, bill_no')
+ ->where('customer_name', $updatedAvance['customer_name'])
+ ->where('customer_phone', $updatedAvance['customer_phone'])
+ ->where('source', 'Avance convertie')
+ ->orderBy('id', 'DESC')
+ ->limit(1)
+ ->get()
+ ->getRowArray();
+
+ if ($order) {
+ log_message('info', "✅ Avance {$avance_id} convertie automatiquement en commande {$order['id']} après paiement");
return $this->response->setJSON([
- 'success' => true,
- 'message' => '✅ Paiement effectué ! L\'avance MER est maintenant complète.',
- 'converted' => false,
- 'type' => 'mere'
+ 'success' => true,
+ 'message' => '✅ Paiement effectué ! L\'avance a été automatiquement convertie en commande.',
+ 'converted' => true,
+ 'type' => 'terre',
+ 'order_id' => $order['id'],
+ 'bill_no' => $order['bill_no'],
+ 'redirect_url' => site_url('orders/update/' . $order['id'])
]);
}
}
- // ✅ Paiement partiel
+ // Paiement partiel ou avance MER complète
+ if ($amount_due <= 0 && $avance['type_avance'] === 'mere') {
+ return $this->response->setJSON([
+ 'success' => true,
+ 'message' => '✅ Paiement effectué ! L\'avance MER est maintenant complète.',
+ 'converted' => false,
+ 'type' => 'mere'
+ ]);
+ }
+
+ // Paiement partiel
return $this->response->setJSON([
'success' => true,
'message' => '✅ Paiement partiel enregistré',
@@ -2200,6 +2252,7 @@ public function payAvance()
'type' => $avance['type_avance']
]);
}
+
/**
* ✅ Conversion manuelle (optionnel - pour forcer la conversion)
*/
diff --git a/app/Controllers/Dashboard.php b/app/Controllers/Dashboard.php
index 42d37f73..933dd3de 100644
--- a/app/Controllers/Dashboard.php
+++ b/app/Controllers/Dashboard.php
@@ -21,7 +21,7 @@ class Dashboard extends AdminController
public function index()
{
- // === 🔥 Récupérer l'utilisateur en premier ===
+ // Récupérer l'utilisateur en premier
$session = session();
$user_id = $session->get('user');
diff --git a/app/Controllers/GroupController.php b/app/Controllers/GroupController.php
index c434b7de..f16d1aeb 100644
--- a/app/Controllers/GroupController.php
+++ b/app/Controllers/GroupController.php
@@ -138,37 +138,35 @@ class GroupController extends AdminController
}
public function delete(int $id = null)
- {
- $this->verifyRole('deleteGroup');
- $data['page_title'] = $this->pageTitle;
- $groupsModel = new Groups();
+{
+ $this->verifyRole('deleteGroup');
+ $data['page_title'] = $this->pageTitle;
+ $groupsModel = new Groups();
- if ($id) {
- if ($this->request->getMethod() === 'post' && $this->request->getPost('confirm')) {
- // Check if the group exists in the user group
- $check = $groupsModel->existInUserGroup($id);
- if ($check) {
- session()->setFlashdata('error', 'Group exists in the users');
- return redirect()->to('/groups');
- } else {
- // Delete group
- if ($groupsModel->delete($id)) {
- session()->setFlashdata('success', 'Successfully removed');
- return redirect()->to('/groups');
- } else {
- session()->setFlashdata('error', 'Error occurred!!');
- return redirect()->to("/groups/delete/{$id}");
- }
- }
- } else {
- // Show confirmation view
- $data['id'] = $id;
- return $this->render_template('groups/delete', $data);
- }
+ if (!$id) {
+ session()->setFlashdata('error', 'Invalid Group ID!');
+ return redirect()->to('/groups');
+ }
+
+ // Vérifier si c'est une requête POST avec confirmation
+ if ($this->request->getMethod() === 'post' && $this->request->getPost('confirm')) {
+
+ // Supprimer d'abord toutes les associations dans user_group
+ $groupsModel->removeUsersFromGroup($id);
+
+ // Puis supprimer le groupe
+ if ($groupsModel->deleteGroup($id)) {
+ session()->setFlashdata('success', 'Rôle supprimé avec succès');
+ return redirect()->to('/groups');
} else {
- session()->setFlashdata('error', 'Invalid Group ID!');
+ session()->setFlashdata('error', 'Une erreur est survenue lors de la suppression!');
return redirect()->to('/groups');
}
}
+
+ // Si ce n'est pas une requête POST, rediriger vers la liste
+ session()->setFlashdata('error', 'Action non autorisée!');
+ return redirect()->to('/groups');
+}
}
\ No newline at end of file
diff --git a/app/Controllers/MecanicienController.php b/app/Controllers/MecanicienController.php
index bf3aeecd..bc7db875 100644
--- a/app/Controllers/MecanicienController.php
+++ b/app/Controllers/MecanicienController.php
@@ -84,7 +84,7 @@ class MecanicienController extends AdminController
}
$image = ' ';
- $produit = $repa['sku'];
+ $produit = $repa['name'] . ' (' . $repa['sku'] . ')';
// Status display
$status = strReparation($repa['reparation_statut']);
$username = $repa['username'];
@@ -313,13 +313,22 @@ class MecanicienController extends AdminController
$session = session();
$users = $session->get('user');
+ // ✅ RÉCUPÉRER LES PARAMÈTRES DE FILTRE
+ $startDate = $this->request->getGet('startDate');
+ $endDate = $this->request->getGet('endDate');
+ $pvente = $this->request->getGet('pvente');
+
+ // Log pour débogage
+ log_message('debug', 'Filtres Mécanicien reçus - startDate: ' . $startDate . ', endDate: ' . $endDate . ', pvente: ' . $pvente);
+
$data['id'] = $users['id'];
- $reparation = $Mecanicien->getReparation($data['id']);
- $result = ['data' => []];
-
- // Iterate through the data
- if($users['group_name'] == "SuperAdmin" || $users['group_name'] == "Direction"){
+ // ✅ PASSER LES FILTRES AU MODÈLE
+ $reparation = $Mecanicien->getReparationWithFilters($data['id'], $startDate, $endDate, $pvente);
+
+ $result = ['data' => []];
+
+ if($users['group_name'] == "SuperAdmin" || $users['group_name'] == "Direction" || $users['group_name'] == "DAF"){
foreach ($reparation as $key => $repa) {
$image = ' ';
$produit = esc($repa['name']);
@@ -328,6 +337,7 @@ class MecanicienController extends AdminController
$user_name = $first_name . ' ' . $last_name;
$date_debut = date("d/m/Y", strtotime($repa['reparation_debut']));
$date_fin = date("d/m/Y", strtotime($repa['reparation_fin']));
+
// Add the row data
$result['data'][$key] = [
$user_name,
@@ -338,17 +348,15 @@ class MecanicienController extends AdminController
$date_fin,
];
}
- return $this->response->setJSON($result);
- }
- else{
+ } else {
foreach ($reparation as $key => $repa) {
$image = ' ';
$produit = $repa['name'];
- // Status display
$username = $repa['username'];
$date_debut = date("d/m/Y", strtotime($repa['reparation_debut']));
$date_fin = date("d/m/Y", strtotime($repa['reparation_fin']));
+
// Add the row data
$result['data'][$key] = [
$image,
@@ -358,11 +366,8 @@ class MecanicienController extends AdminController
$date_fin,
];
}
-
- // Return data in JSON format
- return $this->response->setJSON($result);
}
- // Iterate through the data
-
+
+ return $this->response->setJSON($result);
}
}
diff --git a/app/Controllers/ProductCOntroller.php b/app/Controllers/ProductCOntroller.php
index 3727e403..6d57c867 100644
--- a/app/Controllers/ProductCOntroller.php
+++ b/app/Controllers/ProductCOntroller.php
@@ -247,7 +247,7 @@ class ProductCOntroller extends AdminController
"Un nouveau Produit a été créé",
"COMMERCIALE",
$store_id1,
- 'product/'
+ 'products/'
);
return redirect()->to('/products');
diff --git a/app/Controllers/ReportController.php b/app/Controllers/ReportController.php
index 9f890473..97edd024 100644
--- a/app/Controllers/ReportController.php
+++ b/app/Controllers/ReportController.php
@@ -261,9 +261,18 @@ class ReportController extends AdminController
$session = session();
$users = $session->get('user');
- // Pour Direction et Conseil : afficher TOUTES les performances
- if ($users['group_name'] === "DAF" || $users['group_name'] === "Direction" ||$users['group_name'] === "SuperAdmin" ) {
- $orderPaid = $Orders->getPerformanceByOrders();
+ // ✅ RÉCUPÉRER LES PARAMÈTRES DE FILTRE
+ $startDate = $this->request->getGet('startDate');
+ $endDate = $this->request->getGet('endDate');
+ $pvente = $this->request->getGet('pvente');
+
+ // ✅ CORRECTION : Bonne concaténation des chaînes
+ log_message('debug', 'Filtres Commercial reçus - startDate: ' . $startDate . ', endDate: ' . $endDate . ', pvente: ' . $pvente);
+
+ // Pour Direction et Conseil : afficher TOUTES les performances AVEC FILTRES
+ if ($users['group_name'] === "DAF" || $users['group_name'] === "Direction" || $users['group_name'] === "SuperAdmin") {
+ // ✅ PASSER LES FILTRES AU MODÈLE - UNIQUEMENT POUR L'ADMIN
+ $orderPaid = $Orders->getPerformanceByOrders($startDate, $endDate, $pvente);
foreach ($orderPaid as $key => $value) {
// Déterminer le prix de vente réel
$prix_vente_reel = (!empty($value['discount']) && $value['discount'] > 0)
@@ -287,6 +296,7 @@ class ReportController extends AdminController
return $this->response->setJSON($result);
}
+ // ✅ POUR LES AUTRES RÔLES, GARDER L'ANCIENNE LOGIQUE SANS FILTRES
// Pour Cheffe d'Agence : performances de son magasin
if ($users['group_name'] === "Cheffe d'Agence") {
$orderPaid = $Orders->getPerformanceByOrders1();
@@ -305,6 +315,7 @@ class ReportController extends AdminController
}
return $this->response->setJSON($result);
}
+
if ($users['group_name'] === "Caissière") {
$orderPaid = $Orders->getPerformanceByCaissier($users['id']);
@@ -325,6 +336,7 @@ class ReportController extends AdminController
return $this->response->setJSON($result);
}
+
// Pour COMMERCIALE : uniquement ses propres ventes
if ($users['group_name'] === "COMMERCIALE") {
$orderPaid = $Orders->getPerformanceByOrders2();
diff --git a/app/Models/Avance.php b/app/Models/Avance.php
index 790584e7..e0cad91d 100644
--- a/app/Models/Avance.php
+++ b/app/Models/Avance.php
@@ -40,6 +40,7 @@ class Avance extends Model {
log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id);
return false;
}
+
try {
if (!empty($data['type']) && !empty($data['avance_date'])) {
if (strtolower($data['type']) === 'avance sur terre') {
@@ -48,12 +49,77 @@ class Avance extends Model {
$data['deadline'] = date('Y-m-d', strtotime($data['avance_date'] . ' +2 months'));
}
}
- return $this->update($id, $data);
+
+ // ✅ Mettre à jour l'avance
+ $updateResult = $this->update($id, $data);
+
+ if (!$updateResult) {
+ return false;
+ }
+
+ // ✅ AJOUT CRITIQUE : Vérifier automatiquement si conversion nécessaire
+ $this->autoCheckAndConvert($id);
+
+ return true;
+
} catch (\Exception $e) {
log_message('error', 'Erreur lors de la mise à jour de l\'avance : ' . $e->getMessage());
return false;
}
}
+
+ private function autoCheckAndConvert(int $avance_id)
+{
+ try {
+ // Recharger l'avance fraîchement mise à jour
+ $avance = $this->find($avance_id);
+
+ if (!$avance) {
+ log_message('warning', "⚠️ Avance {$avance_id} introuvable pour vérification auto");
+ return false;
+ }
+
+ // ✅ Conditions de conversion automatique
+ $shouldConvert = (
+ $avance['type_avance'] === 'terre' && // C'est une avance sur terre
+ (float)$avance['amount_due'] <= 0.01 && // Montant dû = 0 (avec tolérance)
+ $avance['is_order'] == 0 && // Pas encore convertie
+ $avance['active'] == 1 // Encore active
+ );
+
+ if ($shouldConvert) {
+ log_message('info', "🔄 [AUTO-CHECK] Avance {$avance_id} complète détectée ! Conversion automatique...");
+
+ // ✅ Appeler la conversion
+ $order_id = $this->convertToOrder($avance_id);
+
+ if ($order_id) {
+ log_message('info', "✅ [AUTO-CHECK] Conversion réussie → Commande {$order_id}");
+ return $order_id;
+ } else {
+ log_message('error', "❌ [AUTO-CHECK] Échec conversion avance {$avance_id}");
+ return false;
+ }
+ } else {
+ // Log pour débogage
+ $reason = '';
+ if ($avance['type_avance'] !== 'terre') $reason .= 'type=' . $avance['type_avance'] . ' ';
+ if ((float)$avance['amount_due'] > 0.01) $reason .= 'reste=' . $avance['amount_due'] . ' ';
+ if ($avance['is_order'] == 1) $reason .= 'déjà_convertie ';
+ if ($avance['active'] == 0) $reason .= 'inactive ';
+
+ if (!empty($reason)) {
+ log_message('info', "ℹ️ [AUTO-CHECK] Avance {$avance_id} non éligible pour conversion : {$reason}");
+ }
+ }
+
+ return false;
+
+ } catch (\Exception $e) {
+ log_message('error', "❌ [AUTO-CHECK] Erreur vérification avance {$avance_id}: " . $e->getMessage());
+ return false;
+ }
+}
public function getAllAvanceData(int $id=null) {
$session = session();
diff --git a/app/Models/Groups.php b/app/Models/Groups.php
index bf5db75a..f4b88d44 100644
--- a/app/Models/Groups.php
+++ b/app/Models/Groups.php
@@ -1,7 +1,5 @@
find($groupId); // Find by id
+ return $this->find($groupId);
}
-
- return $this->where('id !=', 1)->findAll(); // Get all groups except where id = 1
+ return $this->where('id !=', 1)->findAll();
}
/**
@@ -37,7 +33,7 @@ class Groups extends Model
*/
public function createGroup($data)
{
- return $this->insert($data); // Insert data into the groups table
+ return $this->insert($data);
}
/**
@@ -48,17 +44,17 @@ class Groups extends Model
*/
public function editGroup($data, $id)
{
- return $this->update($id, $data); // Update group by id
+ return $this->update($id, $data);
}
/**
* Delete group by id
* @param mixed $id
- * @return bool|\CodeIgniter\Database\BaseResult
+ * @return bool
*/
public function deleteGroup($id)
{
- return $this->delete($id); // Delete group by id
+ return $this->delete($id);
}
/**
@@ -71,6 +67,25 @@ class Groups extends Model
return $this->db->table('user_group')->where('group_id', $id)->countAllResults() > 0;
}
+ /**
+ * Count users in a specific group
+ * @param mixed $id
+ * @return int
+ */
+ public function countUsersInGroup($id)
+ {
+ return $this->db->table('user_group')->where('group_id', $id)->countAllResults();
+ }
+
+ /**
+ * Remove all users from a specific group
+ * @param mixed $id
+ * @return bool
+ */
+ public function removeUsersFromGroup($id)
+ {
+ return $this->db->table('user_group')->where('group_id', $id)->delete();
+ }
/**
* Get user group by userId
* @param mixed $userId
diff --git a/app/Models/Mecanicien.php b/app/Models/Mecanicien.php
index e5889440..c6559e7f 100644
--- a/app/Models/Mecanicien.php
+++ b/app/Models/Mecanicien.php
@@ -75,5 +75,38 @@ class Mecanicien extends Model
->get()
->getRow();
}
-
+ public function getReparationWithFilters(int $id = null, $startDate = null, $endDate = null, $pvente = null)
+ {
+ $session = session();
+ $user = $session->get('user');
+
+ $builder = $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.*, stores.name as store_name')
+ ->join('users', 'reparations.user_id = users.id')
+ ->join('products', 'reparations.produit_id = products.id')
+ ->join('stores', 'products.store_id = stores.id', 'left'); // ✅ JOINTURE AVEC MAGASINS
+
+ // Filtre par utilisateur si pas admin
+ if ($user['group_name'] != "SuperAdmin" && $user['group_name'] != "Direction" && $user['group_name'] != "DAF") {
+ $builder->where('users.id', $id);
+ }
+
+ // ✅ APPLIQUER LES FILTRES PAR DATE
+ if (!empty($startDate) && !empty($endDate)) {
+ $builder->where('DATE(reparations.reparation_debut) >=', $startDate);
+ $builder->where('DATE(reparations.reparation_debut) <=', $endDate);
+ } elseif (!empty($startDate)) {
+ $builder->where('DATE(reparations.reparation_debut) >=', $startDate);
+ } elseif (!empty($endDate)) {
+ $builder->where('DATE(reparations.reparation_debut) <=', $endDate);
+ }
+
+ // ✅ FILTRE PAR POINT DE VENTE
+ if (!empty($pvente) && $pvente !== 'TOUS') {
+ $builder->where('stores.name', $pvente);
+ }
+
+ $builder->orderBy('reparations.reparation_debut', 'DESC');
+
+ return $builder->findAll();
+ }
}
diff --git a/app/Models/Orders.php b/app/Models/Orders.php
index a8f4e0e5..cfddc929 100644
--- a/app/Models/Orders.php
+++ b/app/Models/Orders.php
@@ -110,8 +110,8 @@ class Orders extends Model
'orders.customer_phone',
'orders.customer_cin',
'orders.customer_address',
- 'orders.customer_type', // ✅ DÉJÀ PRÉSENT
- 'orders.source', // ✅ DÉJÀ PRÉSENT
+ 'orders.customer_type',
+ 'orders.source',
'orders.discount',
'orders.date_time',
'orders.gross_amount',
@@ -367,12 +367,12 @@ class Orders extends Model
$isAdmin = in_array($users['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
if($isAdmin) {
- return $this->whereIn('paid_status', [1, 3]) // ← Ajoutez 3 ici
+ return $this->whereIn('paid_status', [1, 3]) // ✅ DÉJÀ BON !
->orderBy('id', 'DESC')
->findAll();
}
else {
- return $this->whereIn('paid_status', [1, 3]) // ← Ajoutez 3 ici
+ return $this->whereIn('paid_status', [1, 3]) // ✅ DÉJÀ BON !
->where('store_id', $users['store_id'])
->orderBy('id', 'DESC')
->findAll();
@@ -598,19 +598,34 @@ class Orders extends Model
return $order;
}
- public function getPerformanceByOrders()
+ public function getPerformanceByOrders($startDate = null, $endDate = null, $pvente = null)
{
- $Performance = $this->db->table('orders')
+ $builder = $this->db->table('orders')
->select('orders.id as orderid, orders.net_amount, orders.date_time as datevente, orders.discount, products.price, products.sku, products.prix_vente, products.name as motoname, stores.id as store_id, users.firstname, users.lastname, users.email')
->join('stores', 'orders.store_id = stores.id')
->join('orders_item', 'orders.id = orders_item.order_id')
->join('products', 'products.id = orders_item.product_id')
->join('users', 'users.id = orders.user_id')
- ->whereIn('orders.paid_status', [1, 3])
- ->get()
- ->getResultArray();
-
- return $Performance;
+ ->whereIn('orders.paid_status', [1, 3]);
+
+ // ✅ AJOUT : FILTRES PAR DATE
+ if (!empty($startDate) && !empty($endDate)) {
+ $builder->where('DATE(orders.date_time) >=', $startDate);
+ $builder->where('DATE(orders.date_time) <=', $endDate);
+ } elseif (!empty($startDate)) {
+ $builder->where('DATE(orders.date_time) >=', $startDate);
+ } elseif (!empty($endDate)) {
+ $builder->where('DATE(orders.date_time) <=', $endDate);
+ }
+
+ // ✅ AJOUT : FILTRE PAR POINT DE VENTE
+ if (!empty($pvente) && $pvente !== 'TOUS') {
+ $builder->where('stores.name', $pvente);
+ }
+
+ $builder->orderBy('orders.date_time', 'DESC');
+
+ return $builder->get()->getResultArray();
}
// for Adminan cheffe d'agence
@@ -692,33 +707,46 @@ class Orders extends Model
public function getUserPerformanceToday(string $date)
{
+ $session = session();
+ $currentUser = $session->get('user');
+ $isAdmin = in_array($currentUser['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
+
$users = $this->getUserList();
$results = [];
foreach ($users as $user) {
- $summary = $this->db->table('orders')
+ $builder = $this->db->table('orders')
->select('COUNT(id) AS total_user_order, SUM(net_amount) AS total_prix_vente')
->where('user_id', $user->user_id)
->where('DATE(date_time)', $date)
- ->whereIn('paid_status', [1, 3])
- ->get()
- ->getRow();
+ ->whereIn('paid_status', [1, 3]);
+
+ // ✅ Filtre par magasin si pas admin
+ if (!$isAdmin && !empty($currentUser['store_id']) && $currentUser['store_id'] != 0) {
+ $builder->where('store_id', $currentUser['store_id']);
+ }
+
+ $summary = $builder->get()->getRow();
- $ids = $this->db->table('orders')
+ $idsBuilder = $this->db->table('orders')
->select('id')
->where('user_id', $user->user_id)
->where('DATE(date_time)', $date)
- ->whereIn('paid_status', [1, 3])
- ->get()
- ->getResult();
-
+ ->whereIn('paid_status', [1, 3]);
+
+ // ✅ Filtre par magasin si pas admin
+ if (!$isAdmin && !empty($currentUser['store_id']) && $currentUser['store_id'] != 0) {
+ $idsBuilder->where('store_id', $currentUser['store_id']);
+ }
+
+ $ids = $idsBuilder->get()->getResult();
$orderIds = array_map(fn($o) => $o->id, $ids);
$results[] = [
'user_id' => $user->user_id,
'full_name' => $user->full_name,
- 'total_user_order' => $summary->total_user_order,
- 'total_prix_vente' => $summary->total_prix_vente,
+ 'total_user_order' => $summary->total_user_order ?? 0,
+ 'total_prix_vente' => $summary->total_prix_vente ?? 0,
'order_ids' => $orderIds,
];
}
@@ -728,6 +756,10 @@ class Orders extends Model
public function getUserPerformanceByWeek(string $date = null)
{
+ $session = session();
+ $currentUser = $session->get('user');
+ $isAdmin = in_array($currentUser['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
+
$users = $this->getUserList();
$results = [];
@@ -740,24 +772,33 @@ class Orders extends Model
$endOfWeek = date('Y-m-d', strtotime('sunday this week', $timestamp));
foreach ($users as $user) {
- $summary = $this->db->table('orders')
+ $builder = $this->db->table('orders')
->select('COUNT(id) AS total_user_order, SUM(net_amount) AS total_prix_vente')
->where('user_id', $user->user_id)
->where('DATE(date_time) >=', $startOfWeek)
->where('DATE(date_time) <=', $endOfWeek)
- ->whereIn('paid_status', [1, 3])
- ->get()
- ->getRow();
+ ->whereIn('paid_status', [1, 3]);
+
+ // ✅ Filtre par magasin si pas admin
+ if (!$isAdmin && !empty($currentUser['store_id']) && $currentUser['store_id'] != 0) {
+ $builder->where('store_id', $currentUser['store_id']);
+ }
+
+ $summary = $builder->get()->getRow();
- $ids = $this->db->table('orders')
+ $idsBuilder = $this->db->table('orders')
->select('id')
->where('user_id', $user->user_id)
->where('DATE(date_time) >=', $startOfWeek)
->where('DATE(date_time) <=', $endOfWeek)
- ->whereIn('paid_status', [1, 3])
- ->get()
- ->getResult();
-
+ ->whereIn('paid_status', [1, 3]);
+
+ // ✅ Filtre par magasin si pas admin
+ if (!$isAdmin && !empty($currentUser['store_id']) && $currentUser['store_id'] != 0) {
+ $idsBuilder->where('store_id', $currentUser['store_id']);
+ }
+
+ $ids = $idsBuilder->get()->getResult();
$orderIds = array_map(fn($o) => $o->id, $ids);
$results[] = [
@@ -771,7 +812,6 @@ class Orders extends Model
return $results;
}
-
public function checkMinimalPrice($product_id, $discount)
{
$fourchetteModel = new FourchettePrix(); // plus court car on a "use"
@@ -787,6 +827,10 @@ public function checkMinimalPrice($product_id, $discount)
public function getUserPerformanceByMonth(string $month)
{
+ $session = session();
+ $currentUser = $session->get('user');
+ $isAdmin = in_array($currentUser['group_name'], ['SuperAdmin', 'Direction', 'DAF']);
+
$startOfMonth = date('Y-m-01', strtotime($month . '-01'));
$endOfMonth = date('Y-m-t', strtotime($month . '-01'));
@@ -794,24 +838,33 @@ public function getUserPerformanceByMonth(string $month)
$results = [];
foreach ($users as $user) {
- $orderData = $this->db->table('orders')
+ $builder = $this->db->table('orders')
->select('COUNT(id) as total_user_order, SUM(net_amount) as total_prix_vente')
->where('user_id', $user->user_id)
->where('DATE(date_time) >=', $startOfMonth)
->where('DATE(date_time) <=', $endOfMonth)
- ->whereIn('paid_status', [1, 3])
- ->get()
- ->getRow();
+ ->whereIn('paid_status', [1, 3]);
+
+ // ✅ Filtre par magasin si pas admin
+ if (!$isAdmin && !empty($currentUser['store_id']) && $currentUser['store_id'] != 0) {
+ $builder->where('store_id', $currentUser['store_id']);
+ }
+
+ $orderData = $builder->get()->getRow();
- $ids = $this->db->table('orders')
+ $idsBuilder = $this->db->table('orders')
->select('id')
->where('user_id', $user->user_id)
->where('DATE(date_time) >=', $startOfMonth)
->where('DATE(date_time) <=', $endOfMonth)
- ->whereIn('paid_status', [1, 3])
- ->get()
- ->getResult();
-
+ ->whereIn('paid_status', [1, 3]);
+
+ // ✅ Filtre par magasin si pas admin
+ if (!$isAdmin && !empty($currentUser['store_id']) && $currentUser['store_id'] != 0) {
+ $idsBuilder->where('store_id', $currentUser['store_id']);
+ }
+
+ $ids = $idsBuilder->get()->getResult();
$orderIds = array_map(fn($o) => $o->id, $ids);
$results[] = [
@@ -866,4 +919,6 @@ public function getPerformanceByCaissier(int $caissierId, $startDate = null, $en
->getResultArray();
}
+
+
}
\ No newline at end of file
diff --git a/app/Views/avances/avance.php b/app/Views/avances/avance.php
index f5e412d5..f18a8368 100644
--- a/app/Views/avances/avance.php
+++ b/app/Views/avances/avance.php
@@ -1205,7 +1205,7 @@ function editFunc(id) {
});
}
-// ✅ AJOUTER cette nouvelle fonction juste après editFunc
+// ✅ FONCTION AMÉLIORÉE pour remplir le modal d'édition
function populateEditModal(r, id) {
$('#avance_id_edit').val(r.avance_id || r.id || id);
@@ -1228,24 +1228,19 @@ function populateEditModal(r, id) {
// ✅ Gérer le produit selon le type
if (r.type_avance === 'mere') {
- // Pour avance MER : utiliser product_name (texte libre)
var productNameMer = r.product_name || '';
$('#product_name_text_edit').val(productNameMer);
} else if (r.type_avance === 'terre') {
- // Pour avance TERRE : utiliser product_id (select)
var productId = r.product_id || r.id_product;
- // ✅ Si le produit existe dans le select, le sélectionner
if (productId) {
setTimeout(function() {
$('#id_product_edit').val(productId);
- // ✅ Vérifier si le produit existe dans le select
if ($('#id_product_edit option:selected').val() == productId) {
console.log('Produit trouvé et sélectionné:', productId);
} else {
console.warn('Produit non trouvé dans le select:', productId);
- // ✅ Optionnel : Ajouter l'option si elle n'existe pas
var productNameFromDB = r.product_name_db || 'Produit #' + productId;
$('#id_product_edit').append(
$(' ')
@@ -1266,7 +1261,7 @@ function populateEditModal(r, id) {
$(this).off('shown.bs.modal');
});
- // ✅ Gestionnaire de soumission (reste identique)
+ // ✅ GESTIONNAIRE DE SOUMISSION AMÉLIORÉ AVEC CONVERSION AUTOMATIQUE
$('#update_avance_form').off('submit').on('submit', function(e) {
e.preventDefault();
@@ -1276,6 +1271,7 @@ function populateEditModal(r, id) {
var avance = unformatNumber($('#avance_amount_edit').val());
var brut = unformatNumber($('#gross_amount_edit').val());
+ // ✅ VALIDATION pour avance sur terre
if (typeAvance === 'terre') {
var minAvance = brutEdit * 0.5;
if (avance < minAvance) {
@@ -1290,6 +1286,7 @@ function populateEditModal(r, id) {
}
}
+ // ✅ VALIDATION pour avance sur mer
if (typeAvance === 'mere') {
if (!$('#product_name_text_edit').val() || brut === 0 || avance === 0) {
Swal.fire({
@@ -1318,15 +1315,38 @@ function populateEditModal(r, id) {
success: function(res) {
if (res.success === true) {
$('#updateModal').modal('hide');
- Swal.fire({
- icon: 'success',
- title: 'Succès',
- text: res.messages,
- timer: 2000,
- showConfirmButton: false
- }).then(() => {
- location.reload();
- });
+
+ // ✅ NOUVEAU : Gestion de la conversion automatique
+ if (res.converted === true && res.redirect_url) {
+ Swal.fire({
+ icon: 'success',
+ title: '🎉 Conversion automatique !',
+ html: '
' +
+ '
✅ Avance modifiée avec succès !
' +
+ '
🔄 L\'avance sur terre étant complète, elle a été automatiquement convertie en commande .
' +
+ '
' +
+ '
N° Commande : ' + (res.bill_no || 'N/A') + '
' +
+ '
Vous allez être redirigé vers la commande...
' +
+ '
',
+ timer: 4000,
+ timerProgressBar: true,
+ showConfirmButton: false,
+ allowOutsideClick: false
+ }).then(() => {
+ window.location.href = res.redirect_url;
+ });
+ } else {
+ // ✅ Modification simple sans conversion
+ Swal.fire({
+ icon: 'success',
+ title: 'Succès',
+ text: res.messages,
+ timer: 2000,
+ showConfirmButton: false
+ }).then(() => {
+ location.reload();
+ });
+ }
} else {
Swal.fire({
icon: 'error',
@@ -1335,7 +1355,8 @@ function populateEditModal(r, id) {
});
}
},
- error: function() {
+ error: function(xhr, status, error) {
+ console.error('Erreur AJAX:', error);
Swal.fire({
icon: 'error',
title: 'Erreur de connexion',
@@ -1475,42 +1496,5 @@ $(document).ready(function() {
\ No newline at end of file
diff --git a/app/Views/dashboard.php b/app/Views/dashboard.php
index 8eb41fe4..d5ddd411 100644
--- a/app/Views/dashboard.php
+++ b/app/Views/dashboard.php
@@ -1,44 +1,8 @@
@@ -60,502 +24,980 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
Ar
+
Totale en espèce
+
+
+
+
+
+
+
+
+
+
+
Ar
+
Totale en banque
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+ Total Orders (Ventes complètes)
+ Ar
+
+
+
+
+
+
+
+
+ Total Avances (Paiements partiels)
+ Ar
+
+
+
+
-
Commande payée
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Statistique des Commercials
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Date de début
+
+
+
+ Date de fin
+
+
+
+
+
+ Filtrer
+
+
+ Exporter
+
+
+
+
+
+
+
+
+ Caissier
+ Moto vendue
+ Date de vente
+ Prix de vente
+
+
+
+
+
+
+
+ Total :
+
+
+
+
+
+
+
-
-
-
-
-
-
Ar
-
Totale en espece
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
Ar
-
Totale en banque
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
+ Bienvenue sur votre tableau de bord sécurité.
+
+
+ Vous avez accès à la consultation du nombre total de produits en stock.
+ Pour plus de détails, cliquez sur "Plus d'information" dans la carte ci-dessus.
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filtrer
+
+
+ Aujourd'hui
+
+
+ Exporter
+
+
+
+
+
+
+ Motos
+ Désignation
+ Numéro de série
+ Début de la réparation
+ Fin de la réparation
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Statistique des Commercials
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Date de début
-
-
-
- Date de fin
-
-
-
- Points de ventes
-
- TOUS
-
- = $value['name']; ?>
-
-
-
-
-
-
- Filtrer
- 🔍
- Exporter
- 📤
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Date de début
-
-
-
- Date de fin
-
-
- get('user');
- if ($users["group_name"] === "Direction" || $users["group_name"] === "SuperAdmin" || $users["group_name"] === "DAF" ) :
-
- ?>
-
- Points de ventes
-
- TOUS
-
- = $value['name']; ?>
-
-
-
-
-
-
-
- Filtrer
- 🔍
- Exporter
- 📤
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
Ar
+
Totale en espece
+
+
+
+
+
+
+
+
+
+
+
+
Ar
+
Totale en banque
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filtrer
+
+
+ Aujourd'hui
+
+
+ Exporter
+
+
+
+ Points de ventes
+
+ TOUS
+
+ = $value['name']; ?>
+
+
+
+
+
+
+ Filtrer
+
+
+ Exporter
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Date de début
+
+
+
+ Date de fin
+
+
+ get('user');
+ if ($users["group_name"] === "Direction" || $users["group_name"] === "SuperAdmin" || $users["group_name"] === "DAF"):
+ ?>
+
+ Points de ventes
+
+ TOUS
+
+ = $value['name']; ?>
+
+
+
+
+
+
+
+ Filtrer
+
+
+ Exporter
+
+
+
+
+
+
+
+
+
+
+
-
-
+
- // Export it
- XLSX.writeFile(wb, 'export-commercial-performance.xlsx');
- });
- // Export en Excel mecanicien
- $('#ExportBTN1').on('click', function () {
- const table = document.getElementById('mecperformance');
- const wb = XLSX.utils.table_to_book(table, { sheet: "Feuille1" });
- XLSX.writeFile(wb, 'export-performance-mecanicien.xlsx');
- });
-
+
-
-