From 1c92093b90458cfedb0e45ac7d47d12cd6d4e920 Mon Sep 17 00:00:00 2001 From: Sarobidy22 Date: Mon, 13 Oct 2025 09:59:40 +0300 Subject: [PATCH] 13102025 --- app/Config/Routes.php | 30 +- app/Controllers/AvanceController.php | 163 +- app/Controllers/Dashboard.php | 112 +- app/Controllers/NotificationController.php | 8 +- app/Controllers/OrderController.php | 949 ++++++----- app/Controllers/ProductCOntroller.php | 221 ++- app/Controllers/RecouvrementController.php | 88 +- app/Controllers/RemiseController.php | 138 +- app/Controllers/ReportController.php | 99 +- app/Controllers/SortieCaisseController.php | 1513 +++++++++++++----- app/Models/Avance.php | 2 +- app/Models/Notification.php | 10 +- app/Models/Orders.php | 503 +++--- app/Models/Products.php | 23 +- app/Models/Recouvrement.php | 2 +- app/Models/Remise.php | 170 +- app/Models/Reports.php | 37 +- app/Models/SortieCaisse.php | 244 +-- app/Views/avances/avance.php | 522 +++--- app/Views/demande/index.php | 246 +-- app/Views/orders/avance.php | 2 +- app/Views/orders/create.php | 179 +++ app/Views/orders/createbyid.php | 107 +- app/Views/orders/edit.php | 223 ++- app/Views/orders/index.php | 299 ++-- app/Views/products/create.php | 41 +- app/Views/products/editbackup.php | 294 ++-- app/Views/securite/index.php | 1 - app/Views/sortieCaisse/index.php | 1664 ++++++++++++++------ app/Views/templates/header_menu.php | 237 ++- 30 files changed, 5302 insertions(+), 2825 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 963d73fd..86209a30 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -184,6 +184,7 @@ $routes->group('', ['filter' => 'auth'], function ($routes) { $routes->group('/orders', function ($routes) { $routes->get('/', [OrderController::class, 'index']); $routes->get('fetchOrdersData', [OrderController::class, 'fetchOrdersData']); + $routes->get('editAsNew/(:num)', 'OrderController::editAsNew/$1'); $routes->get('create', [OrderController::class, 'create']); $routes->post('create', [OrderController::class, 'create']); $routes->post('getProductValueById', [OrderController::class, 'getProductValueById']); @@ -194,11 +195,12 @@ $routes->group('', ['filter' => 'auth'], function ($routes) { $routes->get('printDivBL/(:num)', [OrderController::class, 'print7']); $routes->get('printDivBLF/(:num)', [OrderController::class, 'print31']); $routes->post('remove', [OrderController::class, 'remove']); + $routes->post('markAsDelivered', [OrderController::class, 'markAsDelivered']); // ← AJOUTEZ CETTE LIGNE ICI $routes->get('lookOrder/(:num)', [OrderController::class, 'lookOrder']); $routes->get('createFromEspace/(:num)', [OrderController::class, 'createById']); $routes->get('resrevation', [ReservationController::class, 'index']); + }); - /** * route for the reports */ @@ -261,19 +263,25 @@ $routes->group('', ['filter' => 'auth'], function ($routes) { $routes->post('markAsRead/(:num)', [NotificationController::class, 'markAsRead']); }); // routes for sortie caisse - $routes->group('/sortieCaisse', function ($routes) { - $routes->get('/', [SortieCaisseController::class, 'index']); - $routes->get('fetchSortieCaisseData', [SortieCaisseController::class, 'fetchSortieCaisseData']); - $routes->get('fetchSortieCaisseData1', [SortieCaisseController::class, 'fetchSortieCaisseData1']); - $routes->post('fetchSortieCaisseSingle/(:num)', [SortieCaisseController::class, 'fetchSortieCaisseSingle']); - $routes->post('createSortieCaisse', [SortieCaisseController::class, 'createSortieCaisse']); - // $routes->post('delete', [RecouvrementController::class, 'removeRecouvrement']); - $routes->post('updateSortieCaisse/(:num)', [SortieCaisseController::class, 'updateSortieCaisse']); - $routes->post('validateSortieCaisse/(:num)', [SortieCaisseController::class, 'validateSortieCaisse']); - }); +$routes->group('/sortieCaisse', function ($routes) { + $routes->get('/', [SortieCaisseController::class, 'index']); + $routes->get('fetchSortieCaisseData', [SortieCaisseController::class, 'fetchSortieCaisseData']); + $routes->get('fetchSortieCaisseData1', [SortieCaisseController::class, 'fetchSortieCaisseData1']); + $routes->post('fetchSortieCaisseSingle/(:num)', [SortieCaisseController::class, 'fetchSortieCaisseSingle']); + $routes->post('createSortieCaisse', [SortieCaisseController::class, 'createSortieCaisse']); + $routes->post('markAsPaid/(:num)', 'SortieCaisseController::markAsPaid/$1'); + $routes->post('updateSortieCaisse/(:num)', [SortieCaisseController::class, 'updateSortieCaisse']); + $routes->post('validateSortieCaisse/(:num)', [SortieCaisseController::class, 'validateSortieCaisse']); + + // ✅ Routes d'exportation + $routes->get('exportExcel', [SortieCaisseController::class, 'exportExcel']); + $routes->get('exportCsv', [SortieCaisseController::class, 'exportCsv']); + $routes->get('exportWithFilters', [SortieCaisseController::class, 'exportWithFilters']); +}); // remise $routes->group('/remise', function ($routes) { + $routes->get('', [RemiseController::class, 'index']); $routes->get('/', [RemiseController::class, 'index']); $routes->get('fetchRemiseData', [RemiseController::class, 'fetchRemiseData']); // $routes->post('delete', [RecouvrementController::class, 'removeRecouvrement']); diff --git a/app/Controllers/AvanceController.php b/app/Controllers/AvanceController.php index 1da9c316..69bb6394 100644 --- a/app/Controllers/AvanceController.php +++ b/app/Controllers/AvanceController.php @@ -63,19 +63,25 @@ class AvanceController extends AdminController . ''; } + return $buttons; } private function buildDataRow($value, $product, $isAdmin, $isCommerciale, $isCaissier, $buttons) { $date_time = date('d-m-Y h:i a', strtotime($value['avance_date'])); + + // ✅ Afficher product_name si disponible, sinon récupérer depuis la BDD + $productName = !empty($value['product_name']) + ? $value['product_name'] + : $product->getProductNameById($value['product_id']); if ($isAdmin) { return [ $value['customer_name'], $value['customer_phone'], $value['customer_address'], - $product->getProductNameById($value['product_id']), + $productName, // ✅ Utiliser la variable number_format((int)$value['gross_amount'], 0, ',', ' '), number_format((int)$value['avance_amount'], 0, ',', ' '), number_format((int)$value['amount_due'], 0, ',', ' '), @@ -85,7 +91,7 @@ class AvanceController extends AdminController } elseif ($isCommerciale || $isCaissier) { return [ $value['avance_id'], - $product->getProductNameById($value['product_id']), + $productName, // ✅ Utiliser la variable number_format((int)$value['avance_amount'], 0, ',', ' '), number_format((int)$value['amount_due'], 0, ',', ' '), $date_time, @@ -354,14 +360,14 @@ class AvanceController extends AdminController public function createAvance() { $this->verifyRole('createAvance'); - + if ($this->request->getMethod() !== 'post') { return $this->response->setJSON([ 'success' => false, 'messages' => 'Méthode non autorisée' ]); } - + try { $session = session(); $users = $session->get('user'); @@ -369,37 +375,51 @@ class AvanceController extends AdminController $Avance = new Avance(); $Products = new Products(); $Notification = new NotificationController(); - + + // ✅ Récupérer le type AVANT la validation + $type_avance = $this->request->getPost('type_avance'); + + // ✅ Validation conditionnelle $validation = \Config\Services::validation(); - $validation->setRules([ + + $baseRules = [ 'customer_name_avance' => 'required|min_length[2]', 'customer_phone_avance' => 'required', 'customer_address_avance' => 'required', 'customer_cin_avance' => 'required', - 'id_product' => 'required|numeric', + 'gross_amount' => 'required|numeric|greater_than[0]', 'avance_amount' => 'required|numeric|greater_than[0]', - 'type_avance' => 'required|in_list[terre,mere]' - ]); - + 'type_avance' => 'required|in_list[terre,mere]', + 'type_payment' => 'required' + ]; + + if ($type_avance === 'mere') { + $baseRules['product_name_text'] = 'required|min_length[2]'; + } else { + $baseRules['id_product'] = 'required|numeric'; + } + + $validation->setRules($baseRules); + if (!$validation->withRequest($this->request)->run()) { return $this->response->setJSON([ 'success' => false, 'messages' => 'Données invalides: ' . implode(', ', $validation->getErrors()) ]); } - + $avance_date = date('Y-m-d H:i:s'); - - // Calcul automatique de la deadline selon le type d'avance - $type_avance = $this->request->getPost('type_avance'); + + // Calcul de la deadline if ($type_avance === 'terre') { $deadline = date('Y-m-d', strtotime($avance_date . ' +15 days')); } elseif ($type_avance === 'mere') { $deadline = date('Y-m-d', strtotime($avance_date . ' +2 months')); } else { - $deadline = null; // fallback si jamais + $deadline = null; } - + + // Préparer les données communes $data = [ 'type_avance' => $type_avance, 'type_payment' => $this->request->getPost('type_payment'), @@ -411,16 +431,30 @@ class AvanceController extends AdminController 'deadline' => $deadline, 'user_id' => $users['id'], 'store_id' => $users['store_id'], - 'product_id' => (int)$this->request->getPost('id_product'), 'gross_amount' => (float)$this->request->getPost('gross_amount'), 'avance_amount' => (float)$this->request->getPost('avance_amount'), - 'amount_due' => (float)$this->request->getPost('amount_due'), + 'amount_due' => (float)$this->request->getPost('gross_amount') - (float)$this->request->getPost('avance_amount'), 'is_order' => 0, 'active' => 1, ]; - + + // ✅ Ajouter le produit selon le type + if ($type_avance === 'mere') { + $data['product_name'] = $this->request->getPost('product_name_text'); + // $data['product_id'] = null; + $data['commentaire'] = $this->request->getPost('commentaire'); + } else { + $data['product_id'] = (int)$this->request->getPost('id_product'); + $data['product_name'] = null; + $data['commentaire'] = null; + } + if ($avance_id = $Avance->createAvance($data)) { - $Products->update($data['product_id'], ['product_sold' => 1]); + + // ✅ Marquer le produit comme vendu UNIQUEMENT pour "terre" + if ($type_avance === 'terre' && !empty($data['product_id'])) { + $Products->update($data['product_id'], ['product_sold' => 1]); + } $Notification->createNotification( 'Une nouvelle avance a été créée', @@ -444,10 +478,11 @@ class AvanceController extends AdminController log_message('error', "Erreur création avance: " . $e->getMessage()); return $this->response->setJSON([ 'success' => false, - 'messages' => 'Une erreur interne est survenue' + 'messages' => 'Une erreur interne est survenue: ' . $e->getMessage() ]); } } + public function updateAvance() { $this->verifyRole('updateAvance'); @@ -467,18 +502,31 @@ class AvanceController extends AdminController $Products = new Products(); $Notification = new NotificationController(); - // Validation des données (avec les vrais noms des champs du formulaire) + // ✅ Récupérer le type AVANT la validation + $type_avance = $this->request->getPost('type_avance_edit'); + + // ✅ Validation conditionnelle selon le type $validation = \Config\Services::validation(); - $validation->setRules([ + + $baseRules = [ 'id' => 'required|numeric', 'customer_name_avance_edit' => 'required|min_length[2]', 'customer_phone_avance_edit' => 'required', 'customer_address_avance_edit' => 'required', 'customer_cin_avance_edit' => 'required', - 'id_product_edit' => 'required|numeric', + 'gross_amount_edit' => 'required|numeric|greater_than[0]', 'avance_amount_edit' => 'required|numeric|greater_than[0]', - 'type_avance_edit' => 'required|in_list[terre,mere]' - ]); + 'type_avance_edit' => 'required|in_list[terre,mere]', + 'type_payment_edit' => 'required' + ]; + + if ($type_avance === 'mere') { + $baseRules['product_name_text_edit'] = 'required|min_length[2]'; + } else { + $baseRules['id_product_edit'] = 'required|numeric'; + } + + $validation->setRules($baseRules); if (!$validation->withRequest($this->request)->run()) { return $this->response->setJSON([ @@ -487,7 +535,7 @@ class AvanceController extends AdminController ]); } - $avance_id = $this->request->getPost('id'); // Changement ici + $avance_id = $this->request->getPost('id'); // Vérifier si l'avance existe $existingAvance = $Avance->fetchSingleAvance($avance_id); @@ -510,10 +558,8 @@ class AvanceController extends AdminController } // Recalculer la deadline si le type d'avance a changé - $type_avance = $this->request->getPost('type_avance_edit'); // Changement ici $current_deadline = $existingAvance['deadline']; - // Si le type a changé, recalculer la deadline if ($type_avance !== $existingAvance['type_avance']) { if ($type_avance === 'terre') { $current_deadline = date('Y-m-d', strtotime($existingAvance['avance_date'] . ' +15 days')); @@ -523,34 +569,54 @@ class AvanceController extends AdminController } $old_product_id = $existingAvance['product_id']; - $new_product_id = (int)$this->request->getPost('id_product_edit'); // Changement ici + // Préparer les données communes $data = [ 'type_avance' => $type_avance, - 'type_payment' => $this->request->getPost('type_payment_edit'), // Changement ici - 'customer_name' => $this->request->getPost('customer_name_avance_edit'), // Changement ici - 'customer_address' => $this->request->getPost('customer_address_avance_edit'), // Changement ici - 'customer_phone' => $this->request->getPost('customer_phone_avance_edit'), // Changement ici - 'customer_cin' => $this->request->getPost('customer_cin_avance_edit'), // Changement ici + 'type_payment' => $this->request->getPost('type_payment_edit'), + 'customer_name' => $this->request->getPost('customer_name_avance_edit'), + 'customer_address' => $this->request->getPost('customer_address_avance_edit'), + 'customer_phone' => $this->request->getPost('customer_phone_avance_edit'), + 'customer_cin' => $this->request->getPost('customer_cin_avance_edit'), 'deadline' => $current_deadline, - 'product_id' => $new_product_id, - 'gross_amount' => (float)$this->request->getPost('gross_amount_edit'), // Changement ici - 'avance_amount' => (float)$this->request->getPost('avance_amount_edit'), // Changement ici - 'amount_due' => (float)$this->request->getPost('amount_due_edit'), // Changement ici + 'gross_amount' => (float)$this->request->getPost('gross_amount_edit'), + 'avance_amount' => (float)$this->request->getPost('avance_amount_edit'), + 'amount_due' => (float)$this->request->getPost('amount_due_edit'), ]; + // ✅ Gérer le produit selon le type + if ($type_avance === 'mere') { + $data['product_name'] = $this->request->getPost('product_name_text_edit'); + $data['product_id'] = null; // ⚠️ Sera NULL si la colonne l'accepte + $data['commentaire'] = $this->request->getPost('commentaire_edit'); + $new_product_id = null; + } else { + $new_product_id = (int)$this->request->getPost('id_product_edit'); + $data['product_id'] = $new_product_id; + $data['product_name'] = null; + $data['commentaire'] = null; + } + // Mettre à jour l'avance if ($Avance->updateAvance($avance_id, $data)) { - // Gérer le changement de produit si nécessaire - if ($old_product_id !== $new_product_id) { - // Libérer l'ancien produit - $Products->update($old_product_id, ['product_sold' => 0]); - // Marquer le nouveau produit comme vendu - $Products->update($new_product_id, ['product_sold' => 1]); + // ✅ Gérer le changement de produit UNIQUEMENT pour "terre" + if ($type_avance === 'terre') { + if ($old_product_id && $old_product_id !== $new_product_id) { + // Libérer l'ancien produit + $Products->update($old_product_id, ['product_sold' => 0]); + } + if ($new_product_id) { + // Marquer le nouveau produit comme vendu + $Products->update($new_product_id, ['product_sold' => 1]); + } + } else { + // Si on passe de "terre" à "mere", libérer l'ancien produit + if ($old_product_id && $existingAvance['type_avance'] === 'terre') { + $Products->update($old_product_id, ['product_sold' => 0]); + } } - // Créer une notification $Notification->createNotification( 'Une avance a été modifiée', "Conseil", @@ -574,11 +640,12 @@ class AvanceController extends AdminController log_message('error', "Erreur modification avance: " . $e->getMessage()); return $this->response->setJSON([ 'success' => false, - 'messages' => 'Une erreur interne est survenue' + 'messages' => 'Une erreur interne est survenue: ' . $e->getMessage() ]); } } - + + public function removeAvance() { $this->verifyRole('deleteAvance'); diff --git a/app/Controllers/Dashboard.php b/app/Controllers/Dashboard.php index 3210d8fc..f7266b7d 100644 --- a/app/Controllers/Dashboard.php +++ b/app/Controllers/Dashboard.php @@ -10,6 +10,7 @@ use App\Models\Stores; use App\Models\Users; use App\Models\Recouvrement; use App\Models\SortieCaisse; + class Dashboard extends AdminController { @@ -32,71 +33,78 @@ class Dashboard extends AdminController $totalRecouvrement = $Recouvrement->getTotalRecouvrements(); $sortieCaisse = new SortieCaisse(); $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse(); - $total_sortie_caisse1= $total_sortie_caisse->mr; - // Recouvrements - $total_recouvrement_me = $totalRecouvrement->me; - $total_recouvrement_bm = $totalRecouvrement->bm; - $total_recouvrement_be = $totalRecouvrement->be; - $total_recouvrement_mb = $totalRecouvrement->mb; + // === EXTRACTION DES SORTIES PAR MODE DE PAIEMENT === + $total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0; + $total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0; + $total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0; + + // === TOTAUX RECOUVREMENT === + $me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0; + $bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0; + $be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0; + $mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0; // Avances $Avance = new Avance(); $totalAvance = $Avance->getTotalAvance(); $paymentDataAvance = $Avance->getPaymentModesAvance(); - // Initialisation des totaux Orders (en utilisant les bonnes propriétés) - $total_orders = isset($paymentData->total) ? $paymentData->total : 0; - $total_mvola1_orders = isset($paymentData->total_mvola1) ? $paymentData->total_mvola1 : 0; - $total_mvola2_orders = isset($paymentData->total_mvola2) ? $paymentData->total_mvola2 : 0; - $total_espece1_orders = isset($paymentData->total_espece1) ? $paymentData->total_espece1 : 0; - $total_espece2_orders = isset($paymentData->total_espece2) ? $paymentData->total_espece2 : 0; - $total_virement_bancaire1_orders = isset($paymentData->total_virement_bancaire1) ? $paymentData->total_virement_bancaire1 : 0; - $total_virement_bancaire2_orders = isset($paymentData->total_virement_bancaire2) ? $paymentData->total_virement_bancaire2 : 0; - - // Initialisation des totaux Avances (utiliser les bonnes propriétés de getPaymentModesAvance) - $total_avances = isset($paymentDataAvance->total) ? $paymentDataAvance->total : 0; - $total_mvola_avances = isset($paymentDataAvance->total_mvola) ? $paymentDataAvance->total_mvola : 0; - $total_espece_avances = isset($paymentDataAvance->total_espece) ? $paymentDataAvance->total_espece : 0; - $total_virement_bancaire_avances = isset($paymentDataAvance->total_virement_bancaire) ? $paymentDataAvance->total_virement_bancaire : 0; - - // Addition Orders + Avances + // === TOTAUX PAIEMENTS ORDERS === + $total_orders = isset($paymentData->total) ? (float) $paymentData->total : 0; + $mv1_orders = isset($paymentData->total_mvola1) ? (float) $paymentData->total_mvola1 : 0; + $mv2_orders = isset($paymentData->total_mvola2) ? (float) $paymentData->total_mvola2 : 0; + $es1_orders = isset($paymentData->total_espece1) ? (float) $paymentData->total_espece1 : 0; + $es2_orders = isset($paymentData->total_espece2) ? (float) $paymentData->total_espece2 : 0; + $vb1_orders = isset($paymentData->total_virement_bancaire1) ? (float) $paymentData->total_virement_bancaire1 : 0; + $vb2_orders = isset($paymentData->total_virement_bancaire2) ? (float) $paymentData->total_virement_bancaire2 : 0; + + // === TOTAUX PAIEMENTS AVANCES === + $total_avances = isset($paymentDataAvance->total) ? (float) $paymentDataAvance->total : 0; + $mv_avances = isset($paymentDataAvance->total_mvola) ? (float) $paymentDataAvance->total_mvola : 0; + $es_avances = isset($paymentDataAvance->total_espece) ? (float) $paymentDataAvance->total_espece : 0; + $vb_avances = isset($paymentDataAvance->total_virement_bancaire) ? (float) $paymentDataAvance->total_virement_bancaire : 0; + + // === COMBINAISON ORDERS + AVANCES === + $total_mvola = $mv1_orders + $mv2_orders + $mv_avances; + $total_espece = $es1_orders + $es2_orders + $es_avances; + $total_vb = $vb1_orders + $vb2_orders + $vb_avances; $total = $total_orders + $total_avances; - // Combiner les totaux par type de paiement - $total_mvola1 = $total_mvola1_orders; - $total_mvola2 = $total_mvola2_orders; - $total_espece1 = $total_espece1_orders; - $total_espece2 = $total_espece2_orders; - $total_virement_bancaire1 = $total_virement_bancaire1_orders; - $total_virement_bancaire2 = $total_virement_bancaire2_orders; - - // Totaux combinés Orders - $total_mvola_orders_combined = $total_mvola1 + $total_mvola2; - $total_espece_orders_combined = $total_espece1 + $total_espece2; - $total_virement_bancaire_orders_combined = $total_virement_bancaire1 + $total_virement_bancaire2; - - // Totaux finaux (Orders + Avances) - $total_mvola = $total_mvola_orders_combined + $total_mvola_avances; - $total_espece = $total_espece_orders_combined + $total_espece_avances; - $total_virement_bancaire = $total_virement_bancaire_orders_combined + $total_virement_bancaire_avances; - - // Ajustements avec les recouvrements et sorties caisse - $total_mvola_final = $total_mvola - $total_recouvrement_me - $total_recouvrement_mb + $total_recouvrement_bm; - $total_espece_final = $total_espece + $total_recouvrement_me + $total_recouvrement_be - $total_sortie_caisse1; - $total_virement_bancaire_final = $total_virement_bancaire - $total_recouvrement_be - $total_recouvrement_bm + $total_recouvrement_mb; + // === AJUSTEMENTS AVEC RECOUVREMENTS ET SORTIES (PAR MODE DE PAIEMENT) === + $total_mvola_final = $total_mvola - + $me - + $mb + + $bm - + $total_sortie_mvola; + $total_espece_final = $total_espece + + $me + + $be - + $total_sortie_espece; + + $total_virement_bancaire_final = $total_vb - + $be - + $bm + + $mb - + $total_sortie_virement; + + // === CALCUL DU TOTAL GÉNÉRAL === + // ✅ CORRECTION: Ne PAS additionner les recouvrements au total + // Les recouvrements sont des transferts internes, pas des entrées d'argent + $total_sortie_global = $total_sortie_espece + $total_sortie_mvola + $total_sortie_virement; + $total_final = $total - $total_sortie_global; + // check avance expired $avance = new Avance(); $avance->checkExpiredAvance(); - + $data = [ - 'total' => $total, + 'total' => $total_final, 'total_mvola' => $total_mvola_final, 'total_espece' => $total_espece_final, 'total_virement_bancaire' => $total_virement_bancaire_final, 'user_permission' => $this->permission, - ]; $data['total_products'] = $productModel->countTotalProducts(); @@ -144,10 +152,10 @@ class Dashboard extends AdminController // filter to keep non empty array $filteredArray = array_filter($newData, function($item) { - return !empty($item); // Keep only non-empty arrays + return !empty($item); }); - // Re-index the array (optional, if you want sequential keys) + // Re-index the array $userWhoSoldProducts = array_values($filteredArray); // Count occurrences of each userId @@ -166,6 +174,7 @@ class Dashboard extends AdminController } $data['count_id'] = $countId; + // Check if the user is an Conseil $session = session(); $user_id = $session->get('user'); @@ -174,6 +183,7 @@ class Dashboard extends AdminController $data['isChef'] = false; $data['isCaissier'] = false; $data['isMecanicien'] = false; + if ($user_id['group_name'] == "Direction" || $user_id['group_name'] == "Conseil") { $data['is_admin'] = true; } @@ -186,16 +196,18 @@ class Dashboard extends AdminController } if ($user_id['group_name'] == "Cheffe d'Agence") { $data['isChef'] = true; - } + } if ($user_id['group_name'] == "Caissière") { $data['isCaissier'] = true; } if ($user_id['group_name'] == "MECANICIEN") { $data['isMecanicien'] = true; } + $data['page_title'] = 'Dashboard'; $data['marques_total'] = json_encode($orderModel->getTotalProductvente()); $data['marques'] = json_encode($Brancds->getName()); + $Orders = new Orders(); $Products = new Products(); $Stores = new Stores(); diff --git a/app/Controllers/NotificationController.php b/app/Controllers/NotificationController.php index 49222b02..b271602b 100644 --- a/app/Controllers/NotificationController.php +++ b/app/Controllers/NotificationController.php @@ -11,30 +11,30 @@ class NotificationController extends AdminController parent::__construct(); } + // Récupérer toutes les notifications selon les règles définies dans le modèle public function getNotification() { $Notification = new Notification(); - $notifications = $Notification->getNotifications(); - return $this->response->setJSON($notifications); } + // Marquer une notification comme lue public function markAsRead(int $id) { $Notification = new Notification(); $Notification->markAsRead($id); - return $this->response->setJSON(['status' => 'success']); } + // Créer une nouvelle notification public function createNotification(string $message, string $group, ?int $store_id, ?string $link) { $Notification = new Notification(); $data = [ 'message' => $message, - 'is_read' => 0, + 'is_read' => 0, 'forgroup' => $group, 'store_id' => $store_id, 'link' => $link, diff --git a/app/Controllers/OrderController.php b/app/Controllers/OrderController.php index 82e6c660..b206a79c 100644 --- a/app/Controllers/OrderController.php +++ b/app/Controllers/OrderController.php @@ -14,6 +14,8 @@ use App\Models\Products; use App\Models\Attributes; use App\Models\OrderItems; use App\Models\Remise; +use App\Models\FourchettePrix; + use PhpParser\Node\Stmt\Else_; @@ -36,27 +38,27 @@ class OrderController extends AdminController public function fetchOrdersData() { - // Load the required helpers helper(['url', 'form']); $Orders = new Orders(); $result = ['data' => []]; - // Fetch orders data from the model $data = $Orders->getOrdersData(); $session = session(); $users = $session->get('user'); - if($users['group_name'] == "Caissière"){ + + if ($users['group_name'] == "Caissière") { foreach ($data as $key => $value) { - // $count_total_item = $Orders->countOrderItem($value['id']); - $date_time = date('d-m-Y h:i a', strtotime($value['date_time'])); // Combine date and time formatting + $date_time = date('d-m-Y h:i a', strtotime($value['date_time'])); - // Initialize buttons $buttons = ''; - if (in_array('viewOrder', $this->permission)) { + // Bouton imprimer (sauf pour SECURITE) + if (in_array('viewOrder', $this->permission) && $users['group_name'] != "SECURITE" && $users['group_name'] != "COMMERCIALE") { $buttons .= ''; } + + // Bouton voir if (in_array('viewOrder', $this->permission)) { $buttons .= ' '; } + // Bouton modifier if (in_array('updateOrder', $this->permission) && $users["store_id"] == $value['store_id']) { $buttons .= ' '; } - // Paid status label - $paid_status = ($value['paid_status'] == 1) - ? 'Payé' - : 'Non payé'; + // Statut de paiement + if ($value['paid_status'] == 1) { + $paid_status = 'Validé'; + } elseif ($value['paid_status'] == 2) { + $paid_status = 'En Attente'; + } elseif ($value['paid_status'] == 3) { + $paid_status = 'Validé et Livré'; + } else { + $paid_status = 'Refusé'; + } + // Calcul délai (SANS notification ici) $date1 = new DateTime($date_time); - $date2 = new DateTime(); // Current date and time - - // Calculate the difference in days + $date2 = new DateTime(); $interval = $date1->diff($date2); $daysPassed = $interval->days; - $statuDate = ''; - // die(var_dump($daysPassed)); - $Notification = new NotificationController(); - // die(var_dump($_SERVER['REQUEST_URI'] == "/orders/fetchOrdersData")); - $uri = $_SERVER['REQUEST_URI']; - if ($daysPassed < 8 && $value['paid_status'] == 2) { - $statuDate = ' depuis ' . $daysPassed . ' Jours'; - } else if ($daysPassed >= 8 && $value['paid_status'] == 2) { - $Notification->createNotification("Conseil : Confirmation de reservation", "TOUS", (int)$value['store_id'], str_contains($uri, "fetchOrdersData") ? 'orders/#' : 'orders'); - $statuDate = ' depuis ' . $daysPassed . ' Jours'; - } else if ($daysPassed >= 15 && $value['paid_status'] == 2) { - $Notification->createNotification("Conseil : Reservation expiré", "TOUS", (int)$value['store_id'], str_contains($uri, "fetchOrdersData") ? 'orders/#' : 'orders'); - $statuDate = ' depuis ' . $daysPassed . ' Jours'; + $statuDate = ''; + if ($value['paid_status'] == 2) { + if ($daysPassed < 8) { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } elseif ($daysPassed >= 8 && $daysPassed < 15) { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } else { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } } - $Orders_items= new OrderItems(); - $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']); - // Add data to the result array + // $Orders_items = new OrderItems(); + // $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']); + $result['data'][$key] = [ $value['product_names'], $value['user_name'], $date_time . "
" . $statuDate, - $sum_order_item, - number_format((int) $value['net_amount'], 0, ',', ' '), + number_format((int) $value['discount'], 0, ',', ' '), + number_format((int) $value['gross_amount'], 0, ',', ' '), $paid_status, $buttons ]; } return $this->response->setJSON($result); - } - else if($users['group_name'] == "Direction" || $users['group_name'] == "Conseil"){ + } + + elseif($users['group_name'] == "Direction" || $users['group_name'] == "DAF"){ foreach ($data as $key => $value) { - // $count_total_item = $Orders->countOrderItem($value['id']); - $date_time = date('d-m-Y h:i a', strtotime($value['date_time'])); // Combine date and time formatting + $date_time = date('d-m-Y h:i a', strtotime($value['date_time'])); - // Initialize buttons $buttons = ''; - if (in_array('viewOrder', $this->permission)) { + // Bouton imprimer (sauf pour SECURITE) + if (in_array('viewOrder', $this->permission) && $users['group_name'] != "SECURITE" && $users['group_name'] != "COMMERCIALE") { $buttons .= ''; } + if (in_array('updateOrder', $this->permission)) { $buttons .= ' '; @@ -135,66 +140,70 @@ class OrderController extends AdminController $buttons .= ' '; } - // Paid status label - $paid_status = ($value['paid_status'] == 1) - ? 'Payé' - : 'Non payé'; + - $date1 = new DateTime($date_time); - $date2 = new DateTime(); // Current date and time + + // Statut de paiement + if ($value['paid_status'] == 1) { + $paid_status = 'Validé'; + } elseif ($value['paid_status'] == 2) { + $paid_status = 'En Attente'; + } elseif ($value['paid_status'] == 3) { + $paid_status = 'Validé et Livré'; + } else { + $paid_status = 'Refusé'; + } - // Calculate the difference in days + // Calcul délai (SANS notification) + $date1 = new DateTime($date_time); + $date2 = new DateTime(); $interval = $date1->diff($date2); $daysPassed = $interval->days; - $statuDate = ''; - // die(var_dump($daysPassed)); - $Notification = new NotificationController(); - // die(var_dump($_SERVER['REQUEST_URI'] == "/orders/fetchOrdersData")); - $uri = $_SERVER['REQUEST_URI']; - if ($daysPassed < 8 && $value['paid_status'] == 2) { - $statuDate = ' depuis ' . $daysPassed . ' Jours'; - } else if ($daysPassed >= 8 && $value['paid_status'] == 2) { - $Notification->createNotification("Conseil : Confirmation de reservation", "TOUS", (int)$value['store_id'], str_contains($uri, "fetchOrdersData") ? 'orders/#' : 'orders'); - $statuDate = ' depuis ' . $daysPassed . ' Jours'; - } else if ($daysPassed >= 15 && $value['paid_status'] == 2) { - $Notification->createNotification("Conseil : Reservation expiré", "TOUS", (int)$value['store_id'], str_contains($uri, "fetchOrdersData") ? 'orders/#' : 'orders'); - $statuDate = ' depuis ' . $daysPassed . ' Jours'; + $statuDate = ''; + if ($value['paid_status'] == 2) { + if ($daysPassed < 8) { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } elseif ($daysPassed >= 8 && $daysPassed < 15) { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } else { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } } - $Orders_items= new OrderItems(); - $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']); - // Add data to the result array + // $Orders_items= new OrderItems(); + // $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']); + $result['data'][$key] = [ $value['bill_no'], $value['customer_name'], $value['customer_phone'], $date_time . "
" . $statuDate, - $sum_order_item, - number_format((int) $value['net_amount'], 0, ',', ' '), + number_format((int) $value['discount'], 0, ',', ' '), + number_format((int) $value['gross_amount'], 0, ',', ' '), $paid_status, $buttons ]; } return $this->response->setJSON($result); - } - else { - if($users['group_name'] !== "Direction" || $users['group_name'] !== "Conseil"){ + } + else { foreach ($data as $key => $value) { - - // $count_total_item = $Orders->countOrderItem($value['id']); - $date_time = date('d-m-Y h:i a', strtotime($value['date_time'])); // Combine date and time formatting + $date_time = date('d-m-Y h:i a', strtotime($value['date_time'])); - // Initialize buttons $buttons = ''; - if (in_array('viewOrder', $this->permission)) { + // Bouton imprimer (sauf pour SECURITE) + if (in_array('viewOrder', $this->permission) && $users['group_name'] != "SECURITE" && $users['group_name'] != "COMMERCIALE") { $buttons .= ''; } - + if (in_array('updateOrder', $this->permission) && $users["id"] == $value['user_id']) { - $buttons .= ' '; + + $buttons .= ' '; + } + if (in_array('viewOrder', $this->permission)) { $buttons .= ' '; } - if (in_array('deleteOrder', $this->permission) && $users["id"] == $value['user_id']) { $buttons .= ' '; } - // Paid status label - $paid_status = ($value['paid_status'] == 1) - ? 'Payé' - : 'Non payé'; + + // Statut de paiement + if ($value['paid_status'] == 1) { + $paid_status = 'Validé'; + } elseif ($value['paid_status'] == 2) { + $paid_status = 'En Attente'; + } elseif ($value['paid_status'] == 3) { + $paid_status = 'Validé et Livré'; + } else { + $paid_status = 'Refusé'; + } + // Calcul délai (SANS notification) $date1 = new DateTime($date_time); - $date2 = new DateTime(); // Current date and time - - // Calculate the difference in days + $date2 = new DateTime(); $interval = $date1->diff($date2); $daysPassed = $interval->days; - $statuDate = ''; - // die(var_dump($daysPassed)); - $Notification = new NotificationController(); - // die(var_dump($_SERVER['REQUEST_URI'] == "/orders/fetchOrdersData")); - $uri = $_SERVER['REQUEST_URI']; - if ($daysPassed < 8 && $value['paid_status'] == 2) { - $statuDate = ' depuis ' . $daysPassed . ' Jours'; - } else if ($daysPassed >= 8 && $value['paid_status'] == 2) { - $Notification->createNotification("Conseil : Confirmation de reservation", "TOUS", (int)$value['store_id'], str_contains($uri, "fetchOrdersData") ? 'orders/#' : 'orders'); - $statuDate = ' depuis ' . $daysPassed . ' Jours'; - } else if ($daysPassed >= 15 && $value['paid_status'] == 2) { - $Notification->createNotification("Conseil : Reservation expiré", "TOUS", (int)$value['store_id'], str_contains($uri, "fetchOrdersData") ? 'orders/#' : 'orders'); - $statuDate = ' depuis ' . $daysPassed . ' Jours'; + $statuDate = ''; + if ($value['paid_status'] == 2) { + if ($daysPassed < 8) { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } elseif ($daysPassed >= 8 && $daysPassed < 15) { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } else { + $statuDate = ' depuis ' . $daysPassed . ' Jours'; + } } - $Orders_items= new OrderItems(); - $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']); - // Add data to the result array + // $Orders_items= new OrderItems(); + // $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']); + $result['data'][$key] = [ $value['product_names'], $value['user_name'], $date_time . "
" . $statuDate, - $sum_order_item, - number_format((int) $value['net_amount'], 0, ',', ' '), + number_format((int) $value['discount'], 0, ',', ' '), + number_format((int) $value['gross_amount'], 0, ',', ' '), $paid_status, $buttons ]; } return $this->response->setJSON($result); } - } - // Return JSON response - } + /** + * Affiche le formulaire create avec les données d'une commande existante + * Pour le rôle COMMERCIALE + */ + /** * function who check if the product is null * and create notification about it @@ -273,7 +285,7 @@ class OrderController extends AdminController for ($i = 0; $i < count($product_id); $i++) { $singleProduct = $product->getProductData($product_id[$i]); if ($singleProduct['product_sold'] == true) { - $notification->createNotification("Produit en rupture de stock", "Conseil", $store_id, "products"); + $notification->createNotification("Produit en rupture de stock", "Direction", $store_id, "products"); } } } @@ -285,52 +297,95 @@ class OrderController extends AdminController for ($i = 0; $i < \count($amount); $i++) { $montant += $amount[$i]; } - return $montant; } + + public function create() { $this->verifyRole('createOrder'); $data['page_title'] = $this->pageTitle; - - // Load validation service + $validation = \Config\Services::validation(); - - // echo '
';
-        // die(var_dump($this->request->getPost('product[]')));
-
         $products = $this->request->getPost('product[]');
-
-        // Then, manually check for uniqueness
+    
         if ($products !== null && (count($products) !== count(array_unique($products)))) {
             return redirect()->back()->withInput()->with('errors', ['product' => 'Chaque produit sélectionné doit être unique.']);
         }
-
-        // Set validation rules
+    
         $validation->setRules([
             'product[]' => 'required'
         ]);
-        // echo '
';
-        // die(var_dump($this->request->getPost()));
-
+    
         $validationData = [
             'product[]' => $this->request->getPost('product[]')
         ];
-
+    
         $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 = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4));
-
-            // If validation passes
-
+    
+            // Récupération des produits
+            $posts = $this->request->getPost('product[]');
+            $rates = $this->request->getPost('rate_value[]');
+            $amounts = $this->request->getPost('amount_value[]');
+            $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;
+    
+                    // Récupérer données produit + prix minimal
+                    $productData = $Products->getProductData($productId);
+                    $fourchette = $FourchettePrix->getFourchettePrixByProductId($productId);
+    
+                    if ($fourchette) {
+                        $prixMinimal = (float)$fourchette['prix_minimal'];
+    
+                        // ✅ Le rabais devient le prix de vente
+                        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 inférieur au prix minimal autorisé de {$prixMinimalFormatted} Ar."
+                                ]);
+                        }
+                    }
+                }
+            }
+    
+            // ✅ NOUVELLE LOGIQUE : Calculer le montant à payer et net_amount
+            $montant_a_payer = ($discount > 0) ? $discount : $gross_amount;
+            
+            // Récupérer les tranches
+            $tranche_1 = (float)$this->request->getPost('tranche_1') ?? 0;
+            $tranche_2 = (float)$this->request->getPost('tranche_2') ?? 0;
+            
+            // Calculer net_amount selon les tranches
+            if ($tranche_1 > 0 && $tranche_2 > 0) {
+                // Paiement en 2 tranches
+                $net_amount = $tranche_1 + $tranche_2;
+            } else {
+                // Paiement en 1 tranche ou pas de tranches
+                $net_amount = $montant_a_payer;
+            }
+    
+            // ✅ Création de la commande avec la nouvelle logique
             $data = [
                 'bill_no' => $bill_no,
                 'customer_name' => $this->request->getPost('customer_name'),
@@ -338,97 +393,109 @@ class OrderController extends AdminController
                 'customer_phone' => $this->request->getPost('customer_phone'),
                 'customer_cin' => $this->request->getPost('customer_cin'),
                 'date_time' => date('Y-m-d H:i:s'),
-                'service_charge_rate' => $this->request->getPost('service_charge_rate'),
-                'vat_charge_rate' => $this->request->getPost('vat_charge_rate'),
-                'vat_charge' => ($this->request->getPost('vat_charge_value') > 0) ? $this->request->getPost('vat_charge_value') : 0,
-                'net_amount' => $this->request->getPost('net_amount'),
-                'discount' => $this->request->getPost('discount'),
-                'paid_status' => 2,
+                'service_charge_rate' => 0,
+                'vat_charge_rate' => 0,
+                'vat_charge' => 0,
+                'net_amount' => $net_amount, // ✅ Net amount = tranches OU montant_a_payer
+                'discount' => $discount,
+                'paid_status' => 2, // Toujours En Attente au départ
                 'user_id' => $user_id,
-                // 'qty' => $this->request->getPost('qty[]'),
-                'amount_value' => $this->request->getPost('amount_value[]'),
-                'gross_amount' => $this->calculGross($this->request->getPost('amount_value[]')),
-                'rate_value' => $this->request->getPost('rate_value[]'),
+                'amount_value' => $amounts,
+                'gross_amount' => $gross_amount,
+                'rate_value' => $rates,
                 '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')
             ];
-
-            $posts = $this->request->getPost('product[]');
-
-            // echo '
';
-            // die(var_dump($data));
+    
             $order_id = $Orders->create($data, $posts);
-            $Order_item1 = new OrderItems();
-            $order_item_data = $Order_item1->getOrdersItemData($order_id);
-            $product_ids = array_column($order_item_data, 'product_id');
-
-            $Notification = new NotificationController();
-
-            if ((int) (int) $this->request->getPost('discount') > 0) {
-                $productData = new Products();
-                $product_data_results = [];
-
-                foreach ($product_ids as $prod_id) {
-
-                    $id = (int) $prod_id;
-
-                    // Appel de la méthode pour chaque 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_output = implode("\n", $product_lines);
-
-
-                // data for the remise
-                $data1 = [
-                    'date_demande' => date('Y-m-d H:i:s'),
-                    'montant_demande' => $this->request->getPost('discount'),
-                    'total_price' => $this->request->getPost('amount_value[]'),
-                    '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("Un nouveau demande de remise été ajouté", "Conseil", (int)$users['store_id'], 'remise');
-            }
-
+    
             if ($order_id) {
                 session()->setFlashdata('success', 'Créé avec succès');
-                $Notification->createNotification("Un nouveau commade ajouter", "Caissière", (int)$users['store_id'], "orders");
+    
+                $Notification = new NotificationController();
+    
+                // ✅ WORKFLOW SELON LA REMISE
+                if ($discount > 0) {
+                    // AVEC REMISE : Créer demande + Notifier Conseil
+                    $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_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 au CONSEIL
+                    $Notification->createNotification(
+                        "Nouvelle demande de remise à valider - Commande " . $bill_no, 
+                        "Direction", 
+                        (int)$users['store_id'], 
+                        "remise/"
+                    );
+                    
+                } else {
+                    // SANS REMISE : Notifier directement la Caissière
+                    $Notification->createNotification(
+                        "Nouvelle commande à valider - " . $bill_no, 
+                        "Caissière", 
+                        (int)$users['store_id'], 
+                        "orders"
+                    );
+                }
+    
+                // Redirection selon le rôle
                 if ($users["group_name"] != "COMMERCIALE") {
-
                     $this->checkProductisNull($posts, $users['store_id']);
-
                     return redirect()->to('orders/update/' . $order_id);
                 } else {
-
                     return redirect()->to('orders/');
                 }
+                
             } else {
                 session()->setFlashdata('errors', 'Error occurred!!');
                 return redirect()->to('orders/create/');
             }
+            
         } else {
-            // If validation fails
+            // Affichage du formulaire
             $company = $Company->getCompanyData(1);
-
             $session = session();
             $users = $session->get('user');
             $store_id = $users['store_id'];
-            // Prepare data for the view
+            
             $data = [
+                'paid_status' => 2,
                 'company_data' => $company,
                 'is_vat_enabled' => ($company['vat_charge_value'] > 0),
                 'is_service_enabled' => ($company['service_charge_value'] > 0),
@@ -436,23 +503,111 @@ class OrderController extends AdminController
                 'validation' => $validation,
                 'page_title' => $this->pageTitle,
             ];
-
-            // Render the view with the prepared data
+    
             return $this->render_template('orders/create', $data);
         }
     }
+    
+/**
+ * Marquer une commande comme livrée
+ * Accessible uniquement par le rôle SECURITE
+ */
+public function markAsDelivered()
+{
+    // Activer le retour JSON même en cas d'erreur
+    ini_set('display_errors', 0);
+    
+    $order_id = $this->request->getPost('order_id');
+    $response = ['success' => false, 'messages' => ''];
+
+    try {
+        $session = session();
+        $users = $session->get('user');
+
+        // Vérifier que l'utilisateur est SECURITE
+        if (!isset($users['group_name']) || $users['group_name'] !== 'SECURITE') {
+            $response['messages'] = "Accès refusé : seule la sécurité peut marquer une commande comme livrée.";
+            return $this->response->setJSON($response);
+        }
+
+        if (!$order_id) {
+            $response['messages'] = "ID de commande manquant.";
+            return $this->response->setJSON($response);
+        }
+
+        $Orders = new Orders();
+        
+        // Récupérer la commande actuelle
+        $current_order = $Orders->getOrdersData((int)$order_id);
+        
+        if (!$current_order) {
+            $response['messages'] = "Commande introuvable.";
+            return $this->response->setJSON($response);
+        }
+
+        // Vérifier que la commande est validée (paid_status = 1)
+        if ($current_order['paid_status'] != 1) {
+            $response['messages'] = "Cette commande doit être validée avant d'être marquée comme livrée.";
+            return $this->response->setJSON($response);
+        }
+
+        // Mettre à jour UNIQUEMENT le statut à 3 (Livré)
+        $updated = $Orders->update((int)$order_id, ['paid_status' => 3]);
+
+        if ($updated) {
+            // Créer une notification
+            try {
+                $Notification = new NotificationController();
+                $Notification->createNotification(
+                    "Commande " . $current_order['bill_no'] . " livrée avec succès", 
+                    "Direction", 
+                    (int)$current_order['store_id'], 
+                    "orders"
+                );
+            } catch (\Exception $e) {
+                // Si la notification échoue, on continue quand même
+                log_message('error', 'Erreur notification: ' . $e->getMessage());
+            }
+
+            $response['success'] = true;
+            $response['messages'] = "La commande a été marquée comme livrée avec succès.";
+        } else {
+            $response['messages'] = "Erreur lors de la mise à jour du statut.";
+        }
+
+    } catch (\Exception $e) {
+        log_message('error', 'Erreur markAsDelivered: ' . $e->getMessage());
+        $response['messages'] = "Erreur serveur : " . $e->getMessage();
+    }
+
+    return $this->response->setJSON($response);
+}
+
 
     public function getProductValueById()
     {
         $product_id = $this->request->getPost('product_id');
         if ($product_id) {
-            $Products = new Products();
+            $Products = new \App\Models\Products();
             $product_data = $Products->getProductData($product_id);
-            return $this->response->setJSON($product_data);
+    
+            $FourchettePrix = new \App\Models\FourchettePrix();
+            $fourchette = $FourchettePrix->where('product_id', $product_id)->first();
+    
+            $response = [
+                'id'           => $product_data['id'],
+                'sku'          => $product_data['sku'],
+                'name'         => $product_data['name'],
+                'prix_vente'   => $product_data['prix_vente'],
+                'prix_minimal' => $fourchette ? $fourchette['prix_minimal'] : 0,
+            ];
+    
+            return $this->response->setJSON($response);
         }
     }
+    
 
-    public function getTableProductRow()
+      public function getTableProductRow()
     {
         $Products = new Products();
         $session = session();
@@ -464,7 +619,6 @@ class OrderController extends AdminController
         return $this->response->setJSON($product_data);
     }
 
-
     public function update(int $id)
     {
         $this->verifyRole('updateOrder');
@@ -472,7 +626,7 @@ class OrderController extends AdminController
         $data['page_title'] = $this->pageTitle;
         $validation = \Config\Services::validation();
     
-        // Définir les règles de validation
+        // Règles de validation
         $validation->setRules([
             'product' => 'required'
         ]);
@@ -486,7 +640,29 @@ class OrderController extends AdminController
         $Products = new Products();
         $OrderItems = new OrderItems();
     
+        $session = session();
+        $user = $session->get('user');
+        $role = $user['group_name'] ?? null;
+    
         if ($this->request->getMethod() === 'post' && $validation->run($validationData)) {
+    
+            $current_order = $Orders->getOrdersData($id);
+            $old_paid_status = $current_order['paid_status'];
+    
+            // ✅ Statut payé pour COMMERCIALE reste toujours "En attente"
+            if ($role === 'COMMERCIALE') {
+                $paid_status = 2; // ← mettre la valeur correspondant à "En attente" dans votre table
+            } else {
+                $paid_status = $this->request->getPost('paid_status');
+            }
+    
+            // Gestion remise
+            $discount = $this->request->getPost('discount');
+            $original_discount = $this->request->getPost('original_discount');
+            if ($discount === '' || $discount === null) {
+                $discount = $original_discount;
+            }
+    
             $dataUpdate = [
                 'customer_name' => $this->request->getPost('customer_name'),
                 'customer_address' => $this->request->getPost('customer_address'),
@@ -498,16 +674,16 @@ class OrderController extends AdminController
                 'vat_charge_rate' => $this->request->getPost('vat_charge_rate'),
                 'vat_charge' => max(0, (float)$this->request->getPost('vat_charge_value')),
                 'net_amount' => $this->request->getPost('net_amount_value'),
-                'discount' => $this->request->getPost('discount'),
-                'paid_status' => $this->request->getPost('paid_status'),
+                'discount' => (float)$discount,
+                'paid_status' => $paid_status,
                 'product' => $this->request->getPost('product'),
                 'product_sold' => true,
                 'rate_value' => $this->request->getPost('rate_value'),
                 'amount_value' => $this->request->getPost('amount_value'),
-                'tranche_1' => $this->request->getPost('tranche_1'),
-                'tranche_2' => $this->request->getPost('tranche_2'),
-                'order_payment_mode' => $this->request->getPost('order_payment_mode_1'),
-                'order_payment_mode_1' => $this->request->getPost('order_payment_mode_2')
+                'tranche_1' => $role !== 'COMMERCIALE' ? $this->request->getPost('tranche_1') : null,
+                'tranche_2' => $role !== 'COMMERCIALE' ? $this->request->getPost('tranche_2') : null,
+                'order_payment_mode' => $role !== 'COMMERCIALE' ? $this->request->getPost('order_payment_mode_1') : null,
+                'order_payment_mode_1' => $role !== 'COMMERCIALE' ? $this->request->getPost('order_payment_mode_2') : null
             ];
     
             if ($Orders->updates($id, $dataUpdate)) {
@@ -516,9 +692,21 @@ class OrderController extends AdminController
     
                 $Notification = new NotificationController();
     
-                $discount = (int) $this->request->getPost('discount');
+                // Notification pour la sécurité si commande validée
+                if ($old_paid_status == 2 && $paid_status == 1) {
+                    $customer_name = $this->request->getPost('customer_name');
+                    $bill_no = $current_order['bill_no'];
     
-                if ($discount > 0) {
+                    $Notification->createNotification(
+                        "Commande validée: {$bill_no} - Client: {$customer_name}",
+                        "SECURITE",
+                        (int)$user['store_id'],
+                        'orders'
+                    );
+                }
+    
+                // Gestion demande de remise
+                if ((float)$discount > 0) {
                     $productData = new Products();
                     $product_data_results = [];
     
@@ -535,22 +723,25 @@ class OrderController extends AdminController
     
                     $product_output = implode("\n", $product_lines);
     
-                    $session = session();
-                    $users = $session->get('user');
-                    
                     $data1 = [
                         'date_demande' => date('Y-m-d H:i:s'),
                         'montant_demande' => $this->request->getPost('discount'),
                         'total_price' => $this->request->getPost('amount_value'),
-                        'id_store' => $users['store_id'],
+                        'id_store' => $user['store_id'],
                         'id_order' => $id,
                         'product' => $product_output,
                         'demande_status' => 'En attente'
                     ];
     
                     $Remise = new Remise();
-                    $Remise->updateRemise1($id,$data1);
-                    $Notification->createNotification("Un nouveau demande de remise a été ajouté", "Conseil", (int)$users['store_id'] ?? null, 'remise');
+                    $Remise->updateRemise1($id, $data1);
+    
+                    $Notification->createNotification(
+                        "Une nouvelle demande de remise a été ajoutée",
+                        "Direction",
+                        (int)$user['store_id'] ?? null,
+                        "remise/"
+                    );
                 }
     
                 session()->setFlashData('success', 'Commande mise à jour avec succès.');
@@ -561,13 +752,19 @@ class OrderController extends AdminController
             }
         }
     
-        // En cas d’échec de la validation ou si GET
+        // Si GET ou validation échoue
         $company = $Company->getCompanyData(1);
         $data['company_data'] = $company;
         $data['is_vat_enabled'] = ($company['vat_charge_value'] > 0);
         $data['is_service_enabled'] = ($company['service_charge_value'] > 0);
     
         $orders_data = $Orders->getOrdersData($id);
+    
+        // Montant tranches
+        $orders_data['montant_tranches'] = (!empty($orders_data['discount']) && $orders_data['discount'] > 0)
+            ? $orders_data['discount']
+            : $orders_data['gross_amount'];
+    
         $result = ['order' => $orders_data];
         $orders_item = $OrderItems->getOrdersItemData($orders_data['id']);
     
@@ -594,6 +791,7 @@ class OrderController extends AdminController
         $Company = new Company();
         $Products = new Products();
         $OrderItems = new OrderItems();
+        $Brands = new Brands(); 
     
         // En cas d’échec de la validation ou si GET
         $company = $Company->getCompanyData(1);
@@ -602,10 +800,10 @@ class OrderController extends AdminController
         $data['is_service_enabled'] = ($company['service_charge_value'] > 0);
     
         $orders_data = $Orders->getOrdersData($id);
-        $sum_order_item = $OrderItems->getSumOrdersItemData($orders_data['id']);
+        // $sum_order_item = $OrderItems->getSumOrdersItemData($orders_data['id']);
         $result = [
             'order' => $orders_data,
-            'sum_order_data' => $sum_order_item
+            // 'sum_order_data' => $sum_order_item
         ];
 
         $orders_item = $OrderItems->getOrdersItemData($orders_data['id']);
@@ -616,6 +814,7 @@ class OrderController extends AdminController
     
         $data['order_data'] = $result;
         $data['products'] = $Products->getActiveProductData();
+        $data['brands'] = $Brands->findAll();
     
         return $this->response->setJSON($data);
 
@@ -659,7 +858,13 @@ class OrderController extends AdminController
 
             $html = '';
             // Vérifier si l'utilisateur a payé
-            $paid_status = ($order_data['paid_status'] == 1) ? "Payé" : "Non payé";
+            if ($order_data['paid_status'] == 1) {
+                $paid_status = "Validé";
+            } elseif ($order_data['paid_status'] == 2) {
+                $paid_status = "En Attente";
+            } else {
+                $paid_status = "Refusé";
+            }
 
             // Génération du HTML
             $html .= '
@@ -1093,7 +1298,7 @@ class OrderController extends AdminController
             'is_vat_enabled' => ($company['vat_charge_value'] > 0),
             'is_service_enabled' => ($company['service_charge_value'] > 0),
             'products' => $Products->getProductData($id),
-            'totalqtt' => $Products->getProductData($id)['qty'],
+            // 'discount' => $Products->getProductData($id)['discount'],
             'pu' => $Products->getProductData($id)['prix_vente'],
             'page_title' => $this->pageTitle,
         ];
@@ -1328,6 +1533,8 @@ public function print3(int $id)
     echo'';
 }
 
+// PRINT5 - Facture détaillée avec conditions générales
+// ====================================
 public function print5(int $id)
 {
     $this->verifyRole('viewOrder');
@@ -1341,18 +1548,24 @@ public function print5(int $id)
     $Company    = new Company();
     $Products   = new Products();
     $OrderItems = new OrderItems();
+    $Brand      = new Brands();
+    $Category   = new Category();
 
     // Récupération des données
-    $order       = $Orders->getOrdersData($id);
-    $items       = $OrderItems->getOrdersItemData($id);
-    $company     = $Company->getCompanyData(1);
-    $today       = date('d/m/Y');
+    $order   = $Orders->getOrdersData($id);
+    $items   = $OrderItems->getOrdersItemData($id);
+    $company = $Company->getCompanyData(1);
+    $today   = date('d/m/Y');
 
-    // Pré-calculs
-    $totalTTC  = (float) $order['gross_amount'];
-    $totalHT   = $totalTTC / 1.20;
-    $tva       = $totalTTC - $totalHT;
-    $inWords   = $this->numberToWords((int) round($totalTTC));
+    // ✅ LOGIQUE DE REMISE: Si discount existe, il devient le prix de vente
+    $discount = (float) $order['discount'];
+    $grossAmount = (float) $order['gross_amount'];
+    
+    // Si remise existe, elle devient le montant TTC, sinon on prend gross_amount
+    $totalTTC = ($discount > 0) ? $discount : $grossAmount;
+    $totalHT  = $totalTTC / 1.20;
+    $tva      = $totalTTC - $totalHT;
+    $inWords  = $this->numberToWords((int) round($totalTTC));
 
     // Statut paiement
     $paidLabel = $order['paid_status'] == 1 ? 'Payé' : 'Non payé';
@@ -1404,9 +1617,8 @@ public function print5(int $id)
 
-            
+            
-            
@@ -1414,21 +1626,33 @@ public function print5(int $id)
         ';
+
 foreach ($items as $it) {
     $p = $Products->getProductData($it['product_id']);
-    $Brand = new Brands();
-    $Category = new Category();
+    
+    // ✅ Récupérer le nom de la marque correctement
+    $brandName = 'Non définie';
+    if (!empty($p['marque'])) {
+        $brandData = $Brand->find($p['marque']);
+        if ($brandData && isset($brandData['name'])) {
+            $brandName = $brandData['name'];
+        }
+    }
+    
+    // ✅ LOGIQUE: Si discount existe pour la commande, on l'affiche comme prix
+    $prixAffiche = ($discount > 0) ? $discount : $p['prix_vente'];
+    
     $html .= '
         
-            
-            
+            
-            
+            ';
 }
+
 $html .= '
     
NomDésignation MARQUETYPE N° Moteur N° Châssis Puissance (CC)
'.esc($p['name']).''.esc($Brand->getNameById($p['id'])).''.esc($Category->getNameById($p['categorie_id'])).''.esc($brandName).' '.esc($p['numero_de_moteur']).' '.esc($p['chasis'] ?? '').' '.esc($p['puissance']).''.number_format($p['prix_vente'], 0, '', ' ').''.number_format($prixAffiche, 0, '', ' ').'
@@ -1459,20 +1683,20 @@ $html .= ' -
-
-

Conditions Générales

- Logo -
-
    -
  • Aucun accessoire (casque, rétroviseur, batterie, etc.) n’est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.
  • -
  • Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.
  • -
  • Aucun service après-vente n’est fourni.
  • -
  • La moto est vendue sans garantie, car il s’agit d’un modèle d’occasion.
  • -
  • La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l’achat. Il appartient à ce dernier de procéder à l’immatriculation dans le délai prévu par la loi.
  • -
-
L’Acheteur
+
+
+

Conditions Générales

+ Logo
+
    +
  • Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.
  • +
  • Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.
  • +
  • Aucun service après-vente n\'est fourni.
  • +
  • La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.
  • +
  • La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation dans le délai prévu par la loi.
  • +
+
L\'Acheteur
+
'; @@ -1480,6 +1704,7 @@ $html .= ' return $this->response->setBody($html); } + /** * Convertit un nombre entier en texte (français, sans décimales). * Usage basique, pour Ariary. @@ -1548,7 +1773,10 @@ private function numberToWords(int $num): string return trim($words) . ' ariary'; } - public function print7(int $id) +// ==================================== +// PRINT7 - Bon de commande +// ==================================== +public function print7(int $id) { $this->verifyRole('viewOrder'); @@ -1641,10 +1869,29 @@ private function numberToWords(int $num): string foreach ($items as $item) { $p = $Products->getProductData($item['product_id']); + + // ✅ Récupérer le nom de la marque correctement + $brandName = 'Non définie'; + if (!empty($p['marque'])) { + $brandData = $Brand->find($p['marque']); + if ($brandData && isset($brandData['name'])) { + $brandName = $brandData['name']; + } + } + + // ✅ Récupérer le nom de la catégorie + $categoryName = 'Non définie'; + if (!empty($p['categorie_id'])) { + $categoryData = $Category->find($p['categorie_id']); + if ($categoryData && isset($categoryData['name'])) { + $categoryName = $categoryData['name']; + } + } + $html .= ' '.esc($p['name']).' - '.esc($Brand->getNameById($p['brand_id'] ?? $p['id'])).' - '.esc($Category->getNameById($p['categorie_id'])).' + '.esc($brandName).' + '.esc($categoryName).' '.esc($p['numero_de_moteur']).' '.esc($p['chasis'] ?? '').' '.esc($p['puissance']).' @@ -1659,19 +1906,19 @@ private function numberToWords(int $num): string - + - + - + - + '; if (! empty($order['order_payment_mode'])) { @@ -1684,7 +1931,6 @@ private function numberToWords(int $num): string $html .= '
Total HT : Ar'.number_format($totalHT, 0, '', ' ').' Ar
TVA (20%) : Ar'.number_format($tva, 0, '', ' ').' Ar
Total TTC : Ar'.number_format($totalTTC, 0, '', ' ').' Ar
Statut :'.$paidLabel.'
-
L\'acheteur

__________________
Le vendeur

__________________
@@ -1701,33 +1947,32 @@ private function numberToWords(int $num): string Logo
    -
  • Aucun accessoire (casque, rétroviseur, batterie, etc.) n’est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.
  • +
  • Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.
  • Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.
  • -
  • Aucun service après-vente n’est fourni.
  • -
  • La moto est vendue sans garantie, car il s’agit d’un modèle d’occasion.
  • -
  • La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l’achat. Il appartient à ce dernier de procéder à l’immatriculation dans le délai prévu par la loi.
  • +
  • Aucun service après-vente n\'est fourni.
  • +
  • La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.
  • +
  • La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation dans le délai prévu par la loi.
-
L’Acheteur
+
L\'Acheteur
'; - // Affichage final echo $html; } - +// ==================================== +// PRINT31 - Facture + Bon de commande (pages séparées) +// ==================================== public function print31(int $id) { - // Vérification du rôle $this->verifyRole('viewOrder'); if (! $id) { return $this->response->setStatusCode(400, 'ID manquant'); } - // Instanciation des modèles $Orders = new Orders(); $Company = new Company(); $OrderItems = new OrderItems(); @@ -1735,19 +1980,13 @@ public function print31(int $id) $Brand = new Brands(); $Category = new Category(); - // Récupération des données $order_data = $Orders->getOrdersData($id); $items = $OrderItems->getOrdersItemData($id); $company_info = $Company->getCompanyData(1); - // Statut de paiement $paid_status = $order_data['paid_status'] === 1 - ? "Payé" - : "Non payé"; - - // STYLE COMMUN avec page-break-after - - + ? "Validé" + : "Refusé"; // --- FACTURES : Une par produit --- foreach ($items as $item) { @@ -1758,9 +1997,26 @@ public function print31(int $id) $vatAmount = $subtotal * 0.2; $discount = (float) $order_data['discount']; $totalNet = $subtotal + $vatAmount - $discount; - $inWords = $this->numberToWords((int) round($subtotal)); + $inWords = $this->numberToWords((int) round($subtotal)); + + // ✅ Récupérer le nom de la marque + $brandName = 'Non définie'; + if (!empty($p['marque'])) { + $brandData = $Brand->find($p['marque']); + if ($brandData && isset($brandData['name'])) { + $brandName = $brandData['name']; + } + } + + // ✅ Récupérer le nom de la catégorie + $categoryName = 'Non définie'; + if (!empty($p['categorie_id'])) { + $categoryData = $Category->find($p['categorie_id']); + if ($categoryData && isset($categoryData['name'])) { + $categoryName = $categoryData['name']; + } + } - // Début du document echo ''; echo ''; echo ''; @@ -1784,11 +2040,7 @@ public function print31(int $id) } "; echo ''; - - // Wrapper pour nouvelle page echo '
'; - - // En-tête echo '
'; echo '
'; echo '

' . esc($company_info['company_name']) . '

'; @@ -1796,15 +2048,14 @@ public function print31(int $id) echo '

STAT : ' . esc($company_info['STAT']) . '

'; echo '

Contact : ' . esc($company_info['phone']) . ' | ' . esc($company_info['phone2']) . '

'; echo '

Magasin : ' . esc($this->returnStore($order_data['store_id'])) . '

'; - echo '
'; // infos + echo '
'; echo '
'; echo 'Logo'; echo '

Facture N° ' . esc($order_data['bill_no']) . '

'; echo '

Antananarivo, le ' . date('d/m/Y') . '

'; - echo '
'; // logo - echo '
'; // header + echo ''; + echo ''; - // Client echo '
'; echo '

Client : ' . esc($order_data['customer_name']) . '

'; echo '

Adresse : ' . esc($order_data['customer_address']) . '

'; @@ -1812,104 +2063,13 @@ public function print31(int $id) echo '

CIN : ' . esc($order_data['customer_cin']) . '

'; echo '
'; - // Tableau produits echo ''; echo ''; echo ''; echo ''; echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo '
NomMarqueCatégorieN° MoteurChâssisPuissance (CC)Prix Unit. (Ar)
' . esc($p['name']) . '' . esc($Brand->getNameById($p['id'])) . '' . esc($Category->getNameById($p['categorie_id'])) . '' . esc($p['numero_de_moteur']) . '' . esc($p['chasis'] ?? '') . '' . esc($p['puissance']) . '' . number_format($p['prix_vente'], 0, '', ' ') . '
'; - - // Totaux - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo '
Total HT :' . number_format($subtotal, 0, '.', ' ') . ' Ar
TVA (20%) :' . number_format($vatAmount, 0, '.', ' ') . ' Ar
Réduction :' . number_format($discount, 0, '.', ' ') . ' Ar
Total Net :' . number_format($totalNet, 0, '.', ' ') . ' Ar
Statut :' . $paid_status . '
'; - echo '
- Arrêté à la somme de :
- '.$inWords.' -
'; - - - // Signature - echo '
'; - echo '
L'Acheteur

__________________
'; - echo '
Le Vendeur

__________________
'; - echo '
'; - - echo ''; // page-break - echo ''; - } - - // --- BON DE COMMANDE : une seule fois après la boucle --- - echo ''; - echo ''; - echo ''; - echo ''; - echo ""; - echo ''; - - // Wrapper bon de commande - echo '
'; - echo '
'; - echo '
'; - echo '

' . esc($company_info['company_name']) . '

'; - echo '

Commande ID : ' . esc($order_data['order_no'] ?? $order_data['bill_no']) . '

'; - echo '

Magasin : ' . esc($this->returnStore($order_data['store_id'])) . '

'; - echo '
'; - echo '
'; - echo 'Logo'; - echo '

Bon de commande N° ' . esc($order_data['order_no'] ?? $order_data['bill_no']) . '

'; - echo '

Antananarivo, le ' . date('d/m/Y') . '

'; - echo '
'; - echo '
'; // header - - // Corps bon de commande - echo '
'; - echo '

Client : ' . esc($order_data['customer_name']) . '

'; - echo '

Adresse : ' . esc($order_data['customer_address']) . '

'; - echo '

Téléphone : ' . esc($order_data['customer_phone']) . '

'; - echo '

CIN : ' . esc($order_data['customer_cin']) . '

'; - echo '
'; - - echo ''; - echo ''; - echo ''; - $total_ht = 0; - foreach ($items as $item) { - $p = $Products->getProductData($item['product_id']); - $amount = (float) $item['amount']; - $total_ht += $amount; - echo ''; - echo ''; - echo ''; - echo ''; + echo ''; + echo ''; echo ''; echo ''; echo ''; @@ -1921,10 +2081,10 @@ public function print31(int $id) echo '
NomMarqueCatégorieN° MoteurChâssisPuissance (CC)Prix Unit. (Ar)
' . esc($p['name']) . '' . esc($Brand->getNameById($p['id'])) . '' . esc($Category->getNameById($p['categorie_id'])) . '' . esc($brandName) . '' . esc($categoryName) . '' . esc($p['numero_de_moteur']) . '' . esc($p['chasis'] ?? '') . '' . esc($p['puissance']) . '
'; echo ''; - echo ''; - echo ''; - echo ''; - echo ''; + echo ''; + echo ''; + echo ''; + echo ''; echo '
Total HT :Ar
TVA : Ar
Réduction :Ar
Total TTC : Ar
Total HT :' . number_format($total_ht, 0, '', ' ') . ' Ar
TVA :' . number_format($tva, 0, '', ' ') . ' Ar
Réduction :' . number_format($order_data['discount'], 0, '', ' ') . ' Ar
Total TTC :' . number_format($total_ttc, 0, '', ' ') . ' Ar
'; echo '
'; @@ -1932,25 +2092,24 @@ public function print31(int $id) echo '
Le Vendeur

__________________
'; echo '
'; - - // --- 3) CONDITIONS GÉNÉRALES (avec rupture AVANT) --- + // --- CONDITIONS GÉNÉRALES --- echo '
'; - echo '
'; - echo '

Conditions Générales

'; - echo 'Moto'; - echo '
'; - echo '

* Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. ' - . 'Si le client en a besoin, il doit les acheter séparément.

'; - echo '

* Le client doit s\'assurer de vérifier soigneusement la marchandise avant de quitter notre établissement.

'; - echo '

* Aucun service après-vente n\'est fourni.

'; - echo '

* La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.

'; - echo '

* La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) ' - . 'délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation ' - . 'dans le délai prévu par la loi.

'; - echo '
L\'Acheteur
'; + echo '
'; + echo '

Conditions Générales

'; + echo 'Logo'; + echo '
'; + echo '
    '; + echo '
  • Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.
  • '; + echo '
  • Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.
  • '; + echo '
  • Aucun service après-vente n\'est fourni.
  • '; + echo '
  • La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.
  • '; + echo '
  • La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation dans le délai prévu par la loi.
  • '; + echo '
'; + echo '
L\'Acheteur
'; echo '
'; - echo ''; + echo '
'; + echo ''; } } diff --git a/app/Controllers/ProductCOntroller.php b/app/Controllers/ProductCOntroller.php index be9c2595..bf3d56db 100644 --- a/app/Controllers/ProductCOntroller.php +++ b/app/Controllers/ProductCOntroller.php @@ -159,27 +159,36 @@ class ProductCOntroller extends AdminController $Category = new Category(); $Stores = new Stores(); $Notification = new NotificationController(); + $Fourchette = new \App\Models\FourchettePrix(); $this->verifyRole('createProduct'); $data['page_title'] = $this->pageTitle; - + $validation = \Config\Services::validation(); $validation->setRules([ - 'nom_de_produit' => 'required', - 'marque' => 'required', - 'type' => 'required', - 'numero_de_moteur' => 'required', - 'prix' => 'required|numeric', - 'price_vente' => 'required|numeric', - 'puissance' => 'required', - 'store' => 'required', - 'availability' => 'required', - 'price_min' => 'required|numeric', + 'nom_de_produit' => 'required', + 'marque' => 'required', + 'type' => 'required', + 'numero_de_moteur' => 'required', + 'prix' => 'required|numeric', + 'price_vente' => 'required|numeric', + 'puissance' => 'required', + 'store' => 'required', + 'availability' => 'required', + 'price_min' => 'required|numeric', ]); if ($this->request->getMethod() === 'post' && $validation->withRequest($this->request)->run()) { - + + $prix_min = (float)$this->request->getPost('price_min'); + $prix_vente = (float)$this->request->getPost('price_vente'); + + // --- Vérification côté serveur --- + if ($prix_min > $prix_vente) { + session()->setFlashdata('errors', 'Le prix de vente ne peut pas être inférieur au prix minimal.'); + return redirect()->to('/products/create')->withInput(); + } + $upload_image = ''; - $file = $this->request->getFile('product_image'); if ($file && $file->isValid() && !$file->hasMoved()) { $uploadResult = $this->uploadImage(); @@ -188,65 +197,69 @@ class ProductCOntroller extends AdminController } } - $product_sold = 0; $availabilityValue = (int)$this->request->getPost('availability'); $availability = ($availabilityValue === 1) ? 1 : 0; $data = [ - 'name' => $this->request->getPost('nom_de_produit'), - 'sku' => $this->request->getPost('numero_de_serie'), - 'price' => $this->request->getPost('prix'), - 'qty' => 1, - 'image' => $upload_image, - 'description' => $this->request->getPost('description'), + 'name' => $this->request->getPost('nom_de_produit'), + 'sku' => $this->request->getPost('numero_de_serie'), + 'price' => $this->request->getPost('prix'), + 'qty' => 1, + 'image' => $upload_image, + 'description' => $this->request->getPost('description'), 'numero_de_moteur' => $this->request->getPost('numero_de_moteur'), - 'marque' => $this->request->getPost('marque'), - 'chasis' => $this->request->getPost('chasis'), - 'store_id' => (int)$this->request->getPost('store'), - 'availability' => $availability, - 'prix_vente' => $this->request->getPost('price_vente'), - 'date_arivage' => $this->request->getPost('datea'), - 'puissance' => $this->request->getPost('puissance'), - 'cler' => $this->request->getPost('cler'), - 'categorie_id' => json_encode($this->request->getPost('categorie[]')), - 'etats' => $this->request->getPost('etats'), - 'infoManquekit' => $this->request->getPost('infoManquekit'), - 'info' => $this->request->getPost('info'), - 'infoManque' => $this->request->getPost('infoManque'), - 'product_sold' => $product_sold, - 'type'=> $this->request->getPost('type') + 'marque' => $this->request->getPost('marque'), + 'chasis' => $this->request->getPost('chasis'), + 'store_id' => (int)$this->request->getPost('store'), + 'availability' => $availability, + 'prix_vente' => $prix_vente, + 'date_arivage' => $this->request->getPost('datea'), + 'puissance' => $this->request->getPost('puissance'), + 'cler' => $this->request->getPost('cler'), + 'categorie_id' => json_encode($this->request->getPost('categorie[]')), + 'etats' => $this->request->getPost('etats'), + 'infoManquekit' => $this->request->getPost('infoManquekit'), + 'info' => $this->request->getPost('info'), + 'infoManque' => $this->request->getPost('infoManque'), + 'product_sold' => 0, + 'type' => $this->request->getPost('type') ]; - + $store_id1 = (int)$this->request->getPost('store'); - + $productId = $Products->insert($data); if ($productId) { - $data_fourchette = [ - 'product_id' => $productId, - 'prix_minimal' => $this->request->getPost('price_min'), - ]; - $Fourchette = new FourchettePrix(); + $Fourchette->createFourchettePrix([ + 'product_id' => $productId, + 'prix_minimal' => $prix_min, + ]); - $Fourchette->createFourchettePrix($data_fourchette); session()->setFlashdata('success', 'Créé avec succès'); - $Notification->createNotification("Un nouveau Produit a été crée", "COMMERCIALE",$store_id1,'product/'); + $Notification->createNotification( + "Un nouveau Produit a été créé", + "COMMERCIALE", + $store_id1, + 'product/' + ); + return redirect()->to('/products'); } else { - session()->setFlashdata('errors', 'Error occurred while creating the product'); - return redirect()->to('products/create'); + session()->setFlashdata('errors', 'Erreur lors de la création du produit'); + return redirect()->to('products/create')->withInput(); } } else { $data = [ 'stores' => $Stores->getActiveStore(), 'validation' => $validation, 'page_title' => $this->pageTitle, - 'marque' => $Brands->getActiveBrands(), - 'categorie' => $Category->getActiveCategory(), + 'marque' => $Brands->getActiveBrands(), + 'categorie' => $Category->getActiveCategory(), ]; return $this->render_template('products/create', $data); } } + private function uploadImage() { @@ -282,71 +295,105 @@ class ProductCOntroller extends AdminController public function update(int $id) { $Products = new Products(); - $Stores = new Stores(); + $Stores = new Stores(); $Category = new Category(); + $Brands = new Brands(); + $FourchettePrix = new \App\Models\FourchettePrix(); // Modèle FourchettePrix + $this->verifyRole('updateProduct'); - $data['page_title'] = $this->pageTitle; - $Brands = new Brands(); - + $validation = \Config\Services::validation(); $validation->setRules([ 'nom_de_produit' => 'required', - 'marque' => 'required', + 'marque' => 'required', ]); - + + // Récupérer produit et prix minimal + $product_data = $Products->getProductData($id); + $prix_minimal_data = $FourchettePrix->where('product_id', $id)->first(); + $prix_minimal = $prix_minimal_data['prix_minimal'] ?? ''; + if ($this->request->getMethod() === 'post' && $validation->withRequest($this->request)->run()) { + $availabilityValue = (int)$this->request->getPost('availability'); $availability = ($availabilityValue === 1) ? 1 : 0; - + $data = [ - 'name' => $this->request->getPost('nom_de_produit'), - 'sku' => $this->request->getPost('numero_de_serie'), - 'price' => $this->request->getPost('price'), - 'qty' => 1, + 'name' => $this->request->getPost('nom_de_produit'), + 'sku' => $this->request->getPost('numero_de_serie'), + 'price' => $this->request->getPost('price'), + 'qty' => 1, 'description' => $this->request->getPost('description'), 'numero_de_moteur' => $this->request->getPost('numero_de_moteur'), - 'marque' => $this->request->getPost('marque'), - 'chasis' => $this->request->getPost('chasis'), - 'store_id' => (int)$this->request->getPost('store'), - 'availability' => $availability, - 'prix_vente' => $this->request->getPost('price_vente'), - 'date_arivage' => $this->request->getPost('datea'), - 'puissance' => $this->request->getPost('puissance'), - 'cler' => $this->request->getPost('cler'), - 'categorie_id' => json_encode($this->request->getPost('categorie[]')), - 'etats' => $this->request->getPost('etats'), - 'infoManquekit' => $this->request->getPost('infoManquekit'), - 'info' => $this->request->getPost('info'), - 'infoManque' => $this->request->getPost('infoManque'), - 'type'=> $this->request->getPost('type'), + 'marque' => $this->request->getPost('marque'), + 'chasis' => $this->request->getPost('chasis'), + 'store_id' => (int)$this->request->getPost('store'), + 'availability'=> $availability, + 'prix_vente' => $this->request->getPost('price_vente'), + 'date_arivage'=> $this->request->getPost('datea'), + 'puissance' => $this->request->getPost('puissance'), + 'cler' => $this->request->getPost('cler'), + 'categorie_id'=> json_encode($this->request->getPost('categorie[]')), + 'etats' => $this->request->getPost('etats'), + 'infoManquekit'=> $this->request->getPost('infoManquekit'), + 'info' => $this->request->getPost('info'), + 'infoManque' => $this->request->getPost('infoManque'), + 'type' => $this->request->getPost('type'), ]; - + + // ---- GESTION PRIX MINIMAL ---- + $prix_minimal_saisi = (float)$this->request->getPost('price_min'); + $prix_vente = (float)$this->request->getPost('price_vente'); + + // Vérification serveur : prix minimal <= prix de vente + if ($prix_minimal_saisi > 0 && $prix_vente < $prix_minimal_saisi) { + session()->setFlashdata('errors', 'Le prix de vente ne peut pas être inférieur au prix minimal.'); + return redirect()->to('/products/update/' . $id)->withInput(); + } + + // Sauvegarde ou mise à jour du prix minimal + if ($prix_minimal_data) { + $FourchettePrix->update($prix_minimal_data['id'], [ + 'prix_minimal' => $prix_minimal_saisi + ]); + } else { + $FourchettePrix->insert([ + 'product_id' => $id, + 'prix_minimal' => $prix_minimal_saisi + ]); + } + + // Upload image si fournie if ($this->request->getFile('product_image')->isValid()) { $uploadImage = $this->uploadImage(); - $uploadData = ['image' => $uploadImage]; - $Products->update($id, $uploadData); + $Products->update($id, ['image' => $uploadImage]); } - + + // Mise à jour du produit if ($Products->updateProduct($data, $id)) { - session()->setFlashdata('success', 'Successfully updated'); + session()->setFlashdata('success', 'Produit mis à jour avec succès.'); return redirect()->to('/products'); } else { - session()->setFlashdata('errors', 'Error occurred!!'); + session()->setFlashdata('errors', 'Une erreur est survenue lors de la mise à jour.'); return redirect()->to('/products/update/' . $id); } + } else { + // Affichage du formulaire $data = [ - 'stores' => $Stores->getActiveStore(), - 'validation' => $validation, - 'page_title' => $this->pageTitle, - 'product_data' => $Products->getProductData($id), - 'categorie' => $Category->getActiveCategory(), - 'marque' => $Brands->getActiveBrands() + 'stores' => $Stores->getActiveStore(), + 'validation' => $validation, + 'page_title' => $this->pageTitle, + 'product_data' => $product_data, + 'categorie' => $Category->getActiveCategory(), + 'marque' => $Brands->getActiveBrands(), + 'prix_minimal' => $prix_minimal ]; - + return $this->render_template('products/editbackup', $data); } } + public function remove() { @@ -418,6 +465,7 @@ class ProductCOntroller extends AdminController 'prix d\'achat' => 'price', 'prix ar' => 'prix_vente', 'catégories' => 'categorie_id', + 'prix minimale' => 'prix_minimal', 'magasin' => 'store_id', 'disponibilité' => 'availability', 'état' => 'etats', @@ -565,4 +613,5 @@ class ProductCOntroller extends AdminController ]); } } + } \ No newline at end of file diff --git a/app/Controllers/RecouvrementController.php b/app/Controllers/RecouvrementController.php index 3aee6aa3..2baf6a17 100644 --- a/app/Controllers/RecouvrementController.php +++ b/app/Controllers/RecouvrementController.php @@ -49,46 +49,67 @@ class RecouvrementController extends AdminController $avance = new Avance(); // Récupère les données brutes - $paymentDataOrders = $orders->getPaymentModes(); - $paymentDataAvance = $avance->getPaymentModesAvance(); - $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse()->mr ?? 0; - $totalRecouvrement = $recouvrement->getTotalRecouvrements(); + $paymentDataOrders = $orders->getPaymentModes(); + $paymentDataAvance = $avance->getPaymentModesAvance(); + $totalRecouvrement = $recouvrement->getTotalRecouvrements(); + $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse(); - // Totaux recouvrement - $me = $totalRecouvrement->me ?? 0; - $bm = $totalRecouvrement->bm ?? 0; - $be = $totalRecouvrement->be ?? 0; - $mb = $totalRecouvrement->mb ?? 0; + // === EXTRACTION DES SORTIES PAR MODE DE PAIEMENT === + $total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0; + $total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0; + $total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0; - // Totaux paiements Orders - $total_orders = $paymentDataOrders->total ?? 0; - $mv1_orders = $paymentDataOrders->total_mvola1 ?? 0; - $mv2_orders = $paymentDataOrders->total_mvola2 ?? 0; - $es1_orders = $paymentDataOrders->total_espece1 ?? 0; - $es2_orders = $paymentDataOrders->total_espece2 ?? 0; - $vb1_orders = $paymentDataOrders->total_virement_bancaire1 ?? 0; - $vb2_orders = $paymentDataOrders->total_virement_bancaire2 ?? 0; + // === TOTAUX RECOUVREMENT === + $me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0; + $bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0; + $be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0; + $mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0; - // Totaux paiements Avances - $total_avances = $paymentDataAvance->total ?? 0; - $mv_avances = $paymentDataAvance->total_mvola ?? 0; - $es_avances = $paymentDataAvance->total_espece ?? 0; - $vb_avances = $paymentDataAvance->total_virement_bancaire ?? 0; - $ta = $avance->getTotalAvance()->ta ?? 0; + // === TOTAUX PAIEMENTS ORDERS === + $total_orders = isset($paymentDataOrders->total) ? (float) $paymentDataOrders->total : 0; + $mv1_orders = isset($paymentDataOrders->total_mvola1) ? (float) $paymentDataOrders->total_mvola1 : 0; + $mv2_orders = isset($paymentDataOrders->total_mvola2) ? (float) $paymentDataOrders->total_mvola2 : 0; + $es1_orders = isset($paymentDataOrders->total_espece1) ? (float) $paymentDataOrders->total_espece1 : 0; + $es2_orders = isset($paymentDataOrders->total_espece2) ? (float) $paymentDataOrders->total_espece2 : 0; + $vb1_orders = isset($paymentDataOrders->total_virement_bancaire1) ? (float) $paymentDataOrders->total_virement_bancaire1 : 0; + $vb2_orders = isset($paymentDataOrders->total_virement_bancaire2) ? (float) $paymentDataOrders->total_virement_bancaire2 : 0; - // Combinaison Orders + Avances - $total_mvola = $mv1_orders + $mv2_orders + $mv_avances; - $total_espece = $es1_orders + $es2_orders + $es_avances; - $total_vb = $vb1_orders + $vb2_orders + $vb_avances; - $total = $total_orders + $total_avances; + // === TOTAUX PAIEMENTS AVANCES === + $total_avances = isset($paymentDataAvance->total) ? (float) $paymentDataAvance->total : 0; + $mv_avances = isset($paymentDataAvance->total_mvola) ? (float) $paymentDataAvance->total_mvola : 0; + $es_avances = isset($paymentDataAvance->total_espece) ? (float) $paymentDataAvance->total_espece : 0; + $vb_avances = isset($paymentDataAvance->total_virement_bancaire) ? (float) $paymentDataAvance->total_virement_bancaire : 0; - // Ajustements avec recouvrements et sorties caisse - $total_mvola_final = $total_mvola - $me - $mb + $bm; - $total_espece_final = $total_espece + $me + $be - $total_sortie_caisse; - $total_virement_bancaire_final = $total_vb - $be - $bm + $mb; + // === COMBINAISON ORDERS + AVANCES === + $total_mvola = $mv1_orders + $mv2_orders + $mv_avances; + $total_espece = $es1_orders + $es2_orders + $es_avances; + $total_vb = $vb1_orders + $vb2_orders + $vb_avances; + $total = $total_orders + $total_avances; + + // === AJUSTEMENTS AVEC RECOUVREMENTS ET SORTIES (PAR MODE DE PAIEMENT) === + $total_mvola_final = $total_mvola - + $me - + $mb + + $bm - + $total_sortie_mvola; + + $total_espece_final = $total_espece + + $me + + $be - + $total_sortie_espece; + + $total_virement_bancaire_final = $total_vb - + $be - + $bm + + $mb - + $total_sortie_virement; + + // === CALCUL DU TOTAL GÉNÉRAL === + $total_sortie_global = $total_sortie_espece + $total_sortie_mvola + $total_sortie_virement; + $total_final = $total - $total_sortie_global; return [ - 'total' => $total, + 'total' => $total_final, 'total_mvola' => $total_mvola_final, 'total_espece' => $total_espece_final, 'total_virement_bancaire' => $total_virement_bancaire_final, @@ -96,7 +117,6 @@ class RecouvrementController extends AdminController 'permission' => $this->permission ]; } - public function fetchRecouvrementData() { helper(['url', 'form']); diff --git a/app/Controllers/RemiseController.php b/app/Controllers/RemiseController.php index dc93a626..2c799caf 100644 --- a/app/Controllers/RemiseController.php +++ b/app/Controllers/RemiseController.php @@ -36,55 +36,54 @@ class RemiseController extends AdminController public function fetchRemiseData() -{ - helper(['url', 'form']); - $Remise = new Remise(); // Assure-toi du bon namespace - - $draw = intval($this->request->getVar('draw')); - $data = $Remise->getAllDemandeRemiseToday(); - $totalRecords = count($data); - - $result = [ - "draw" => $draw, - "recordsTotal" => $totalRecords, - "recordsFiltered" => $totalRecords, - "data" => [] - ]; - foreach ($data as $key => $value) { - $buttons = ''; - - if (in_array('validateRemise', $this->permission) && $value['demande_status'] == 'En attente') { - $buttons .= ''; - } + { + helper(['url', 'form']); + $Remise = new Remise(); - if (in_array('refusedRemise', $this->permission) && $value['demande_status'] == 'En attente') { - $buttons .= ' '; - } - // die(var_dump($value)); - $result['data'][$key] = [ - $value['id_demande'], - $value['product'], - number_format($value['total_price'], 0, '.', ' '), - number_format($value['montant_demande'], 0, '.', ' '), - $value['demande_status'], - $buttons + $draw = intval($this->request->getVar('draw')); + $data = $Remise->getAllDemandeRemiseToday(); + $totalRecords = count($data); + + $result = [ + "draw" => $draw, + "recordsTotal" => $totalRecords, + "recordsFiltered" => $totalRecords, + "data" => [] ]; - } + foreach ($data as $key => $value) { + $buttons = ''; - return $this->response->setJSON($result); -} + if (in_array('validateRemise', $this->permission) && $value['demande_status'] == 'En attente') { + $buttons .= ''; + } + + if (in_array('refusedRemise', $this->permission) && $value['demande_status'] == 'En attente') { + $buttons .= ' '; + } + + $result['data'][$key] = [ + $value['id_demande'], + $value['product'], + number_format($value['total_price'], 0, '.', ' '), + number_format($value['montant_demande'], 0, '.', ' '), + $value['demande_status'], + $buttons + ]; + } + return $this->response->setJSON($result); + } - public function updateRemise($id_demande) { + + public function updateRemise($id_demande) + { $this->verifyRole('validateRemise'); - // Load validation service $validation = \Config\Services::validation(); - $data['page_title'] = $this->pageTitle; $validation->setRules([ @@ -94,15 +93,15 @@ class RemiseController extends AdminController $validationData = [ 'demande_status' => $this->request->getPost('demande_status') ]; - $data = [ - 'demande_status' => $this->request->getPost('demande_status') - ]; - + $Remise = new Remise(); - if ($this->request->getMethod()=='post') { + + if ($this->request->getMethod() == 'post') { $today = date('Y-m-d'); + $demande_status = $this->request->getPost('demande_status'); + $data = [ - 'demande_status' => $this->request->getPost('demande_status'), + 'demande_status' => $demande_status, 'date_demande' => $today, ]; @@ -111,10 +110,47 @@ class RemiseController extends AdminController $Notification = new NotificationController(); $session = session(); $users = $session->get('user'); - $Notification->createNotification("Une demande de remise a été " . $this->request->getPost('demande_status')." Pour le produit: ".$remise_product,"Caissière",(int)$users['store_id'],'remise/'); + $ordersModel = new Orders(); + $order_id = $Remise->getOrderIdByDemandeId($id_demande); + + // Logique de notification selon le statut + 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 + $Notification->createNotification( + "Votre demande de remise a été refusée pour le produit: " . $remise_product . ". Veuillez modifier la commande.", + "COMMERCIALE", + (int)$users['store_id'], + "orders" // Retour à l'ajout de commande chez le commercial + ); + + $message = 'La demande de remise a été refusée. Une notification a été envoyée au commercial.'; + + } 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 + if ($order_id) { + $ordersModel->update($order_id, ['paid_status' => 2]); // 2 = En Attente + } + + // Notification à la Caissière pour qu'elle valide la commande + $Notification->createNotification( + "Une commande avec remise acceptée est prête pour validation. Produit: " . $remise_product, + "Caissière", + (int)$users['store_id'], + "orders" // Va chez la caissière pour validation finale + ); + + $message = 'La demande de remise a été acceptée. La commande a été envoyée à la caissière pour validation.'; + } + return $this->response->setJSON([ 'success' => true, - 'messages' => 'Vous avez '.$this->request->getPost('demande_status').' la remise' + 'messages' => $message ]); } else { return $this->response->setJSON([ @@ -123,7 +159,5 @@ class RemiseController extends AdminController ]); } } - } - -} +} \ No newline at end of file diff --git a/app/Controllers/ReportController.php b/app/Controllers/ReportController.php index 33fcb821..22836b87 100644 --- a/app/Controllers/ReportController.php +++ b/app/Controllers/ReportController.php @@ -36,20 +36,24 @@ class ReportController extends AdminController $parking_data = $Reports->getOrderData($today_year); $data['report_years'] = $Reports->getOrderYear(); - // Process the parking data and calculate total amounts - $final_parking_data = []; - foreach ($parking_data as $month => $orders) { - $total_amount_earned = 0; // Initialize the total amount for the month - - if (!empty($orders)) { - foreach ($orders as $order) { - $total_amount_earned += (float) $order['net_amount']; + // Process the parking data and calculate total amounts + $final_parking_data = []; + foreach ($parking_data as $month => $orders) { + $total_amount_earned = 0; + + if (!empty($orders)) { + foreach ($orders as $order) { + // Utiliser le montant selon la logique : discount si existe, sinon gross_amount + if (!empty($order['discount']) && $order['discount'] > 0) { + $total_amount_earned += (float) $order['discount']; + } else { + $total_amount_earned += (float) $order['gross_amount']; + } + } } + + $final_parking_data[$month] = $total_amount_earned; } - - $final_parking_data[$month] = $total_amount_earned; - } - // Data for the camembert (pie chart) $paymentModes = $Orders->getPaymentModes(); $total_mvola1 = $paymentModes->total_mvola1; @@ -256,76 +260,73 @@ class ReportController extends AdminController $Orders = new Orders(); $session = session(); $users = $session->get('user'); - if ($users['group_name'] === "Conseil" || $users['group_name'] === "Direction" ) { + + // Pour Direction et Conseil : afficher TOUTES les performances + if ($users['group_name'] === "DAF" || $users['group_name'] === "Direction") { $orderPaid = $Orders->getPerformanceByOrders(); foreach ($orderPaid as $key => $value) { - $benefice = + // Déterminer le prix de vente réel + $prix_vente_reel = (!empty($value['discount']) && $value['discount'] > 0) + ? $value['discount'] + : $value['prix_vente']; + + // Calculer le bénéfice + $benefice = $prix_vente_reel - $value['price']; + $result['data'][$key] = [ $value['firstname'] . ' ' . $value['lastname'], $value['email'], ($value['sku'] == "" ? $value['motoname'] : $value['sku']), (new DateTime($value['datevente']))->format('Y-m-d'), - number_format($value['price'],0,'.',' '), - number_format($value['prix_vente'],0,'.',' '), + number_format($value['price'], 0, '.', ' '), + number_format($prix_vente_reel, 0, '.', ' '), $this->returnName($value['store_id']), - number_format($value['prix_vente'] - $value['price'],0,'.',' '), + number_format($benefice, 0, '.', ' '), ]; } - - // Return data in JSON format return $this->response->setJSON($result); } - if ($users['group_name'] === "Conseil" || $users['group_name'] === "Direction") { + + // Pour Cheffe d'Agence : performances de son magasin + if ($users['group_name'] === "Cheffe d'Agence") { $orderPaid = $Orders->getPerformanceByOrders1(); foreach ($orderPaid as $key => $value) { - $benefice = + // Déterminer le prix de vente réel + $prix_vente_reel = (!empty($value['discount']) && $value['discount'] > 0) + ? $value['discount'] + : $value['prix_vente']; + $result['data'][$key] = [ $value['firstname'] . ' ' . $value['lastname'], - $value['email'], ($value['sku'] == "" ? $value['motoname'] : $value['sku']), (new DateTime($value['datevente']))->format('Y-m-d'), - number_format($value['price'],0,'.',' '), - number_format($value['prix_vente'],0,'.',' '), - $this->returnName($value['store_id']), - number_format($value['prix_vente'] - $value['price'],0,'.',' '), - + number_format($prix_vente_reel, 0, '.', ' '), ]; } - - // Return data in JSON format return $this->response->setJSON($result); } + + // Pour COMMERCIALE : uniquement ses propres ventes if ($users['group_name'] === "COMMERCIALE") { $orderPaid = $Orders->getPerformanceByOrders2(); foreach ($orderPaid as $key => $value) { - $benefice = - $result['data'][$key] = [ - $value['firstname'] . ' ' . $value['lastname'], - ($value['sku'] == "" ? $value['motoname'] : $value['sku']), - (new DateTime($value['datevente']))->format('Y-m-d'), - number_format($value['prix_vente'],0,'.',' '), - ]; - } - - // Return data in JSON format - return $this->response->setJSON($result); - } - - if ($users['group_name'] === "Cheffe d'Agence") { - $orderPaid = $Orders->getPerformanceByOrders1(); - foreach ($orderPaid as $key => $value) { - $benefice = + // Déterminer le prix de vente réel + $prix_vente_reel = (!empty($value['discount']) && $value['discount'] > 0) + ? $value['discount'] + : $value['prix_vente']; + $result['data'][$key] = [ $value['firstname'] . ' ' . $value['lastname'], ($value['sku'] == "" ? $value['motoname'] : $value['sku']), (new DateTime($value['datevente']))->format('Y-m-d'), - number_format($value['prix_vente'],0,'.',' '), + number_format($prix_vente_reel, 0, '.', ' '), ]; } - - // Return data in JSON format return $this->response->setJSON($result); } + + // Retour par défaut si aucun rôle ne correspond + return $this->response->setJSON($result); } public function fetchmecperformance() diff --git a/app/Controllers/SortieCaisseController.php b/app/Controllers/SortieCaisseController.php index a4ed188b..61ba799d 100644 --- a/app/Controllers/SortieCaisseController.php +++ b/app/Controllers/SortieCaisseController.php @@ -7,6 +7,12 @@ use App\Models\SortieCaisse; use App\Models\Orders; use App\Models\Recouvrement; use App\Models\Stores; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Writer\Csv as CsvWriter; +use PhpOffice\PhpSpreadsheet\Style\Fill; +use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Alignment; class SortieCaisseController extends AdminController { @@ -157,7 +163,6 @@ class SortieCaisseController extends AdminController ], ]; - private $pageTitle = 'Décaissement'; public function index() @@ -223,400 +228,829 @@ class SortieCaisseController extends AdminController return json_encode($data); } - public function fetchSortieCaisseData() -{ - helper(['url', 'form']); - $SortieCaisse = new SortieCaisse(); - - // Initialiser les variables pour DataTables - $draw = intval($this->request->getVar('draw')); - - $data = $SortieCaisse->getAllSortieCaisse(); - $totalRecords = count($data); - - $session = session(); - $users = $session->get('user'); - $result = [ - "draw" => $draw, - "recordsTotal" => $totalRecords, - "recordsFiltered" => $totalRecords, - "data" => [] - ]; - - foreach ($data as $key => $value) { - $buttons = ''; - if (in_array('updateSortieCaisse', $this->permission) && $value['id_sortie'] == $users['id']) { - $buttons .= ''; - } - if (in_array('validateSortieCaisse', $this->permission)) { - $buttons .= ''; - } - // D'éventuelles autres actions peuvent être ajoutées ici + { + helper(['url', 'form']); + $SortieCaisse = new SortieCaisse(); + + // Initialiser les variables pour DataTables + $draw = intval($this->request->getVar('draw')); + + $data = $SortieCaisse->getAllSortieCaisse(); + $totalRecords = count($data); + $session = session(); $users = $session->get('user'); - if($users["group_name"] === "Caissière"){ - $result['data'][$key] = [ - $value['id_sortie'], - number_format($value['montant_retire'], 0, '.', ' '), - $value['date_retrait'], - $value['sortie_personnel'], - $value['motif'], - $value['statut'], - $value['admin_raison'], - $buttons - ]; - } - elseif ($users["group_name"] === "Direction" || $users["group_name"] === "Conseil") { - $result['data'][$key] = [ - $value['id_sortie'], - number_format($value['montant_retire'], 0, '.', ' '), - $value['date_retrait'], - $value['sortie_personnel'], - $value['motif'], - $value['source_fond'], - $value['initiateur_demande'], - $this->returnStoreName($value['store_id']), - $value['commentaire'], - $value['statut'], - $buttons - ]; - } - } - return $this->response->setJSON($result); -} - -public function fetchSortieCaisseData1() -{ - helper(['url', 'form']); - $SortieCaisse = new SortieCaisse(); - - // Initialiser les variables pour DataTables - $draw = intval($this->request->getVar('draw')); - - $data = $SortieCaisse->getAllSortieCaisse1(); - $totalRecords = count($data); - - $session = session(); - $users = $session->get('user'); - $result = [ - "draw" => $draw, - "recordsTotal" => $totalRecords, - "recordsFiltered" => $totalRecords, - "data" => [] - ]; - - foreach ($data as $key => $value) { - $buttons = ''; - if (in_array('updateSortieCaisse', $this->permission) && $value['id_sortie'] == $users['id']) { - $buttons .= ''; - } - if (in_array('validateSortieCaisse', $this->permission)) { - $buttons .= ''; + // ✅ Vérifier si l'utilisateur est DAF ou Direction UNIQUEMENT + $canManage = in_array($users['group_name'], ['Direction', 'DAF']); + $isCaissiere = $users['group_name'] === 'Caissière'; + + $result = [ + "draw" => $draw, + "recordsTotal" => $totalRecords, + "recordsFiltered" => $totalRecords, + "data" => [] + ]; + + foreach ($data as $key => $value) { + $buttons = ''; + + // ✅ BOUTON MODIFICATION : Seulement pour DAF et Direction + if (in_array('updateSortieCaisse', $this->permission) && $canManage) { + $buttons .= ''; + } + + // ✅ BOUTON VALIDATION : Seulement pour DAF et Direction + if (in_array('validateSortieCaisse', $this->permission) && $canManage) { + $buttons .= ''; + } + + // ✅ NOUVEAU : BOUTON "MARQUER COMME PAYÉ" pour Caissière + if ($isCaissiere && $value['statut'] === 'Valider') { + $buttons .= ''; + } + + // Afficher un badge si déjà payé + if ($value['statut'] === 'Payé') { + $buttons .= ' Payé'; + } + + // ✅ CORRECTION : Enlever 'admin_raison' pour les caissières + if($users["group_name"] === "Caissière"){ + $result['data'][$key] = [ + $value['id_sortie'], + number_format($value['montant_retire'], 0, '.', ' '), + $value['date_retrait'], + $value['sortie_personnel'], + $value['motif'], + $value['statut'], + $value['admin_raison'], + $buttons // Les caissières ne verront PAS les boutons (car $canManage = false) + ]; + } + elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF") { + $result['data'][$key] = [ + $value['id_sortie'], + number_format($value['montant_retire'], 0, '.', ' '), + $value['date_retrait'], + $value['sortie_personnel'], + $value['motif'], + $value['source_fond'], + $value['initiateur_demande'], + $this->returnStoreName($value['store_id']), + $value['commentaire'], + $value['statut'], + $buttons // DAF et Direction verront les boutons + ]; + } + elseif ($users["group_name"] === "Conseil") { + // ✅ Conseil peut voir les données mais SANS boutons d'action + $result['data'][$key] = [ + $value['id_sortie'], + number_format($value['montant_retire'], 0, '.', ' '), + $value['date_retrait'], + $value['sortie_personnel'], + $value['motif'], + $value['source_fond'], + $value['initiateur_demande'], + $this->returnStoreName($value['store_id']), + $value['commentaire'], + $value['statut'], + '' // Pas de boutons pour Conseil + ]; + } } - // D'éventuelles autres actions peuvent être ajoutées ici + return $this->response->setJSON($result); + } + + /** + * ✅ 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'); - if($users["group_name"] === "Caissière"){ - $result['data'][$key] = [ - $value['id_sortie'], - number_format($value['montant_retire'], 0, '.', ' '), - $value['date_retrait'], - $value['sortie_personnel'], - $value['motif'], - $value['statut'], - $buttons - ]; + + 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é.' + ]); } - elseif ($users["group_name"] === "Direction" || $users["group_name"] === "Conseil") { - $result['data'][$key] = [ - $value['id_sortie'], - number_format($value['montant_retire'], 0, '.', ' '), - $value['date_retrait'], - $value['sortie_personnel'], - $value['motif'], - $value['source_fond'], - $value['initiateur_demande'], - $this->returnStoreName($value['store_id']), - $value['commentaire'], - $value['statut'], - $buttons + + 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é.
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 + if (class_exists('App\Controllers\NotificationController')) { + $Notification = new NotificationController(); + + $montant = number_format($decaissement['montant_retire'], 0, ',', ' '); + $message = "💰 Décaissement payé - " . $montant . " Ar
" . + "Motif: " . $decaissement['motif'] . "
" . + "Caissière: " . $users['firstname'] . ' ' . $users['lastname']; + + // Notifier la Direction + $Notification->createNotification( + $message, + "Direction", + (int)$users['store_id'], + 'sortieCaisse' + ); + + // Notifier le DAF + $Notification->createNotification( + $message, + "DAF", + (int)$users['store_id'], + 'sortieCaisse' + ); + } + + return $this->response->setJSON([ + 'success' => true, + 'messages' => '✅ Décaissement marqué comme PAYÉ
' . + 'Direction et DAF ont été notifiés.
' . + '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) { + log_message('error', 'Erreur markAsPaid: ' . $e->getMessage()); + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur serveur: ' . $e->getMessage() + ]); } + } + + public function fetchSortieCaisseData1() + { + helper(['url', 'form']); + $SortieCaisse = new SortieCaisse(); + + // Initialiser les variables pour DataTables + $draw = intval($this->request->getVar('draw')); + + $data = $SortieCaisse->getAllSortieCaisse1(); + $totalRecords = count($data); + + $session = session(); + $users = $session->get('user'); + + // ✅ Vérifier si l'utilisateur est DAF ou Direction UNIQUEMENT + $canManage = in_array($users['group_name'], ['Direction', 'DAF']); + $isCaissiere = $users['group_name'] === 'Caissière'; + $result = [ + "draw" => $draw, + "recordsTotal" => $totalRecords, + "recordsFiltered" => $totalRecords, + "data" => [] + ]; + + foreach ($data as $key => $value) { + $buttons = ''; + + // ✅ BOUTON MODIFICATION : Seulement pour DAF et Direction + if (in_array('updateSortieCaisse', $this->permission) && $canManage) { + $buttons .= ''; + } + + // ✅ BOUTON VALIDATION : Seulement pour DAF et Direction + if (in_array('validateSortieCaisse', $this->permission) && $canManage) { + $buttons .= ''; + } + + // ✅ NOUVEAU : BOUTON "MARQUER COMME PAYÉ" pour Caissière + if ($isCaissiere && $value['statut'] === 'Valider') { + $buttons .= ''; + } + + // Afficher un badge si déjà payé + if ($value['statut'] === 'Payé') { + $buttons .= ' Payé'; + } + + if($users["group_name"] === "Caissière"){ + $result['data'][$key] = [ + $value['id_sortie'], + number_format($value['montant_retire'], 0, '.', ' '), + $value['date_retrait'], + $value['sortie_personnel'], + $value['motif'], + $value['statut'], + $value['admin_raison'], + $buttons // Les caissières ne verront PAS les boutons + ]; + } + elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF") { + $result['data'][$key] = [ + $value['id_sortie'], + number_format($value['montant_retire'], 0, '.', ' '), + $value['date_retrait'], + $value['sortie_personnel'], + $value['motif'], + $value['source_fond'], + $value['initiateur_demande'], + $this->returnStoreName($value['store_id']), + $value['commentaire'], + $value['statut'], + $buttons // DAF et Direction verront les boutons + ]; + } + elseif ($users["group_name"] === "Conseil") { + // ✅ Conseil peut voir les données mais SANS boutons d'action + $result['data'][$key] = [ + $value['id_sortie'], + number_format($value['montant_retire'], 0, '.', ' '), + $value['date_retrait'], + $value['sortie_personnel'], + $value['motif'], + $value['source_fond'], + $value['initiateur_demande'], + $this->returnStoreName($value['store_id']), + $value['commentaire'], + $value['statut'], + '' // Pas de boutons pour Conseil + ]; + } + } + return $this->response->setJSON($result); } - return $this->response->setJSON($result); -} - - private function returnStoreName(int $id) - { - $Stores = new Stores(); - $stor = $Stores->getActiveStore(); - $Storename = ""; - foreach ($stor as $key => $value) { - if ($value['id'] == $id) { - $Storename = $value['name']; - } + { + $Stores = new Stores(); + $stor = $Stores->getActiveStore(); + $Storename = ""; + foreach ($stor as $key => $value) { + if ($value['id'] == $id) { + $Storename = $value['name']; } - - return $Storename; } + return $Storename; + } + public function createSortieCaisse() { $this->verifyRole('createSortieCaisse'); - - $data['page_title'] = $this->pageTitle; - + $validation = \Config\Services::validation(); - + $validation->setRules([ - 'montant_retire' => 'required', - 'motif' => 'required', + 'montant_retire' => 'required|numeric', + 'motif_select' => 'required', + 'nom' => 'required', + 'fonction' => 'required', + 'mode_paiement' => 'required', + 'montant_lettre' => 'required', + 'date_demande' => 'required' ]); - - - $validationData = [ - 'montant_retire' => $this->request->getPost('montant_retire'), - 'motif' => $this->request->getPost('motif_select'), - ]; - - - // Set validation rules - $orders = new Orders(); - $Recouvrement = new Recouvrement(); - $paymentData = $orders->getPaymentModes(); - $sortieCaisse = new SortieCaisse(); - $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse(); - $total_sortie_caisse1= $total_sortie_caisse->mr; - $totalRecouvrement = $Recouvrement->getTotalRecouvrements(); - //me MVOLA => ESPECE - //bm BANQUE => MVOLA - //be BANQUE => ESPECE - //mb MVOLA => BANQUE - $total_recouvrement_me = $totalRecouvrement->me; - $total_recouvrement_bm = $totalRecouvrement->bm; - $total_recouvrement_be = $totalRecouvrement->be; - $total_recouvrement_mb = $totalRecouvrement->mb; - - // Initialisation des totaux avec 0 au cas où il n'y aurait pas de données - $total = isset($paymentData->total) ? $paymentData->total : 0; - $total_espece1 = isset($paymentData->total_espece1) ? $paymentData->total_espece1 : 0; - $total_espece2 = isset($paymentData->total_espece2) ? $paymentData->total_espece2 : 0; - - $total_espece = $total_espece1 + $total_espece2; - $total_espece1 = $total_espece + $total_recouvrement_me + $total_recouvrement_be - $total_sortie_caisse1; - - - - - $Notification = new NotificationController(); - // $recouvrement_id = $this->request->getPost('recouvrement_id'); - $session = session(); - $users = $session->get('user'); - if ($users && isset($users['firstname'], $users['lastname'])) { - $fullname = $users['firstname'] . ' ' . $users['lastname']; - } - - // Motif venant du select ou du champ "Autre" - $motif = $this->request->getPost('motif_select'); - $source_fond =''; - $initiateur_demande = ''; - $binaryData = ""; - $mimeType = ""; - if (isset($mapping[$motif])) { - $source_fond = $this->mapping[$motif]['source_fond']; - $initiateur_demande = $this->mapping[$motif]['initiateur_demande']; + + if (!$validation->withRequest($this->request)->run()) { + return $this->response->setJSON([ + 'success' => false, + 'messages' => $validation->getErrors() + ]); } - - $file = $this->request->getFile('sortie_preuve'); - - - if ( - $file - && $file->isValid() - && $file->getError() !== UPLOAD_ERR_NO_FILE - && ($tmpName = $file->getTempName()) - && file_exists($tmpName) - ) { - - $binaryData = file_get_contents($tmpName); + + try { + $session = session(); + $user = $session->get('user'); + + // Nettoyage et conversion du montant + $montant_retire_raw = $this->request->getPost('montant_retire'); + $montant_retire = (float) str_replace([' ', ','], ['', '.'], $montant_retire_raw); - $mimeType = $file->getClientMimeType(); + // Récupérer le mode de paiement + $mode_paiement = $this->request->getPost('mode_paiement'); + + if ($montant_retire <= 0) { + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Le montant doit être supérieur à 0' + ]); + } + + // RÉCUPÉRATION DES DONNÉES FINANCIÈRES + $orders = new Orders(); + $Recouvrement = new Recouvrement(); + $sortieCaisse = new SortieCaisse(); + + $paymentData = $orders->getPaymentModes(); + $totalRecouvrement = $Recouvrement->getTotalRecouvrements(); + $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse(); + + // EXTRACTION DES TOTAUX DES SORTIES PAR MODE DE PAIEMENT + $total_sortie_espece = 0; + $total_sortie_mvola = 0; + $total_sortie_virement = 0; - - } - - - - - if ($validation->run($validationData)) { - $data = [ - 'montant_retire' => $this->request->getPost('montant_retire'), - 'date_retrait' => date('Y-m-d'), - 'motif' => $this->request->getPost('motif_select'), - 'commentaire' => $this->request->getPost('sortie_commentaire'), - 'fournisseur' => $this->request->getPost('sortie_fournisseur'), - 'nif_cin' => $this->request->getPost('sortie_nif'), - 'statistique' => $this->request->getPost('sortie_statistique'), - 'telephone' => $this->request->getPost('sortie_phone'), - 'code_postal' => $this->request->getPost('sortie_adresse'), - 'source_fond' => $source_fond, - 'initiateur_demande' => $initiateur_demande, - 'store_id' => $users['store_id'], - 'preuve_achat' => $binaryData, - 'mime_type' => $mimeType, - 'statut' => "En attente", - 'user_id' => $users['id'], - 'sortie_personnel' => $fullname - ]; + if (is_object($total_sortie_caisse)) { + $total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0; + $total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0; + $total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0; + } + + // Recouvrements + $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 + if (is_object($totalRecouvrement)) { + $total_recouvrement_me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0; + $total_recouvrement_be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0; + $total_recouvrement_bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0; + $total_recouvrement_mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0; + } + + // Orders + $total_espece1 = 0; + $total_espece2 = 0; + $total_mvola1 = 0; + $total_mvola2 = 0; + $total_virement1 = 0; + $total_virement2 = 0; - if ((int)$this->request->getPost('montant_retire') > $total_espece1) { - if($sortieCaisse->addSortieCaisse($data)){ - $Notification->createNotification("Une demande décaissement a été enregistré", "Conseil", (int)$users['store_id'], 'sortieCaisse'); - $response['success'] = true; - $response['messages'] = 'Décaissement enregistré avec succès'; - } - else { + if (is_object($paymentData)) { + $total_espece1 = isset($paymentData->total_espece1) ? (float) $paymentData->total_espece1 : 0; + $total_espece2 = isset($paymentData->total_espece2) ? (float) $paymentData->total_espece2 : 0; + $total_mvola1 = isset($paymentData->total_mvola1) ? (float) $paymentData->total_mvola1 : 0; + $total_mvola2 = isset($paymentData->total_mvola2) ? (float) $paymentData->total_mvola2 : 0; + $total_virement1 = isset($paymentData->total_virement_bancaire1) ? (float) $paymentData->total_virement_bancaire1 : 0; + $total_virement2 = isset($paymentData->total_virement_bancaire2) ? (float) $paymentData->total_virement_bancaire2 : 0; + } + + // 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 - + $total_recouvrement_mb + + $total_recouvrement_bm - + $total_sortie_mvola; + + // Virement: Orders - Recouvrements sortants + Recouvrements entrants - Sorties virement + $total_virement_disponible = $total_virement1 + + $total_virement2 - + $total_recouvrement_be - + $total_recouvrement_bm + + $total_recouvrement_mb - + $total_sortie_virement; + + // VÉRIFICATION SELON LE MODE DE PAIEMENT CHOISI + $fonds_disponible = 0; + $mode_paiement_label = ''; + + switch ($mode_paiement) { + case 'En espèce': + $fonds_disponible = $total_espece_disponible; + $mode_paiement_label = 'en espèce'; + break; + + case 'MVOLA': + $fonds_disponible = $total_mvola_disponible; + $mode_paiement_label = 'MVOLA'; + break; + + case 'Virement Bancaire': + $fonds_disponible = $total_virement_disponible; + $mode_paiement_label = 'virement bancaire'; + break; + + default: return $this->response->setJSON([ 'success' => false, - 'messages' => 'Erreur lors de la modification du décaissement. Veuillez réessayer.' + 'messages' => 'Mode de paiement invalide' ]); + } + + // Vérification des fonds + if ($montant_retire > $fonds_disponible) { + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Décaissement échoué — fonds ' . $mode_paiement_label . ' insuffisants.
' . + 'Disponible: ' . number_format($fonds_disponible, 0, ',', ' ') . ' Ar
' . + 'Demandé: ' . number_format($montant_retire, 0, ',', ' ') . ' Ar

' . + 'Soldes actuels:
' . + '• Espèce: ' . number_format($total_espece_disponible, 0, ',', ' ') . ' Ar
' . + '• MVOLA: ' . number_format($total_mvola_disponible, 0, ',', ' ') . ' Ar
' . + '• Virement: ' . number_format($total_virement_disponible, 0, ',', ' ') . ' Ar' + ]); + } + + // PRÉPARATION DES DONNÉES + $motif = $this->request->getPost('motif_select'); + + $data = [ + 'montant_retire' => $montant_retire, + 'date_retrait' => date('Y-m-d H:i:s'), + 'motif' => $motif, + 'commentaire' => $this->request->getPost('sortie_commentaire') ?? '', + 'fournisseur' => $this->request->getPost('sortie_fournisseur') ?? '', + 'nif_cin' => $this->request->getPost('sortie_nif') ?? '', + 'statistique' => $this->request->getPost('sortie_statistique') ?? '', + 'telephone' => $this->request->getPost('sortie_phone') ?? '', + 'code_postal' => $this->request->getPost('sortie_adresse') ?? '', + 'statut' => 'En attente', + 'user_id' => $user['id'], + 'store_id' => $user['store_id'], + 'admin_raison' => '', + 'sortie_personnel' => $user['firstname'] . ' ' . $user['lastname'], + 'nom_demandeur' => $this->request->getPost('nom'), + 'fonction_demandeur' => $this->request->getPost('fonction'), + 'mode_paiement' => $mode_paiement, + 'montant_lettre' => $this->request->getPost('montant_lettre'), + 'date_demande' => $this->request->getPost('date_demande'), + 'reference' => $this->request->getPost('reference') ?? '' + ]; + + // Mapping source_fond et initiateur + if (isset($this->mapping[$motif])) { + $data['source_fond'] = $this->mapping[$motif]['source_fond']; + $data['initiateur_demande'] = $this->mapping[$motif]['initiateur_demande']; + } else { + $data['source_fond'] = 'Caisse Courante'; + $data['initiateur_demande'] = 'Caissière'; + } + + // Champs supplémentaires pour montant > 1,000,000 + if ($montant_retire > 1000000) { + $data['numero_fiche'] = $this->request->getPost('numero_fiche') ?? ''; + $data['date_fiche'] = $this->request->getPost('date_fiche') ?? null; + $data['service_demandeur'] = $this->request->getPost('service_demandeur') ?? ''; + $data['objet_depense'] = $this->request->getPost('objet_depense') ?? ''; + $data['nature_depense'] = $this->request->getPost('nature_depense') ?? ''; + $data['montant_estime'] = $this->request->getPost('montant_estime') ?? ''; + $data['mode_reglement'] = $this->request->getPost('mode_reglement') ?? ''; + $data['date_paiement_prevue'] = $this->request->getPost('date_paiement_prevue') ?? null; + + $justificatifs = $this->request->getPost('justificatifs'); + if (is_array($justificatifs) && count($justificatifs) > 0) { + $data['justificatifs'] = json_encode($justificatifs); + } + + $data['visa_demandeur'] = $this->request->getPost('visa_demandeur') ?? ''; + $data['date_visa_demandeur'] = $this->request->getPost('date_visa_demandeur') ?? null; + $data['visa_chef_service'] = $this->request->getPost('visa_chef_service') ?? ''; + $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['observations'] = $this->request->getPost('observations') ?? ''; + } + + // Gestion du fichier + $preuveFile = $this->request->getFile('sortie_preuve'); + if ($preuveFile && $preuveFile->isValid() && !$preuveFile->hasMoved()) { + $newName = $preuveFile->getRandomName(); + $uploadPath = WRITEPATH . 'uploads/sortie_caisse'; + + if (!is_dir($uploadPath)) { + mkdir($uploadPath, 0755, true); + } + + $preuveFile->move($uploadPath, $newName); + $data['preuve_achat'] = $newName; + $data['mime_type'] = $preuveFile->getClientMimeType(); + } + + // INSERTION EN BASE + $model = new SortieCaisse(); + $result = $model->addSortieCaisse($data); + + if ($result) { + // Notification + if (class_exists('App\Controllers\NotificationController')) { + $Notification = new NotificationController(); + $Notification->createNotification( + "Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")", + "Direction", + (int)$user['store_id'], + 'sortieCaisse' + ); } + return $this->response->setJSON([ + 'success' => true, + 'messages' => 'Décaissement de ' . number_format($montant_retire, 0, ',', ' ') . ' Ar créé avec succès
' . + 'Mode de paiement: ' . $mode_paiement . '
' . + 'Nouveau solde ' . $mode_paiement_label . ': ' . number_format($fonds_disponible - $montant_retire, 0, ',', ' ') . ' Ar' + ]); } else { - $response['success'] = false; - $response['messages'] = 'Décaissements échoués — fonds en espèce insuffisants'; + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur lors de l\'enregistrement en base de données' + ]); } - } else { - // Validation failed, return error messages - $response['success'] = false; - $response['messages'] = $validation->getErrors(); + + } catch (\Exception $e) { + log_message('error', 'Erreur createSortieCaisse: ' . $e->getMessage() . ' | Ligne: ' . $e->getLine()); + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur serveur: ' . $e->getMessage() + ]); } - - return $this->response->setJSON($response); } - - - public function updateSortieCaisse($id_sortie) { $this->verifyRole('updateSortieCaisse'); - + $data['page_title'] = $this->pageTitle; - - // Load validation service - $validation = \Config\Services::validation(); - - - $SortieCaisse = new SortieCaisse(); - $orders = new Orders(); - $Recouvrement = new Recouvrement(); - $paymentData = $orders->getPaymentModes(); - $sortieCaisse = new SortieCaisse(); - $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse(); - $total_sortie_caisse1= $total_sortie_caisse->mr; - $totalRecouvrement = $Recouvrement->getTotalRecouvrements(); - - $total_recouvrement_me = $totalRecouvrement->me; - $total_recouvrement_bm = $totalRecouvrement->bm; - $total_recouvrement_be = $totalRecouvrement->be; - $total_recouvrement_mb = $totalRecouvrement->mb; - - // Initialisation des totaux avec 0 au cas où il n'y aurait pas de données - $total = isset($paymentData->total) ? $paymentData->total : 0; - $total_espece1 = isset($paymentData->total_espece1) ? $paymentData->total_espece1 : 0; - $total_espece2 = isset($paymentData->total_espece2) ? $paymentData->total_espece2 : 0; - - $total_espece = $total_espece1 + $total_espece2; - $total_espece1 = $total_espece + $total_recouvrement_me + $total_recouvrement_be - $total_sortie_caisse1; - - - - - + if ($this->request->getMethod() === 'post') { - $session = session(); - $users = $session->get('user'); - if ($users && isset($users['firstname'], $users['lastname'])) { - $fullname = $users['firstname'] . ' ' . $users['lastname']; - } - - // Motif venant du select ou du champ "Autre" - $motif = $this->request->getPost('motif_select'); - - - if (isset($mapping[$motif])) { - $source_fond = $this->mapping[$motif]['source_fond']; - $initiateur_demande = $this->mapping[$motif]['initiateur_demande']; - } - - // Récupération de l'objet UploadedFile - $file = $this->request->getFile('sortie_preuve_edit'); - - if ($file && $file->isValid() && ! $file->hasMoved()) { - // Récupère le chemin temporaire - $tmpName = $file->getTempName(); - // Lit tout le contenu binaire - $binaryData = file_get_contents($tmpName); - // Récupère le Mime-Type pour le stocker si besoin - $mimeType = $file->getClientMimeType(); - } else { - $binaryData = null; - $mimeType = null; + $validation = \Config\Services::validation(); + + $validation->setRules([ + 'montant_retire_edit' => 'required|numeric', + 'nom_edit' => 'required', + 'fonction_edit' => 'required', + 'date_demande_edit' => 'required' + // Suppression des règles de validation pour les champs qui seront en hidden + // La preuve d'achat devient facultative + ]); + + if (!$validation->withRequest($this->request)->run()) { + return $this->response->setJSON([ + 'success' => false, + 'messages' => $validation->getErrors() + ]); } - $data = [ - 'montant_retire' => $this->request->getPost('montant_retire_edit'), - 'date_retrait' => date('Y-m-d'), - 'motif' => $this->request->getPost('motif_select'), - 'commentaire' => $this->request->getPost('sortie_commentaire_edit'), - 'fournisseur' => $this->request->getPost('sortie_fournisseur_edit'), - 'nif_cin' => $this->request->getPost('sortie_nif_edit'), - 'statistique' => $this->request->getPost('sortie_statistique_edit'), - 'telephone' => $this->request->getPost('sortie_phone_edit'), - 'code_postal' => $this->request->getPost('sortie_adresse_edit'), - 'sortie_personnel' => $fullname, - 'source_fond' => $source_fond, - 'initiateur_demande' => $initiateur_demande, - 'store_id' => $users['store_id'], - 'preuve_achat' => $binaryData, - 'mime_type' => $mimeType - ]; - - - $Notification = new NotificationController(); - if ((int) $this->request->getPost('sortie_montant_edit') > $total_espece1) { - if($SortieCaisse->updateSortieCaisse($id_sortie, $data)){ + + try { + $session = session(); + $users = $session->get('user'); + + // Récupérer le décaissement actuel + $sortieCaisse = new SortieCaisse(); + $currentSortie = $sortieCaisse->getSortieCaisseSingle($id_sortie); + + if (!$currentSortie) { return $this->response->setJSON([ - 'success' => true, - 'messages' => 'Décaissement modifié avec succès !' + 'success' => false, + 'messages' => 'Décaissement introuvable' ]); } - else { + + // Nettoyage et conversion du montant + $montant_retire_raw = $this->request->getPost('montant_retire_edit'); + $montant_retire = (float) str_replace([' ', ','], ['', '.'], $montant_retire_raw); + + // Récupérer le motif (disabled dans le form, donc on le récupère du champ hidden) + $motif = $this->request->getPost('motif_select_edit') ?: $this->request->getPost('motif_select_edit_hidden'); + + // Récupérer le mode de paiement (maintenant éditable, donc directement du formulaire) + $mode_paiement = $this->request->getPost('mode_paiement_edit'); + + if ($montant_retire <= 0) { return $this->response->setJSON([ 'success' => false, - 'messages' => 'Erreur lors de la modification du décaissement. Veuillez réessayer.' + 'messages' => 'Le montant doit être supérieur à 0' ]); } + + // RÉCUPÉRATION DES DONNÉES FINANCIÈRES + $orders = new Orders(); + $Recouvrement = new Recouvrement(); - - } else { + $paymentData = $orders->getPaymentModes(); + $totalRecouvrement = $Recouvrement->getTotalRecouvrements(); + $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse(); + + // EXTRACTION DES TOTAUX DES SORTIES PAR MODE DE PAIEMENT + $total_sortie_espece = 0; + $total_sortie_mvola = 0; + $total_sortie_virement = 0; + + if (is_object($total_sortie_caisse)) { + $total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0; + $total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0; + $total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0; + } + + // IMPORTANT : Ajouter le montant actuel du décaissement aux sorties + // pour avoir le solde réel avant modification + if ($currentSortie['statut'] === 'Valider') { + switch ($currentSortie['mode_paiement']) { + case 'En espèce': + $total_sortie_espece -= (float) $currentSortie['montant_retire']; + break; + case 'MVOLA': + $total_sortie_mvola -= (float) $currentSortie['montant_retire']; + break; + case 'Virement Bancaire': + $total_sortie_virement -= (float) $currentSortie['montant_retire']; + break; + } + } + + // Recouvrements + $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; + $total_recouvrement_be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0; + $total_recouvrement_bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0; + $total_recouvrement_mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0; + } + + // Orders + $total_espece1 = 0; + $total_espece2 = 0; + $total_mvola1 = 0; + $total_mvola2 = 0; + $total_virement1 = 0; + $total_virement2 = 0; + + if (is_object($paymentData)) { + $total_espece1 = isset($paymentData->total_espece1) ? (float) $paymentData->total_espece1 : 0; + $total_espece2 = isset($paymentData->total_espece2) ? (float) $paymentData->total_espece2 : 0; + $total_mvola1 = isset($paymentData->total_mvola1) ? (float) $paymentData->total_mvola1 : 0; + $total_mvola2 = isset($paymentData->total_mvola2) ? (float) $paymentData->total_mvola2 : 0; + $total_virement1 = isset($paymentData->total_virement_bancaire1) ? (float) $paymentData->total_virement_bancaire1 : 0; + $total_virement2 = isset($paymentData->total_virement_bancaire2) ? (float) $paymentData->total_virement_bancaire2 : 0; + } + + // CALCUL DES SOLDES DISPONIBLES + $total_espece_disponible = $total_espece1 + + $total_espece2 + + $total_recouvrement_me + + $total_recouvrement_be - + $total_sortie_espece; + + $total_mvola_disponible = $total_mvola1 + + $total_mvola2 - + $total_recouvrement_me - + $total_recouvrement_mb + + $total_recouvrement_bm - + $total_sortie_mvola; + + $total_virement_disponible = $total_virement1 + + $total_virement2 - + $total_recouvrement_be - + $total_recouvrement_bm + + $total_recouvrement_mb - + $total_sortie_virement; + + // VÉRIFICATION SELON LE MODE DE PAIEMENT CHOISI + $fonds_disponible = 0; + $mode_paiement_label = ''; + + switch ($mode_paiement) { + case 'En espèce': + $fonds_disponible = $total_espece_disponible; + $mode_paiement_label = 'en espèce'; + break; + + case 'MVOLA': + $fonds_disponible = $total_mvola_disponible; + $mode_paiement_label = 'MVOLA'; + break; + + case 'Virement Bancaire': + $fonds_disponible = $total_virement_disponible; + $mode_paiement_label = 'virement bancaire'; + break; + + default: + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Mode de paiement invalide' + ]); + } + + // Vérification des fonds + if ($montant_retire > $fonds_disponible) { + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Modification échouée — fonds ' . $mode_paiement_label . ' insuffisants.
' . + 'Disponible: ' . number_format($fonds_disponible, 0, ',', ' ') . ' Ar
' . + 'Demandé: ' . number_format($montant_retire, 0, ',', ' ') . ' Ar' + ]); + } + + // PRÉPARATION DES DONNÉES + + $data = [ + 'montant_retire' => $montant_retire, + 'date_retrait' => date('Y-m-d H:i:s'), + 'motif' => $motif, + 'commentaire' => $this->request->getPost('sortie_commentaire_edit') ?? '', + 'fournisseur' => $this->request->getPost('sortie_fournisseur_edit') ?? '', + 'nif_cin' => $this->request->getPost('sortie_nif_edit') ?? '', + 'statistique' => $this->request->getPost('sortie_statistique_edit') ?? '', + 'telephone' => $this->request->getPost('sortie_phone_edit') ?? '', + 'code_postal' => $this->request->getPost('sortie_adresse_edit') ?? '', + 'sortie_personnel' => $users['firstname'] . ' ' . $users['lastname'], + 'store_id' => $users['store_id'], + 'nom_demandeur' => $this->request->getPost('nom_edit'), + 'fonction_demandeur' => $this->request->getPost('fonction_edit'), + 'mode_paiement' => $mode_paiement, + 'montant_lettre' => $this->request->getPost('montant_lettre_edit'), + 'date_demande' => $this->request->getPost('date_demande_edit'), + 'reference' => $this->request->getPost('reference_edit') ?? '' + ]; + + // Mapping source_fond et initiateur + if (isset($this->mapping[$motif])) { + $data['source_fond'] = $this->mapping[$motif]['source_fond']; + $data['initiateur_demande'] = $this->mapping[$motif]['initiateur_demande']; + } else { + $data['source_fond'] = 'Caisse Courante'; + $data['initiateur_demande'] = 'Caissière'; + } + + // Gestion du fichier (FACULTATIF) + $preuveFile = $this->request->getFile('sortie_preuve_edit'); + if ($preuveFile && $preuveFile->isValid() && !$preuveFile->hasMoved()) { + $newName = $preuveFile->getRandomName(); + $uploadPath = WRITEPATH . 'uploads/sortie_caisse'; + + if (!is_dir($uploadPath)) { + mkdir($uploadPath, 0755, true); + } + + $preuveFile->move($uploadPath, $newName); + $data['preuve_achat'] = $newName; + $data['mime_type'] = $preuveFile->getClientMimeType(); + } + // Si aucun nouveau fichier n'est uploadé, on garde l'ancien (pas de modification) + + // MISE À JOUR EN BASE + if ($sortieCaisse->updateSortieCaisse($id_sortie, $data)) { + return $this->response->setJSON([ + 'success' => true, + 'messages' => 'Décaissement modifié avec succès !
' . + 'Nouveau montant: ' . number_format($montant_retire, 0, ',', ' ') . ' Ar (' . $mode_paiement . ')' + ]); + } else { + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur lors de la modification en base de données' + ]); + } + + } catch (\Exception $e) { + log_message('error', 'Erreur updateSortieCaisse: ' . $e->getMessage() . ' | Ligne: ' . $e->getLine()); return $this->response->setJSON([ 'success' => false, - 'messages' => 'Décaissements échoués — fonds en espèce insuffisants' + 'messages' => 'Erreur serveur: ' . $e->getMessage() ]); } } } - public function fetchSortieCaisseSingle($id) { if ($id) { $SortieCaisse = new SortieCaisse(); - $data = $SortieCaisse->getSortieCaisseSingle($id); echo json_encode($data); } @@ -626,41 +1060,42 @@ public function fetchSortieCaisseData1() $this->verifyRole('validateSortieCaisse'); $data['page_title'] = $this->pageTitle; - - if ($this->request->getMethod() === 'post') { - $data = [ - 'admin_raison' => $this->request->getPost('admin_raison'), - 'statut' => $this->request->getPost('statut'), - - ]; - $session = session(); - $users = $session->get('user'); - $SortieCaisse = new SortieCaisse(); - $Notification = new NotificationController(); - - if($SortieCaisse->updateSortieCaisse($id_sortie, $data) && $this->request->getPost('statut') === "Valider"){ - $Notification->createNotification("Décaissement validé avec succès", "Caissière", (int)$users["store_id"], 'sortieCaisse'); - return $this->response->setJSON([ - 'success' => true, - 'messages' => 'Décaissement modifié avec succès !' - ]); - } - elseif($SortieCaisse->updateSortieCaisse($id_sortie, $data) && $this->request->getPost('statut') === "Refuser"){ - $Notification->createNotification("un décaissement a été réfusé", "Caissière", (int)$users["store_id"], 'sortieCaisse'); - return $this->response->setJSON([ - 'success' => true, - 'messages' => 'Décaissement modifié avec succès !' - ]); - } - elseif($SortieCaisse->updateSortieCaisse($id_sortie, $data) && $this->request->getPost('statut') === "En attente"){ - $Notification->createNotification("Décaissement mis en attente", "Caissière", (int)$users["store_id"], 'sortieCaisse'); + if ($this->request->getMethod() === 'post') { + $data = [ + 'admin_raison' => $this->request->getPost('admin_raison'), + 'statut' => $this->request->getPost('statut'), + ]; + + $session = session(); + $users = $session->get('user'); + $SortieCaisse = new SortieCaisse(); + $Notification = new NotificationController(); + + if ($SortieCaisse->updateSortieCaisse($id_sortie, $data)) { + $statut = $this->request->getPost('statut'); + $message = ''; + + switch ($statut) { + case "Valider": + $message = "Décaissement validé avec succès"; + $Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse'); + break; + case "Refuser": + $message = "Un décaissement a été refusé"; + $Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse'); + break; + case "En attente": + $message = "Décaissement mis en attente"; + $Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse'); + break; + } + return $this->response->setJSON([ 'success' => true, - 'messages' => 'Décaissement modifié avec succès !' + 'messages' => 'Décaissement modifié avec succès !' ]); - } - else { + } else { return $this->response->setJSON([ 'success' => false, 'messages' => 'Erreur lors de la modification du décaissement. Veuillez réessayer.' @@ -668,5 +1103,351 @@ public function fetchSortieCaisseData1() } } } + /** + * Exporter les décaissements en Excel + */ +public function exportExcel() +{ + $this->verifyRole('viewSortieCaisse'); + + try { + $SortieCaisse = new SortieCaisse(); + $data = $SortieCaisse->getAllSortieCaisse(); + + $session = session(); + $user = $session->get('user'); + + // Créer un nouveau Spreadsheet + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + // Définir le titre + $sheet->setTitle('Décaissements'); + + // En-têtes selon le groupe de l'utilisateur + if ($user["group_name"] === "Caissière") { + $headers = [ + 'ID', + 'Montant Retiré (Ar)', + 'Date Retrait', + 'Personnel', + 'Motif', + 'Statut', + 'Raison Admin' + ]; + } else { + $headers = [ + 'ID', + 'Montant Retiré (Ar)', + 'Date Retrait', + 'Personnel', + 'Motif', + 'Source Fond', + 'Initiateur', + 'Magasin', + 'Commentaire', + 'Statut' + ]; + } + + // Écrire les en-têtes + $col = 'A'; + foreach ($headers as $header) { + $sheet->setCellValue($col . '1', $header); + $sheet->getStyle($col . '1')->applyFromArray([ + 'font' => [ + 'bold' => true, + 'color' => ['rgb' => 'FFFFFF'] + ], + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'startColor' => ['rgb' => '4472C4'] + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + 'vertical' => Alignment::VERTICAL_CENTER + ] + ]); + $col++; + } + + // Remplir les données + $row = 2; + foreach ($data as $item) { + if ($user["group_name"] === "Caissière") { + $sheet->setCellValue('A' . $row, $item['id_sortie']); + $sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' ')); + $sheet->setCellValue('C' . $row, $item['date_retrait']); + $sheet->setCellValue('D' . $row, $item['sortie_personnel']); + $sheet->setCellValue('E' . $row, $item['motif']); + $sheet->setCellValue('F' . $row, $item['statut']); + $sheet->setCellValue('G' . $row, $item['admin_raison']); + } else { + $sheet->setCellValue('A' . $row, $item['id_sortie']); + $sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' ')); + $sheet->setCellValue('C' . $row, $item['date_retrait']); + $sheet->setCellValue('D' . $row, $item['sortie_personnel']); + $sheet->setCellValue('E' . $row, $item['motif']); + $sheet->setCellValue('F' . $row, $item['source_fond']); + $sheet->setCellValue('G' . $row, $item['initiateur_demande']); + $sheet->setCellValue('H' . $row, $this->returnStoreName($item['store_id'])); + $sheet->setCellValue('I' . $row, $item['commentaire']); + $sheet->setCellValue('J' . $row, $item['statut']); + } + $row++; + } + + // Auto-dimensionner les colonnes + foreach (range('A', $col) as $columnID) { + $sheet->getColumnDimension($columnID)->setAutoSize(true); + } + + // Ajouter des bordures + $styleArray = [ + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'] + ] + ] + ]; + $sheet->getStyle('A1:' . $col . ($row - 1))->applyFromArray($styleArray); + + // Générer le fichier + $writer = new Xlsx($spreadsheet); + $filename = 'decaissements_' . date('Y-m-d_His') . '.xlsx'; + + // Envoyer le fichier au navigateur + header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + header('Content-Disposition: attachment;filename="' . $filename . '"'); + header('Cache-Control: max-age=0'); + + $writer->save('php://output'); + exit; + + } catch (\Exception $e) { + log_message('error', 'Erreur exportExcel: ' . $e->getMessage()); + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur lors de l\'exportation: ' . $e->getMessage() + ]); + } +} + +/** + * Exporter les décaissements en CSV + */ +public function exportCsv() +{ + $this->verifyRole('viewSortieCaisse'); + try { + $SortieCaisse = new SortieCaisse(); + $data = $SortieCaisse->getAllSortieCaisse(); + + $session = session(); + $user = $session->get('user'); + + // Créer un nouveau Spreadsheet + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + // En-têtes selon le groupe de l'utilisateur + if ($user["group_name"] === "Caissière") { + $headers = [ + 'ID', + 'Montant Retiré (Ar)', + 'Date Retrait', + 'Personnel', + 'Motif', + 'Statut', + 'Raison Admin' + ]; + } else { + $headers = [ + 'ID', + 'Montant Retiré (Ar)', + 'Date Retrait', + 'Personnel', + 'Motif', + 'Source Fond', + 'Initiateur', + 'Magasin', + 'Commentaire', + 'Statut' + ]; + } + + // Écrire les en-têtes + $col = 'A'; + foreach ($headers as $header) { + $sheet->setCellValue($col . '1', $header); + $col++; + } + + // Remplir les données + $row = 2; + foreach ($data as $item) { + if ($user["group_name"] === "Caissière") { + $sheet->setCellValue('A' . $row, $item['id_sortie']); + $sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' ')); + $sheet->setCellValue('C' . $row, $item['date_retrait']); + $sheet->setCellValue('D' . $row, $item['sortie_personnel']); + $sheet->setCellValue('E' . $row, $item['motif']); + $sheet->setCellValue('F' . $row, $item['statut']); + $sheet->setCellValue('G' . $row, $item['admin_raison']); + } else { + $sheet->setCellValue('A' . $row, $item['id_sortie']); + $sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' ')); + $sheet->setCellValue('C' . $row, $item['date_retrait']); + $sheet->setCellValue('D' . $row, $item['sortie_personnel']); + $sheet->setCellValue('E' . $row, $item['motif']); + $sheet->setCellValue('F' . $row, $item['source_fond']); + $sheet->setCellValue('G' . $row, $item['initiateur_demande']); + $sheet->setCellValue('H' . $row, $this->returnStoreName($item['store_id'])); + $sheet->setCellValue('I' . $row, $item['commentaire']); + $sheet->setCellValue('J' . $row, $item['statut']); + } + $row++; + } + + // Générer le fichier CSV + $writer = new CsvWriter($spreadsheet); + $writer->setDelimiter(';'); + $writer->setEnclosure('"'); + $writer->setLineEnding("\r\n"); + $writer->setSheetIndex(0); + + $filename = 'decaissements_' . date('Y-m-d_His') . '.csv'; + + // Envoyer le fichier au navigateur + header('Content-Type: text/csv'); + header('Content-Disposition: attachment;filename="' . $filename . '"'); + header('Cache-Control: max-age=0'); + + $writer->save('php://output'); + exit; + + } catch (\Exception $e) { + log_message('error', 'Erreur exportCsv: ' . $e->getMessage()); + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur lors de l\'exportation: ' . $e->getMessage() + ]); + } +} + +/** + * Exporter avec filtres (optionnel) + */ +public function exportWithFilters() +{ + $this->verifyRole('viewSortieCaisse'); + + $format = $this->request->getGet('format'); // 'excel' ou 'csv' + $dateDebut = $this->request->getGet('date_debut'); + $dateFin = $this->request->getGet('date_fin'); + $statut = $this->request->getGet('statut'); + + try { + $SortieCaisse = new SortieCaisse(); + + // Récupérer les données avec filtres + $builder = $SortieCaisse->builder(); + + if ($dateDebut) { + $builder->where('date_retrait >=', $dateDebut); + } + if ($dateFin) { + $builder->where('date_retrait <=', $dateFin); + } + if ($statut) { + $builder->where('statut', $statut); + } + + $data = $builder->get()->getResultArray(); + + $session = session(); + $user = $session->get('user'); + + // Créer le Spreadsheet + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setTitle('Décaissements Filtrés'); + + // En-têtes + if ($user["group_name"] === "Caissière") { + $headers = ['ID', 'Montant Retiré (Ar)', 'Date Retrait', 'Personnel', 'Motif', 'Statut', 'Raison Admin']; + } else { + $headers = ['ID', 'Montant Retiré (Ar)', 'Date Retrait', 'Personnel', 'Motif', 'Source Fond', 'Initiateur', 'Magasin', 'Commentaire', 'Statut']; + } + + $col = 'A'; + foreach ($headers as $header) { + $sheet->setCellValue($col . '1', $header); + $sheet->getStyle($col . '1')->applyFromArray([ + 'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']], + 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '4472C4']], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER] + ]); + $col++; + } + + // Remplir les données + $row = 2; + foreach ($data as $item) { + if ($user["group_name"] === "Caissière") { + $sheet->setCellValue('A' . $row, $item['id_sortie']); + $sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' ')); + $sheet->setCellValue('C' . $row, $item['date_retrait']); + $sheet->setCellValue('D' . $row, $item['sortie_personnel']); + $sheet->setCellValue('E' . $row, $item['motif']); + $sheet->setCellValue('F' . $row, $item['statut']); + $sheet->setCellValue('G' . $row, $item['admin_raison']); + } else { + $sheet->setCellValue('A' . $row, $item['id_sortie']); + $sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' ')); + $sheet->setCellValue('C' . $row, $item['date_retrait']); + $sheet->setCellValue('D' . $row, $item['sortie_personnel']); + $sheet->setCellValue('E' . $row, $item['motif']); + $sheet->setCellValue('F' . $row, $item['source_fond']); + $sheet->setCellValue('G' . $row, $item['initiateur_demande']); + $sheet->setCellValue('H' . $row, $this->returnStoreName($item['store_id'])); + $sheet->setCellValue('I' . $row, $item['commentaire']); + $sheet->setCellValue('J' . $row, $item['statut']); + } + $row++; + } + + // Auto-dimensionner + foreach (range('A', $col) as $columnID) { + $sheet->getColumnDimension($columnID)->setAutoSize(true); + } + + // Générer selon le format + if ($format === 'csv') { + $writer = new CsvWriter($spreadsheet); + $writer->setDelimiter(';'); + $filename = 'decaissements_filtres_' . date('Y-m-d_His') . '.csv'; + header('Content-Type: text/csv'); + } else { + $writer = new Xlsx($spreadsheet); + $filename = 'decaissements_filtres_' . date('Y-m-d_His') . '.xlsx'; + header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + } + + header('Content-Disposition: attachment;filename="' . $filename . '"'); + header('Cache-Control: max-age=0'); + + $writer->save('php://output'); + exit; + + } catch (\Exception $e) { + log_message('error', 'Erreur exportWithFilters: ' . $e->getMessage()); + return $this->response->setJSON([ + 'success' => false, + 'messages' => 'Erreur lors de l\'exportation: ' . $e->getMessage() + ]); + } } +} \ No newline at end of file diff --git a/app/Models/Avance.php b/app/Models/Avance.php index dad3586d..c842b481 100644 --- a/app/Models/Avance.php +++ b/app/Models/Avance.php @@ -11,7 +11,7 @@ class Avance extends Model { 'avance_amount', 'avance_date','user_id', 'customer_name', 'customer_address', 'customer_phone', 'customer_cin', 'gross_amount','amount_due','product_id','is_order','active','store_id', - 'type_avance','type_payment', 'deadline' // Ajout du champ type et deadline + 'type_avance','type_payment', 'deadline','commentaire','product_name' // Ajout du champ type et deadline ]; public function createAvance(array $data) { diff --git a/app/Models/Notification.php b/app/Models/Notification.php index aca407c2..51f378f0 100644 --- a/app/Models/Notification.php +++ b/app/Models/Notification.php @@ -15,12 +15,20 @@ class Notification extends Model $session = session(); $users = $session->get('user'); + $today = date('Y-m-d'); + return $this->where('store_id', $users['store_id']) ->groupStart() ->where('forgroup', $users['group_name']) ->orWhere('forgroup', strtolower('TOUS')) ->groupEnd() - ->where('is_read', 0) + ->groupStart() + ->where('is_read', 0) // toutes les notifications non lues + ->orGroupStart() + ->where('is_read', 1) // notifications lues + ->where('DATE(created_at)', $today) // mais seulement celles d’aujourd’hui + ->groupEnd() + ->groupEnd() ->orderBy('created_at', 'DESC') ->findAll(); } diff --git a/app/Models/Orders.php b/app/Models/Orders.php index acdbe9da..bff43c59 100644 --- a/app/Models/Orders.php +++ b/app/Models/Orders.php @@ -5,6 +5,7 @@ namespace App\Models; use CodeIgniter\Model; use App\Models\Products; use App\Models\OrderItems; +use App\Models\FourchettePrix; class Orders extends Model { @@ -32,7 +33,9 @@ class Orders extends Model 'tranche_1', 'tranche_2', 'order_payment_mode', - 'order_payment_mode_1' + 'order_payment_mode_1', + 'delivered_at', + 'delivered_by' ]; @@ -75,66 +78,66 @@ class Orders extends Model public function getOrdersData(int $id = null) -{ - // Récupération des infos de session - $session = session(); - $user = $session->get('user'); - - // Construction de la requête avec jointures et agrégation des noms de produits - $builder = $this->db->table('orders') - ->select([ - 'orders.id', - 'orders.bill_no', - 'orders.store_id', - 'orders.customer_name', - 'orders.customer_phone', - 'orders.customer_cin', - 'orders.customer_address', - 'orders.discount', - 'orders.date_time', - 'orders.gross_amount', - 'orders.net_amount', - 'orders.paid_status', - 'orders.user_id', - "CONCAT(users.firstname, ' ', users.lastname) AS user_name", - ]) - ->join('users', 'orders.user_id = users.id', 'left') - ->join('orders_item', 'orders_item.order_id = orders.id', 'left') - ->join('products', 'products.id = orders_item.product_id', 'left') - // Sépare chaque nom de produit par un saut de ligne '\n' - ->select("GROUP_CONCAT(products.name SEPARATOR '\\n') AS product_names", false) - ->groupBy('orders.id'); - - // Si on demande une commande précise - if ($id !== null) { - return $builder - ->where('orders.id', $id) - ->get() - ->getRowArray(); - } - - // Récupération du groupe de l'utilisateur - $group = $this->getUserGroupNameByUserId($user['id']); - $groupName = $group['group_name'] ?? ''; - - // Selon le rôle, on affiche toutes les commandes ou seulement celles du magasin - if (in_array($groupName, ['Direction', 'Conseil'], true)) { + { + $session = session(); + $user = $session->get('user'); + + // Si on demande UNE commande spécifique (pas de GROUP_CONCAT) + if ($id !== null) { + return $this->db->table('orders') + ->select([ + 'orders.*', + "CONCAT(users.firstname, ' ', users.lastname) AS user_name" + ]) + ->join('users', 'orders.user_id = users.id', 'left') + ->where('orders.id', $id) + ->get() + ->getRowArray(); + } + + // Pour la LISTE des commandes (avec GROUP_CONCAT) + $builder = $this->db->table('orders') + ->select([ + 'orders.id', + 'orders.bill_no', + 'orders.store_id', + 'orders.customer_name', + 'orders.customer_phone', + 'orders.customer_cin', + 'orders.customer_address', + 'orders.discount', + 'orders.date_time', + 'orders.gross_amount', + 'orders.net_amount', + 'orders.paid_status', + 'orders.user_id', + "CONCAT(users.firstname, ' ', users.lastname) AS user_name" + ]) + ->join('users', 'orders.user_id = users.id', 'left') + ->join('orders_item', 'orders_item.order_id = orders.id', 'left') + ->join('products', 'products.id = orders_item.product_id', 'left') + ->select("IFNULL(GROUP_CONCAT(DISTINCT products.name SEPARATOR '\\n'), '') AS product_names", false) + ->groupBy('orders.id'); + + // Récupération du groupe de l'utilisateur + $group = $this->getUserGroupNameByUserId($user['id']); + $groupName = $group['group_name'] ?? ''; + + // Selon le rôle + if (in_array($groupName, ['Direction', 'Conseil'], true)) { + return $builder + ->orderBy('orders.id', 'DESC') + ->get() + ->getResultArray(); + } + return $builder + ->where('orders.store_id', $user['store_id']) ->orderBy('orders.id', 'DESC') ->get() ->getResultArray(); } - // Autres utilisateurs : filtrage par magasin - return $builder - ->where('orders.store_id', $user['store_id']) - ->orderBy('orders.id', 'DESC') - ->get() - ->getResultArray(); -} - - - public function getOrdersDataByBillNo(string $id = null) { $session = session(); @@ -356,24 +359,27 @@ class Orders extends Model */ public function countTotalPaidOrders() { - return $this->where('paid_status', 1)->countAllResults(); + return $this->whereIn('paid_status', [1, 3])->countAllResults(); } public function getPaidOrderData() { $session = session(); - $users = $session->get('user'); + $users = $session->get('user'); $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); + if($isAdmin) { - return $this->where('paid_status', 1)->orderBy('id', 'DESC')->findAll(); + return $this->whereIn('paid_status', [1, 3]) // ← Ajoutez 3 ici + ->orderBy('id', 'DESC') + ->findAll(); } - else{ - return $this->where('paid_status', 1) - ->where('store_id', $users['store_id']) - ->orderBy('id', 'DESC')->findAll(); - + else { + return $this->whereIn('paid_status', [1, 3]) // ← Ajoutez 3 ici + ->where('store_id', $users['store_id']) + ->orderBy('id', 'DESC') + ->findAll(); } - } + } public function getSingleStat(int $id) { @@ -386,46 +392,87 @@ class Orders extends Model public function getPaymentModes() { $session = session(); - $users = $session->get('user'); + $users = $session->get('user'); $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); - if($isAdmin){ - return $this->db->table('orders') - ->select(' - SUM(net_amount) AS total, - SUM(CASE WHEN orders.order_payment_mode = "MVOLA" THEN tranche_1 ELSE 0 END) AS total_mvola1, - SUM(CASE WHEN orders.order_payment_mode = "MVOLA" THEN tranche_2 ELSE 0 END) AS total_mvola2, - SUM(CASE WHEN orders.order_payment_mode = "En espèce" THEN tranche_1 ELSE 0 END) AS total_espece1, - SUM(CASE WHEN orders.order_payment_mode = "En espèce" THEN tranche_2 ELSE 0 END) AS total_espece2, - SUM(CASE WHEN orders.order_payment_mode = "Virement bancaire" THEN tranche_1 ELSE 0 END) AS total_virement_bancaire1, - SUM(CASE WHEN orders.order_payment_mode = "Virement bancaire" THEN tranche_2 ELSE 0 END) AS total_virement_bancaire2 - ') - ->where('orders.paid_status', 1) - ->get() - ->getRowObject(); - } - else{ - return $this->db->table('orders') + + $baseQuery = $this->db->table('orders') ->select(' - SUM(net_amount) AS total, - SUM(CASE WHEN orders.order_payment_mode = "MVOLA" THEN tranche_1 ELSE 0 END) AS total_mvola1, - SUM(CASE WHEN orders.order_payment_mode = "MVOLA" THEN tranche_2 ELSE 0 END) AS total_mvola2, - SUM(CASE WHEN orders.order_payment_mode = "En espèce" THEN tranche_1 ELSE 0 END) AS total_espece1, - SUM(CASE WHEN orders.order_payment_mode = "En espèce" THEN tranche_2 ELSE 0 END) AS total_espece2, - SUM(CASE WHEN orders.order_payment_mode = "Virement bancaire" THEN tranche_1 ELSE 0 END) AS total_virement_bancaire1, - SUM(CASE WHEN orders.order_payment_mode = "Virement bancaire" THEN tranche_2 ELSE 0 END) AS total_virement_bancaire2 + + SUM(CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN orders.discount + ELSE orders.net_amount + END) AS total, + + + SUM(CASE + WHEN orders.order_payment_mode = "MVOLA" THEN + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN (orders.tranche_1 * orders.discount / orders.net_amount) + ELSE orders.tranche_1 + END + ELSE 0 + END) AS total_mvola1, + SUM(CASE + WHEN orders.order_payment_mode_1 = "MVOLA" THEN + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN (orders.tranche_2 * orders.discount / orders.net_amount) + ELSE orders.tranche_2 + END + ELSE 0 + END) AS total_mvola2, + + + SUM(CASE + WHEN orders.order_payment_mode = "En espèce" THEN + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN (orders.tranche_1 * orders.discount / orders.net_amount) + ELSE orders.tranche_1 + END + ELSE 0 + END) AS total_espece1, + SUM(CASE + WHEN orders.order_payment_mode_1 = "En espèce" THEN + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN (orders.tranche_2 * orders.discount / orders.net_amount) + ELSE orders.tranche_2 + END + ELSE 0 + END) AS total_espece2, + + + SUM(CASE + WHEN orders.order_payment_mode = "Virement bancaire" THEN + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN (orders.tranche_1 * orders.discount / orders.net_amount) + ELSE orders.tranche_1 + END + ELSE 0 + END) AS total_virement_bancaire1, + SUM(CASE + WHEN orders.order_payment_mode_1 = "Virement bancaire" THEN + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN (orders.tranche_2 * orders.discount / orders.net_amount) + ELSE orders.tranche_2 + END + ELSE 0 + END) AS total_virement_bancaire2 ') - ->where('orders.paid_status', 1) - ->where('orders.store_id', $users['store_id']) - ->get() - ->getRowObject(); + ->whereIn('orders.paid_status', [1, 3]); // ← CHANGEZ CETTE LIGNE + + if (!$isAdmin) { + $baseQuery->where('orders.store_id', $users['store_id']); } + + return $baseQuery->get()->getRowObject(); } - - - - - public function getTotalOrders() { return $this->db->table('orders') @@ -497,13 +544,13 @@ class Orders extends Model ->join('orders_item', 'orders.id = orders_item.order_id') ->join('products', 'orders_item.product_id = products.id') ->join('brands', 'brands.id = products.marque') - ->where('orders.paid_status', 1) + ->whereIn('orders.paid_status', [1, 3]) // ← CHANGÉ ICI ->get() - ->getResult(); // Return a single object - + ->getResult(); + return $order; } - + public function getTotalProductvente2(?int $id) { if ($id != 0) { @@ -512,35 +559,44 @@ class Orders extends Model ->join('orders_item', 'orders.id = orders_item.order_id') ->join('products', 'orders_item.product_id = products.id') ->join('brands', 'brands.id = products.marque') - ->where('orders.paid_status', 1) + ->whereIn('orders.paid_status', [1, 3]) // ← CHANGÉ ICI ->where('orders.store_id', $id) ->get() - ->getResult(); // Return a single object + ->getResult(); } else { $order = $this->db->table('orders') ->select('orders.id, orders.paid_status, orders.store_id, orders.date_time, brands.name, products.name as Pname, products.sku') ->join('orders_item', 'orders.id = orders_item.order_id') ->join('products', 'orders_item.product_id = products.id') ->join('brands', 'brands.id = products.marque') - ->where('orders.paid_status', 1) + ->whereIn('orders.paid_status', [1, 3]) // ← CHANGÉ ICI ->get() - ->getResult(); // Return a single object + ->getResult(); } - - + return $order; } public function getOrderVendue() { $order = $this->db->table('products') - ->select('products.*, products.name as Pname, products.sku, (orders_item.amount - orders.discount) as totalNet, orders.date_time as DateTime') + ->select(' + products.*, + products.name as Pname, + products.sku, + CASE + WHEN orders.discount > 0 AND orders.discount IS NOT NULL + THEN orders.discount + ELSE orders.gross_amount + END as totalNet, + orders.date_time as DateTime + ', false) // ← MODIFIÉ ICI ->join('orders_item', 'products.id = orders_item.product_id') ->join('orders', 'orders_item.order_id = orders.id') - ->where('orders.paid_status', 1) + ->whereIn('orders.paid_status', [1, 3]) // ← ET ICI ->get() - ->getResultArray(); // Return a single object - + ->getResultArray(); + return $order; } @@ -552,31 +608,32 @@ class Orders extends Model ->join('orders_item', 'orders.id = orders_item.order_id') ->join('products', 'products.id = orders_item.product_id') ->join('users', 'users.id = orders.user_id') - ->where('orders.paid_status', 1) + ->whereIn('orders.paid_status', [1, 3]) ->get() ->getResultArray(); - + return $Performance; } + // for Adminan cheffe d'agence - public function getPerformanceByOrders1() - { - $session = session(); - $users = $session->get('user'); - $Performance = $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') - ->where('orders.paid_status', 1) - ->where('orders.store_id', $users["store_id"]) - ->get() - ->getResultArray(); + public function getPerformanceByOrders1() +{ + $session = session(); + $users = $session->get('user'); + $Performance = $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]) + ->where('orders.store_id', $users["store_id"]) + ->get() + ->getResultArray(); - return $Performance; - } + return $Performance; +} // for commerciale public function getPerformanceByOrders2() @@ -590,14 +647,13 @@ class Orders extends Model ->join('orders_item', 'orders.id = orders_item.order_id') ->join('products', 'products.id = orders_item.product_id') ->join('users', 'users.id = orders.user_id') - ->where('orders.paid_status', 1) + ->whereIn('orders.paid_status', [1, 3]) ->where('orders.user_id', $users["id"]) ->get() ->getResultArray(); - + return $Performance; } - public function update1(int $id, $data1,$data2) @@ -614,9 +670,6 @@ class Orders extends Model } - - - public function getUserList() { $session = session(); @@ -640,103 +693,102 @@ class Orders extends Model public function getUserPerformanceToday(string $date) -{ - $users = $this->getUserList(); - $results = []; - - foreach ($users as $user) { - // 1) Récupérer count et sum - $summary = $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) - ->where('paid_status', 1) - ->get() - ->getRow(); - - // 2) Récupérer la liste des order_id - $ids = $this->db->table('orders') - ->select('id') - ->where('user_id', $user->user_id) - ->where('DATE(date_time)', $date) - ->where('paid_status', 1) - ->get() - ->getResult(); - - $orderIds = array_map(fn($o) => $o->id, $ids); - - // 3) Composer le résultat pour cet utilisateur - $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, - 'order_ids' => $orderIds, - ]; + { + $users = $this->getUserList(); + $results = []; + + foreach ($users as $user) { + $summary = $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(); + + $ids = $this->db->table('orders') + ->select('id') + ->where('user_id', $user->user_id) + ->where('DATE(date_time)', $date) + ->whereIn('paid_status', [1, 3]) + ->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, + 'order_ids' => $orderIds, + ]; + } + + return $results; } - return $results; -} - - -public function getUserPerformanceByWeek(string $date = null) -{ - $users = $this->getUserList(); - $results = []; - - // Si aucune date fournie, on prend aujourd'hui - if ($date === null) { - $date = date('Y-m-d'); + public function getUserPerformanceByWeek(string $date = null) + { + $users = $this->getUserList(); + $results = []; + + if ($date === null) { + $date = date('Y-m-d'); + } + + $timestamp = strtotime($date); + $startOfWeek = date('Y-m-d', strtotime('monday this week', $timestamp)); + $endOfWeek = date('Y-m-d', strtotime('sunday this week', $timestamp)); + + foreach ($users as $user) { + $summary = $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(); + + $ids = $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(); + + $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 ?? 0, + 'total_prix_vente' => $summary->total_prix_vente ?? 0, + 'order_ids' => $orderIds, + ]; + } + + return $results; } - // Trouver le lundi et le dimanche de la semaine de $date - $timestamp = strtotime($date); - $startOfWeek = date('Y-m-d', strtotime('monday this week', $timestamp)); - $endOfWeek = date('Y-m-d', strtotime('sunday this week', $timestamp)); - - foreach ($users as $user) { - // 1) Récupérer count et sum pour la semaine - $summary = $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) - ->where('paid_status', 1) - ->get() - ->getRow(); - - // 2) Récupérer la liste des order_id de la semaine - $ids = $this->db->table('orders') - ->select('id') - ->where('user_id', $user->user_id) - ->where('DATE(date_time) >=', $startOfWeek) - ->where('DATE(date_time) <=', $endOfWeek) - ->where('paid_status', 1) - ->get() - ->getResult(); - - $orderIds = array_map(fn($o) => $o->id, $ids); +public function checkMinimalPrice($product_id, $discount) +{ + $fourchetteModel = new FourchettePrix(); // plus court car on a "use" + $row = $fourchetteModel->where('product_id', $product_id)->first(); - // 3) Résultat pour l'utilisateur - $results[] = [ - 'user_id' => $user->user_id, - 'full_name' => $user->full_name, - 'total_user_order' => $summary->total_user_order ?? 0, - 'total_prix_vente' => $summary->total_prix_vente ?? 0, - 'order_ids' => $orderIds, - ]; + if ($row) { + return ($discount >= $row['prix_minimal']); } - - return $results; + return true; } - - public function getUserPerformanceByMonth(string $month) { - // Calcul du premier et dernier jour du mois donné $startOfMonth = date('Y-m-01', strtotime($month . '-01')); $endOfMonth = date('Y-m-t', strtotime($month . '-01')); @@ -744,29 +796,26 @@ public function getUserPerformanceByMonth(string $month) $results = []; foreach ($users as $user) { - // 1) Récupérer les stats du mois $orderData = $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) - ->where('paid_status', 1) + ->whereIn('paid_status', [1, 3]) ->get() ->getRow(); - // 2) Récupérer les IDs des commandes du mois $ids = $this->db->table('orders') ->select('id') ->where('user_id', $user->user_id) ->where('DATE(date_time) >=', $startOfMonth) ->where('DATE(date_time) <=', $endOfMonth) - ->where('paid_status', 1) + ->whereIn('paid_status', [1, 3]) ->get() ->getResult(); $orderIds = array_map(fn($o) => $o->id, $ids); - // 3) Composer les résultats $results[] = [ 'user_id' => $user->user_id, 'full_name' => $user->full_name, diff --git a/app/Models/Products.php b/app/Models/Products.php index 6851160d..05380e5f 100644 --- a/app/Models/Products.php +++ b/app/Models/Products.php @@ -153,15 +153,32 @@ class Products extends Model return $this->delete($id) ? true : false; } + public function countTotalProducts() + { + $db = \Config\Database::connect(); + + // Sous-requête pour obtenir les product_id dans avances actives + $subQuery = $db->table('avances') + ->select('product_id') + ->where('active', 1) + ->where('is_order', 0) + ->getCompiledSelect(); + + // Compter les produits disponibles + return $this->where('is_piece', 0) + ->where('product_sold', 0) + ->where("id NOT IN ($subQuery)", null, false) + ->countAllResults(); + } + /** - * count all product + * count all products including sold and reserved (méthode originale si besoin) */ - public function countTotalProducts() + public function countAllProductsIncludingSold() { return $this->countAll(); } - public function getTotalProductPriceByIds(array $productIds) { try { diff --git a/app/Models/Recouvrement.php b/app/Models/Recouvrement.php index 05b34386..beab52b1 100644 --- a/app/Models/Recouvrement.php +++ b/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'], ['Conseil', 'Direction']); + $isAdmin = in_array($users['group_name'], ['DAF', 'Direction']); if($isAdmin){ if ($id) { return $this->db->table('recouvrement') diff --git a/app/Models/Remise.php b/app/Models/Remise.php index 03cb9bd1..c742e84d 100644 --- a/app/Models/Remise.php +++ b/app/Models/Remise.php @@ -1,6 +1,5 @@ -get('user'); - if ($users["group_name"] === "Conseil") { - try { - - $session = session(); - $users = $session->get('user'); - $users = session()->get('user_data'); - if($users['group_name'] === "Direction"){ - return $this ->where('demande_status', 'En attente') - ->orderBy('date_demande', 'DESC') - ->findAll(); - } - return $this->where('demande_status', 'En attente') - ->orderBy('date_demande', 'DESC') - ->findAll(); - - } catch (\Exception $e) { - - log_message('error', 'Erreur lors de la récupération des demandes du jour : ' . $e->getMessage()); - return []; - } - } try { - - $session = session(); - $users = $session->get('user'); - - return $this - ->where('id_store',$users['store_id']) - ->orderBy('date_demande', 'DESC') - ->findAll(); - + $session = session(); + $users = $session->get('user'); + + // Si le rôle est CONSEIL → voir toutes les demandes "En attente" + if ($users["group_name"] === "Conseil") { + 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") { + return $this->orderBy('date_demande', 'DESC') + ->findAll(); + } + + // Autres rôles (Caissière, etc.) → voir uniquement celles de son 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()); return []; } } - - public function addDemande( array $data) { + + public function addDemande(array $data) + { try { - return $this->insert($data); + return $this->insert($data); } catch (\Exception $e) { log_message('error', 'Erreur lors de l\'ajout de la demande de remise : ' . $e->getMessage()); return false; } } - - public function getRemiseData1(int $remise_id){ - return $this->select('montant_demande','id_order') - ->where('id_demande',$remise_id) - ->first(); + + public function getRemiseData1(int $remise_id) + { + return $this->select('montant_demande, id_order') + ->where('id_demande', $remise_id) + ->first(); } - - public function updateRemise( $id,$data) { + + /** + * 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') + ->where('id_demande', $id_demande) + ->first(); + + return $result['id_order'] ?? null; + } + + public function updateRemise($id, $data) + { if ($id <= 0) { - log_message('error', 'ID invalide pour la mise à jour du recouvrement : ' . $id); + log_message('error', 'ID invalide pour la mise à jour de la demande : ' . $id); return false; } - + try { - // Essayer de mettre à jour les données - return $this->update($id, $data); - if ($data['demande_status'] == 'accepte') { - $Order = new Orders(); - $remise_data = $this->getRemiseData1($id); - $montant_demande = $remise_data->montant_demande; - $order_id = $remise_data->id_order; - $data1 = [ - 'net_amount' => $montant_demande, - 'gross_amount' => $montant_demande - ]; - $data2 = [ - 'rate' => $montant_demande, - 'amount' => $montant_demande - ]; - $Order->update1($order_id, $data1,$data2); - } + // 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) { - // En cas d'exception, loguer l'erreur - log_message('error', 'Erreur lors de la mise à jour du recouvrement : ' . $e->getMessage()); - return false; + log_message('error', 'Erreur lors de la mise à jour de la demande : ' . $e->getMessage()); + return false; } } - + public function getProductByDemandeId(int $id_demande): ?string { - $row = $this - ->select('product') - ->where('id_order', $id_demande) - ->first(); - + $row = $this->select('product') + ->where('id_demande', $id_demande) + ->first(); + return $row['product'] ?? null; } - - public function updateRemise1(int $id,$data) -{ - $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."; + + public function updateRemise1(int $id, $data) + { + $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."; + } } } - -} \ No newline at end of file diff --git a/app/Models/Reports.php b/app/Models/Reports.php index a1d85d24..dec52b01 100644 --- a/app/Models/Reports.php +++ b/app/Models/Reports.php @@ -1,7 +1,5 @@ @@ -20,22 +18,22 @@ class Reports extends Model { return ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']; } - + /** - * Getting the year of the orders (paid_status = 1) + * Getting the year of the orders (paid_status = 1 or 3) * @return array */ public function getOrderYear() { - $result = $this->where('paid_status', 1)->findAll(); - + $result = $this->whereIn('paid_status', [1, 3])->findAll(); // ← CHANGÉ ICI + $return_data = array_map(function($v) { return date('Y', strtotime($v['date_time'])); }, $result); - + return array_values(array_unique($return_data)); } - + /** * Getting the order reports based on the year and months * @param mixed $year @@ -45,28 +43,33 @@ class Reports extends Model { if ($year) { $months = $this->months(); + + // Fetch orders with paid_status = 1 or 3 + $result = $this->whereIn('paid_status', [1, 3])->findAll(); - // Fetch orders with paid_status = 1 - $result = $this->where('paid_status', 1)->findAll(); - $final_data = []; - + foreach ($months as $month) { $get_mon_year = $year . '-' . $month; $final_data[$get_mon_year] = []; - + foreach ($result as $v) { $month_year = date('Y-m', strtotime($v['date_time'])); if ($get_mon_year == $month_year) { + // Modifier le montant selon votre logique + if (!empty($v['discount']) && $v['discount'] > 0) { + $v['amount_to_display'] = $v['discount']; // Utiliser le rabais + } else { + $v['amount_to_display'] = $v['gross_amount']; // Utiliser gross_amount + } $final_data[$get_mon_year][] = $v; } } } - - // die(var_dump($final_data)); + return $final_data; } - + return []; } } \ No newline at end of file diff --git a/app/Models/SortieCaisse.php b/app/Models/SortieCaisse.php index 5b569af3..78e1f6e9 100644 --- a/app/Models/SortieCaisse.php +++ b/app/Models/SortieCaisse.php @@ -11,109 +11,138 @@ class SortieCaisse extends Model */ protected $table = 'sortie_caisse'; protected $primaryKey = 'id_sortie'; // Primary key column - 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' - ]; + // Dans le fichier App/Models/SortieCaisse.php +// Mettre à jour le tableau $allowedFields + +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'); - if ($users['group_name'] === 'Direction') { - return $this + { + try { + $session = session(); + $users = $session->get('user'); + if ($users['group_name'] === 'Direction') { + return $this + ->select('*') + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } + + if ($users['group_name'] === 'Conseil') { + return $this + ->select('*') + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } + + if($users["group_name"]==="Caissière"){ + return $this ->select('*') - ->orderBy('date_retrait', 'DESC') + ->where('user_id', $users['id']) + ->orderBy('date_retrait', 'DESC') ->findAll(); - } - - if ($users['group_name'] === 'Conseil') { + } return $this ->select('*') - ->orderBy('date_retrait', 'DESC') + ->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 []; } - - 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 []; } -} -public function getAllSortieCaisse1() -{ - try { - $session = session(); - $users = $session->get('user'); - if ($users['group_name'] === 'Direction') { - return $this + public function getAllSortieCaisse1() + { + try { + $session = session(); + $users = $session->get('user'); + 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) + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } + + 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(); + } + + if($users["group_name"]==="Caissière"){ + return $this ->select('*') - ->join('user_group', 'user_group.user_id = sortie_caisse.user_id') - ->where('user_group.group_id', 7) - ->orderBy('date_retrait', 'DESC') + ->where('user_id', $users['id']) + ->orderBy('date_retrait', 'DESC') ->findAll(); - } - - 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') + ->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 []; } - - 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 []; } -} - - - public function addSortieCaisse( array $data) { + public function addSortieCaisse(array $data) { try { - return $this->insert($data); + return $this->insert($data); } catch (\Exception $e) { log_message('error', 'Erreur lors de l\'ajout de la sortie : ' . $e->getMessage()); return false; @@ -121,17 +150,14 @@ public function getAllSortieCaisse1() } public function updateSortieCaisse(int $id, array $data) { - // Vérifier si l'ID est valide (par exemple, positif et non nul) if ($id <= 0) { log_message('error', 'ID invalide pour la mise à jour de la sortie caisse : ' . $id); return false; } try { - // Essayer de mettre à jour les données return $this->update($id, $data); } catch (\Exception $e) { - // En cas d'exception, loguer l'erreur log_message('error', 'Erreur lors de la mise à jour du recouvrement : ' . $e->getMessage()); return false; } @@ -142,35 +168,55 @@ public function getAllSortieCaisse1() $reparation = $this->select('*') ->where('id_sortie', $id) ->first(); - // return $this->where('user_id', $id)->findAll(); - return $reparation; } public function getTotalSortieCaisse() { $session = session(); - $users = $session->get('user'); + $users = $session->get('user'); $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); + if ($isAdmin) { try { return $this->select(' - SUM(montant_retire) AS mr, - + 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 ') - ->where('statut', "Valider") + ->whereIn('statut', ['Valider', 'Payé']) ->get() ->getRowObject(); } catch (\Exception $e) { - log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage()); - return false; + log_message('error', 'Erreur getTotalSortieCaisse (Admin) : ' . $e->getMessage()); + return (object)[ + 'total_espece' => 0, + 'total_mvola' => 0, + 'total_virement' => 0, + 'mr' => 0 + ]; } } else { - return $this->select('SUM(montant_retire) AS mr') + 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 + ') ->where('store_id', $users['store_id']) - ->where('statut', "Valider") + ->whereIn('statut', ['Valider', 'Payé']) ->get() ->getRowObject(); + } catch (\Exception $e) { + log_message('error', 'Erreur getTotalSortieCaisse (Store) : ' . $e->getMessage()); + return (object)[ + 'total_espece' => 0, + 'total_mvola' => 0, + 'total_virement' => 0, + 'mr' => 0 + ]; + } } - } } \ No newline at end of file diff --git a/app/Views/avances/avance.php b/app/Views/avances/avance.php index 8cad673c..77f0702d 100644 --- a/app/Views/avances/avance.php +++ b/app/Views/avances/avance.php @@ -1,4 +1,6 @@ + +

@@ -96,56 +98,51 @@ @@ -65,33 +60,34 @@

+ \ No newline at end of file diff --git a/app/Views/orders/avance.php b/app/Views/orders/avance.php index dd34fb62..ae3cf068 100644 --- a/app/Views/orders/avance.php +++ b/app/Views/orders/avance.php @@ -56,7 +56,7 @@ Avance Reste à payer Date - + Action diff --git a/app/Views/orders/create.php b/app/Views/orders/create.php index f67fbfbe..26aa8793 100644 --- a/app/Views/orders/create.php +++ b/app/Views/orders/create.php @@ -414,4 +414,183 @@ $("#product_info_table tbody tr#row_" + tr_id).remove(); subAmount(); } + function formatAllNumbers() { + // Sélecteur pour tous les inputs que tu veux formater + const inputs = document.querySelectorAll('input[type="text"], input[disabled]'); + + inputs.forEach(input => { + if (input.value && !isNaN(input.value.replace(/\s/g, ''))) { + let val = input.value.replace(/\s/g, ''); + input.value = Number(val).toLocaleString('fr-FR'); // ajoute les espaces tous les 3 chiffres + } + }); +} + +// Appel après que la page et le DOM sont chargés +document.addEventListener('DOMContentLoaded', () => { + formatAllNumbers(); +}); + +console.log('%c🚀 MODE EDIT AS NEW ACTIVÉ', 'color: green; font-weight: bold; font-size: 16px;'); + +// DEBUG: Afficher les données reçues +var orderData = ; +var itemsData = ; + +console.log('📦 Order Data:', orderData); +console.log('📦 Items Data:', itemsData); +console.log('📊 Nombre de produits:', itemsData.length); + +$(document).ready(function() { + + // ============================================ + // ÉTAPE 1: PRÉ-REMPLIR LES CHAMPS CLIENT + // ============================================ + console.log('✅ Étape 1: Pré-remplissage des champs client'); + + $('#customer_name').val(''); + $('#customer_address').val(''); + $('#customer_phone').val(''); + $('#customer_cin').val(''); + + var originalDiscount = parseFloat('') || 0; + if (originalDiscount > 0) { + $('#discount').val(originalDiscount); + console.log('💰 Rabais appliqué:', originalDiscount); + } + + // ============================================ + // ÉTAPE 2: NETTOYER LE TABLEAU + // ============================================ + console.log('✅ Étape 2: Nettoyage du tableau'); + $('#product_info_table tbody').empty(); + + // ============================================ + // ÉTAPE 3: CHARGER LES PRODUITS + // ============================================ + + console.log('✅ Étape 3: Chargement de ' + itemsData.length + ' produit(s)'); + + // Fonction de formatage + function formatNumber(num) { + return parseFloat(num).toLocaleString('fr-FR', { + minimumFractionDigits: 0, + maximumFractionDigits: 2 + }); + } + + // Charger chaque produit + itemsData.forEach(function(item, index) { + var rowId = index + 1; + console.log('🔄 Chargement ligne ' + rowId + ':', item); + + // CONSTRUIRE LA LIGNE HTML + var html = ''; + + // COLONNE 1: SELECT PRODUIT + html += ''; + html += ''; + + // COLONNE 2: PRIX UNITAIRE + html += ''; + html += ''; + html += ''; + html += ''; + + // COLONNE 3: MONTANT + html += ''; + html += ''; + html += ''; + html += ''; + + // COLONNE 4: BOUTON SUPPRIMER + html += ''; + html += ''; + html += ''; + + // QTY CACHÉ (toujours 1 pour les motos) + html += ''; + + html += ''; + + // AJOUTER AU TABLEAU + $('#product_info_table tbody').append(html); + console.log(' ✓ Ligne ' + rowId + ' ajoutée au DOM'); + }); + + // ============================================ + // ÉTAPE 4: INITIALISER SELECT2 ET RECALCULER + // ============================================ + console.log('✅ Étape 4: Finalisation'); + + setTimeout(function() { + // Initialiser Select2 + if ($.fn.select2) { + $('.product').select2(); + console.log(' ✓ Select2 initialisé'); + } + + // Recalculer les montants + if (typeof subAmount === 'function') { + subAmount(); + console.log(' ✓ Montants recalculés'); + } + + // Formater les nombres + if (typeof formatAllNumbers === 'function') { + formatAllNumbers(); + console.log(' ✓ Nombres formatés'); + } + + console.log('%c🎉 CHARGEMENT TERMINÉ AVEC SUCCÈS!', 'color: green; font-weight: bold; font-size: 14px;'); + + // Vérification finale + var rowCount = $('#product_info_table tbody tr').length; + console.log('📊 Nombre de lignes dans le tableau:', rowCount); + + }, 500); + + + console.warn('⚠️ Aucun produit à charger'); + + +}); \ No newline at end of file diff --git a/app/Views/orders/createbyid.php b/app/Views/orders/createbyid.php index 715e9a1c..1ae8db63 100644 --- a/app/Views/orders/createbyid.php +++ b/app/Views/orders/createbyid.php @@ -1,3 +1,4 @@ +

@@ -26,16 +27,21 @@

getFlashdata('errors')): ?> - - + +

Ajouter une commande

@@ -110,8 +116,8 @@ Prix unitaire Montant - + @@ -131,6 +137,7 @@ autocomplete="off" value=""> + @@ -207,16 +214,10 @@ $(document).ready(function () { getTotal(1); $(".select_group").select2(); - // $("#description").wysihtml5(); $("#mainOrdersNav").addClass('active'); $("#addOrderNav").addClass('active'); - var btnCust = ''; - // Add new row in the table $("#add_row").unbind('click').bind('click', function () { var table = $("#product_info_table"); @@ -228,8 +229,6 @@ type: 'post', dataType: 'json', success: function (response) { - - // console.log(reponse.x); var html = '' + '' + '' + '' + - '' + + '' + '' + '' + ''; @@ -252,13 +251,25 @@ } $(".product").select2(); - } }); return false; }); + // Vérifier lors de la saisie du rabais + $("#discount").on('blur', function() { + checkMinimalPrice(); + }); + + // Bloquer la soumission du formulaire + $('form').on('submit', function(e) { + if (!checkMinimalPrice()) { + e.preventDefault(); + return false; + } + }); + }); // /document function getTotal(row = null) { @@ -281,9 +292,8 @@ if (product_id == "") { $("#rate_" + row_id).val(""); $("#rate_value_" + row_id).val(""); - + $("#min_price_" + row_id).val(""); $("#qty_" + row_id).val(""); - $("#amount_" + row_id).val(""); $("#amount_value_" + row_id).val(""); @@ -297,9 +307,9 @@ dataType: 'json', success: function (response) { // setting the rate value into the rate input field - $("#rate_" + row_id).val(response.prix_vente); $("#rate_value_" + row_id).val(response.prix_vente); + $("#min_price_" + row_id).val(response.prix_minimal); // ✅ Stockage du prix minimal $("#qty_" + row_id).val(1); $("#qty_value_" + row_id).val(1); @@ -310,8 +320,8 @@ $("#amount_value_" + row_id).val(total); subAmount(); - } // /success - }); // /ajax function to fetch the product data + } + }); } } @@ -328,7 +338,7 @@ count = count.substring(4); totalSubAmount = Number(totalSubAmount) + Number($("#amount_" + count).val()); - } // /for + } totalSubAmount = totalSubAmount.toFixed(2); @@ -351,8 +361,7 @@ // total amount var totalAmount = (Number(totalSubAmount)); totalAmount = totalAmount.toFixed(2); - // $("#net_amount").val(totalAmount); - // $("#totalAmountValue;").val(totalAmount); + var discount = $("#discount").val(); if (discount) { var grandTotal = Number(totalAmount) - Number(discount); @@ -362,10 +371,41 @@ } else { $("#net_amount").val(totalAmount); $("#net_amount_value").val(totalAmount); + } + } - } // /else discount + // ✅ Vérification du rabais vs prix minimal + function checkMinimalPrice() { + var discount = Number($("#discount").val()) || 0; + + // Si pas de rabais, pas de vérification nécessaire + if (discount === 0) return true; + + var tableProductLength = $("#product_info_table tbody tr").length; + var error = false; + var messages = []; + + for (var i = 0; i < tableProductLength; i++) { + var tr = $("#product_info_table tbody tr")[i]; + var rowId = $(tr).attr('id').replace('row_', ''); + var minPrice = Number($("#min_price_" + rowId).val()) || 0; + + // ✅ Le rabais devient le prix de vente + if (minPrice > 0 && discount < minPrice) { + error = true; + var productText = $("#product_" + rowId + " option:selected").text(); + messages.push("Le rabais (" + discount + ") pour « " + productText + " » est inférieur au prix minimal (" + minPrice + ")"); + } + } - } // /sub total amount + if (error) { + alert(messages.join("\n")); + $("#discount").val(''); + subAmount(); + return false; + } + return true; + } function removeRow(tr_id) { $("#product_info_table tbody tr#row_" + tr_id).remove(); @@ -378,7 +418,7 @@ const amount = document.getElementById('amount_1') const gross_amount_value = document.getElementById('gross_amount'); const gross_amount_value2 = document.getElementById('gross_amount_value'); - const remise = document.getElementById('remise'); + const remise = document.getElementById('discount'); const net_amount = document.getElementById('net_amount'); const net_amount_value = document.getElementById('net_amount_value'); const amount_value = document.getElementById('amount_value_1'); @@ -393,5 +433,6 @@ remise.addEventListener('input', function () { net_amount.value = (gross_amount_value.value - remise.value); net_amount_value.value = (gross_amount_value.value - remise.value); + subAmount(); }) \ No newline at end of file diff --git a/app/Views/orders/edit.php b/app/Views/orders/edit.php index e083fc47..7f4763d0 100644 --- a/app/Views/orders/edit.php +++ b/app/Views/orders/edit.php @@ -172,7 +172,14 @@
- + get('user'); + if($users && $users['group_name'] == 'COMMERCIALE'): + ?> + + + +
@@ -194,24 +201,34 @@
--> + get('user'); + if ($users && $users['group_name'] !== 'COMMERCIALE'): + ?> +
- - + +
+
- + - + get('user'); + if ($users && $users['group_name'] !== 'COMMERCIALE'): + ?>
- +
+ +
+
+
+
+ +
- +
+
- - + + +
+
+
+
+
- +
+
+
- $id > 0); - } elseif (is_int($rawCats) || ctype_digit((string)$rawCats)) { - // wrap l'int en tableau - $catIds = [(int) $rawCats]; - } else { - $catIds = []; - } - ?> - - - - + $id > 0); + } elseif (is_int($rawCats) || ctype_digit((string)$rawCats)) { + $catIds = [(int) $rawCats]; + } else { + $catIds = []; + } + ?> +
+
+
- - + +
- + +
- +
+
+
- +
-
+ +
- +
- + - - - - - - - - - - - - - - - - - + + + + + + + \ No newline at end of file + diff --git a/app/Views/securite/index.php b/app/Views/securite/index.php index d2c8ae46..be38eb6c 100644 --- a/app/Views/securite/index.php +++ b/app/Views/securite/index.php @@ -113,7 +113,6 @@ $(function() { ], order: [[1, 'asc']] }); -}); // ===== Edition / validation ===== function editFunc(id) { diff --git a/app/Views/sortieCaisse/index.php b/app/Views/sortieCaisse/index.php index aeb6e88e..796a2c87 100644 --- a/app/Views/sortieCaisse/index.php +++ b/app/Views/sortieCaisse/index.php @@ -1,5 +1,44 @@ - - + + +
@@ -115,11 +154,12 @@ Caissier Motif Statut + Raison Action # Montant @@ -153,584 +193,1318 @@
+ + + +
+
+
+
+

Exporter avec filtres

+
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+
- ection> + - - - -