From 6a2fd6138503e0b1007823b65bf4f7d58fed2442 Mon Sep 17 00:00:00 2001 From: Sarobidy22 Date: Fri, 31 Oct 2025 21:18:13 +0300 Subject: [PATCH] 01112025 --- app/Config/Routes.php | 8 +- app/Controllers/Dashboard.php | 27 +- app/Controllers/OrderController.php | 537 +++++++++++++++------ app/Controllers/RecouvrementController.php | 142 ++++-- app/Controllers/ReportController.php | 61 ++- app/Controllers/SortieCaisseController.php | 54 ++- app/Models/Orders.php | 48 +- app/Models/Recouvrement.php | 25 + app/Models/SortieCaisse.php | 180 ++++--- app/Views/dashboard.php | 244 ++++++---- app/Views/orders/edit.php | 192 ++++---- app/Views/recouvrement/index.php | 448 ++++++++++------- app/Views/sortieCaisse/index.php | 275 +++++++++-- 13 files changed, 1514 insertions(+), 727 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 7126fd64..cb3cd98b 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -197,12 +197,14 @@ $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->post('markAsDelivered', [OrderController::class, 'markAsDelivered']); $routes->get('lookOrder/(:num)', [OrderController::class, 'lookOrder']); $routes->get('createFromEspace/(:num)', [OrderController::class, 'createById']); $routes->get('resrevation', [ReservationController::class, 'index']); - + + }); + /** * route for the reports */ @@ -215,7 +217,9 @@ $routes->group('', ['filter' => 'auth'], function ($routes) { $routes->get('detail/fetctDataStock2/(:num)', [ReportController::class, 'fetchProductStock2']); $routes->get('detail/performance', [ReportController::class, 'performancedetail']); $routes->get('detail/fetchPerformances', [ReportController::class, 'fetchPerformances']); + $routes->get('detail/fetchCaissierPerformances', [ReportController::class, 'fetchCaissierPerformances']); }); + /** * route for the company diff --git a/app/Controllers/Dashboard.php b/app/Controllers/Dashboard.php index 8aa0d864..f499c8dd 100644 --- a/app/Controllers/Dashboard.php +++ b/app/Controllers/Dashboard.php @@ -93,14 +93,14 @@ class Dashboard extends AdminController 'total_virement_bancaire' => $total_virement_bancaire_final, // === POUR CAISSIÈRE (MÊME CALCUL QUE DIRECTION) === - 'total_caisse' => $total_final, // ← Identique à 'total' - 'total_mvola_caisse' => $total_mvola_final, // ← Identique à 'total_mvola' - 'total_espece_caisse' => $total_espece_final, // ← Identique à 'total_espece' - 'total_vb_caisse' => $total_virement_bancaire_final, // ← Identique à 'total_virement_bancaire' + 'total_caisse' => $total_final, + 'total_mvola_caisse' => $total_mvola_final, + 'total_espece_caisse' => $total_espece_final, + 'total_vb_caisse' => $total_virement_bancaire_final, // === DÉTAIL POUR LA CAISSIÈRE === - 'total_orders_only' => $total_orders, // Ventes complètes uniquement - 'total_avances' => $total_avances, // Avances uniquement + 'total_orders_only' => $total_orders, + 'total_avances' => $total_avances, // ✅ Détails des sorties 'total_sorties' => $total_sortie_global, @@ -109,10 +109,10 @@ class Dashboard extends AdminController 'total_sortie_virement' => $total_sortie_virement, // ✅ Détails des recouvrements - 'recouvrement_me' => $me, // Mvola → Espèce - 'recouvrement_be' => $be, // Banque → Espèce - 'recouvrement_bm' => $bm, // Banque → Mvola - 'recouvrement_mb' => $mb, // Mvola → Banque + 'recouvrement_me' => $me, + 'recouvrement_be' => $be, + 'recouvrement_bm' => $bm, + 'recouvrement_mb' => $mb, 'total_recouvrements' => $me + $be + $bm + $mb, // Détail avances par mode de paiement @@ -125,7 +125,7 @@ class Dashboard extends AdminController 'total_espece_orders' => $es1_orders + $es2_orders, 'total_vb_orders' => $vb1_orders + $vb2_orders, - // ✅ Montants bruts (avant recouvrements et sorties) + // ✅ Montants bruts 'total_brut' => $total_brut, 'total_mvola_brut' => $total_mvola_brut, 'total_espece_brut' => $total_espece_brut, @@ -232,9 +232,14 @@ class Dashboard extends AdminController if ($user_id['group_name'] == "Cheffe d'Agence") { $data['isChef'] = true; } + + // ✅ AJOUT POUR CAISSIER : Passer les données de performance if ($user_id['group_name'] == "Caissière") { $data['isCaissier'] = true; + // Pas besoin de données supplémentaires car fetchCaissierPerformances + // récupère déjà les données via AJAX } + if ($user_id['group_name'] == "MECANICIEN") { $data['isMecanicien'] = true; } diff --git a/app/Controllers/OrderController.php b/app/Controllers/OrderController.php index 262f2479..65fea499 100644 --- a/app/Controllers/OrderController.php +++ b/app/Controllers/OrderController.php @@ -36,6 +36,31 @@ class OrderController extends AdminController return $this->render_template('orders/index', $data); } + /** + * Génère un numéro de facture personnalisé selon le magasin + * @param int $store_id + * @return string + */ + private function generateBillNo(int $store_id): string + { + // Mapping des préfixes par magasin + $storePrefixes = [ + 1 => 'ANTS', // ANTSAKAVIRO + 2 => 'BESA', // BESARETY + 3 => 'BYPA', // BYPASS + 4 => 'TOAM', // TOAMASINA + ]; + + // Récupérer le préfixe du magasin, ou utiliser un préfixe par défaut + $prefix = $storePrefixes[$store_id] ?? 'BILPR'; + + // Générer un identifiant unique + $uniqueId = strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 6)); + + // Retourner le numéro de facture formaté + return $prefix . '-' . $uniqueId; + } + public function fetchOrdersData() { helper(['url', 'form']); @@ -335,13 +360,15 @@ class OrderController extends AdminController $session = session(); $users = $session->get('user'); $user_id = $users['id']; - $bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4)); + + // ✅ UTILISER LA NOUVELLE MÉTHODE + $bill_no = $this->generateBillNo($users['store_id']); // Récupération des produits $posts = $this->request->getPost('product[]'); $rates = $this->request->getPost('rate_value[]'); $amounts = $this->request->getPost('amount_value[]'); - $puissances = $this->request->getPost('puissance[]'); // ✅ AJOUTER CETTE LIGNE + $puissances = $this->request->getPost('puissance[]'); $discount = (float)$this->request->getPost('discount') ?? 0; $gross_amount = $this->calculGross($amounts); @@ -352,14 +379,12 @@ class OrderController extends AdminController 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, ',', ' '); @@ -374,23 +399,19 @@ class OrderController extends AdminController } } - // ✅ NOUVELLE LOGIQUE : Calculer le montant à payer et net_amount + // ✅ 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 + // ✅ Création de la commande $data = [ 'bill_no' => $bill_no, 'customer_name' => $this->request->getPost('customer_name'), @@ -408,7 +429,7 @@ class OrderController extends AdminController 'amount_value' => $amounts, 'gross_amount' => $gross_amount, 'rate_value' => $rates, - 'puissance' => $puissances, // ✅ AJOUTER CETTE LIGNE + 'puissance' => $puissances, 'store_id' => $users['store_id'], 'tranche_1' => $tranche_1, 'tranche_2' => $tranche_2, @@ -423,9 +444,8 @@ class OrderController extends AdminController $Notification = new NotificationController(); - // ✅ WORKFLOW SELON LA REMISE if ($discount > 0) { - // AVEC REMISE : Créer demande + Notifier Conseil + // Logique demande de remise... $Order_item1 = new OrderItems(); $order_item_data = $Order_item1->getOrdersItemData($order_id); $product_ids = array_column($order_item_data, 'product_id'); @@ -462,7 +482,6 @@ class OrderController extends AdminController $Remise = new Remise(); $id_remise = $Remise->addDemande($data1); - // Notification au CONSEIL $Notification->createNotification( "Nouvelle demande de remise à valider - Commande " . $bill_no, "Direction", @@ -471,7 +490,6 @@ class OrderController extends AdminController ); } else { - // SANS REMISE : Notifier directement la Caissière $Notification->createNotification( "Nouvelle commande à valider - " . $bill_no, "Caissière", @@ -480,7 +498,6 @@ class OrderController extends AdminController ); } - // Redirection selon le rôle if ($users["group_name"] != "COMMERCIALE") { $this->checkProductisNull($posts, $users['store_id']); } @@ -662,7 +679,7 @@ public function getTableProductRow() return $this->response->setJSON($product_data); } - public function update(int $id) +public function update(int $id) { $this->verifyRole('updateOrder'); @@ -714,6 +731,22 @@ public function getTableProductRow() $paid_status = $this->request->getPost('paid_status'); } + // ✅ AJOUT : TRACER LA VALIDATION PAR LE CAISSIER + $validated_by = $current_order['validated_by'] ?? null; // Garder l'ancienne valeur si existe + $validated_at = $current_order['validated_at'] ?? null; + + // Si le statut passe à "Validé" (1) et que l'utilisateur est un caissier + if ($old_paid_status != 1 && $paid_status == 1 && $role === 'Caissière') { + $validated_by = $user['id']; + $validated_at = date('Y-m-d H:i:s'); + } + + // Si le statut repasse à "En attente" ou "Refusé", effacer la validation + if (in_array($paid_status, [0, 2])) { + $validated_by = null; + $validated_at = null; + } + $discount = $this->request->getPost('discount'); $original_discount = $this->request->getPost('original_discount'); if ($discount === '' || $discount === null) { @@ -737,11 +770,14 @@ public function getTableProductRow() 'product_sold' => true, 'rate_value' => $this->request->getPost('rate_value'), 'amount_value' => $this->request->getPost('amount_value'), - 'puissance' => $this->request->getPost('puissance'), // ✅ AJOUT PUISSANCE + 'puissance' => $this->request->getPost('puissance'), '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 + 'order_payment_mode_1' => $role !== 'COMMERCIALE' ? $this->request->getPost('order_payment_mode_2') : null, + // ✅ AJOUT DES CHAMPS DE TRACABILITÉ + 'validated_by' => $validated_by, + 'validated_at' => $validated_at ]; if ($Orders->updates($id, $dataUpdate)) { @@ -760,6 +796,16 @@ public function getTableProductRow() (int)$user['store_id'], 'orders' ); + + // ✅ AJOUT : Notification pour la Direction quand un caissier valide + if ($role === 'Caissière') { + $Notification->createNotification( + "Commande validée par la caisse: {$bill_no}", + "Direction", + (int)$user['store_id'], + 'orders' + ); + } } if ((float)$discount > 0) { @@ -1604,16 +1650,14 @@ public function print5(int $id) { $this->verifyRole('viewOrder'); - if (! $id) { + if (!$id) { throw new \CodeIgniter\Exceptions\PageNotFoundException(); } - // Modèles $Orders = new Orders(); $Company = new Company(); $OrderItems = new OrderItems(); - // Récupération des données $order = $Orders->getOrdersData($id); $items = $OrderItems->getOrdersItemData($id); $company = $Company->getCompanyData(1); @@ -1628,7 +1672,6 @@ public function print5(int $id) } } - // Calculs $discount = (float) $order['discount']; $grossAmount = (float) $order['gross_amount']; $totalTTC = ($discount > 0) ? $discount : $grossAmount; @@ -1638,164 +1681,344 @@ public function print5(int $id) $paidLabel = $order['paid_status'] == 1 ? 'Payé' : 'Non payé'; - // Début du HTML $html = ' Facture '.$order['bill_no'].' -
-
-

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

-

NIF : '.esc($company['NIF']).'

-

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

-

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

-
-
- Logo -

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

-
-
+ +
'; -
-

DOIT Nom : '.esc($order['customer_name']).'

-

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

-

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

-

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

-

Antananarivo, le '.$today.'

-
'; - - // ✅ TABLEAU ADAPTÉ SELON LE TYPE - if ($isAvanceMere) { - // ======================================== - // TABLE SIMPLIFIÉE POUR AVANCE "SUR MER" - // ======================================== + // ✅ GÉNÉRER 2 FACTURES IDENTIQUES + for ($i = 0; $i < 2; $i++) { $html .= ' - - - - - - - - '; +
+
+
+

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

+

NIF : '.esc($company['NIF']).'

+

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

+

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

+
+
+ Logo +

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

+
+
- foreach ($items as $it) { - $details = $this->getOrderItemDetails($it); - - if (!$details) continue; - - $prixAffiche = ($discount > 0) ? $discount : $details['prix']; - - - // Afficher le commentaire s'il existe - if (!empty($details['commentaire'])) { - $html .= '
'.esc($details['commentaire']).''; +
+

DOIT Nom : '.esc($order['customer_name']).'

+

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

+

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

+

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

+

Antananarivo, le '.$today.'

+
'; + + // ✅ TABLEAU ADAPTÉ SELON LE TYPE + if ($isAvanceMere) { + $html .= ' +
ProduitPrix (Ar)
+ + + + + + + '; + + foreach ($items as $it) { + $details = $this->getOrderItemDetails($it); + + if (!$details) continue; + + $prixAffiche = ($discount > 0) ? $discount : $details['prix']; + + $html .= ' + + '; } - - $html .= ' - - '; - } - } else { - // ======================================== - // TABLE COMPLÈTE POUR AVANCE "SUR TERRE" OU COMMANDE NORMALE - // ======================================== - $html .= ' -
ProduitPrix (Ar)
'.esc($details['product_name']); + + if (!empty($details['commentaire'])) { + $html .= '
'.esc($details['commentaire']).''; + } + + $html .= '
'.number_format($prixAffiche, 0, '', ' ').'
'.number_format($prixAffiche, 0, '', ' ').'
- - - - - - - - - - - '; + // ✅ CORRECTION : Fermer le tableau pour avance + $html .= ' + +
MARQUEDésignationN° MoteurN° ChâssisPuissance (CC)PRIX (Ar)
'; - foreach ($items as $it) { - $details = $this->getOrderItemDetails($it); - - if (!$details) continue; - - $prixAffiche = ($discount > 0) ? $discount : $details['prix']; - - + } else { $html .= ' - - '.esc($details['marque']).' - '.esc($details['product_name']).' - '.esc($details['numero_moteur']).' - '.esc($details['numero_chassis']).' - '.esc($details['puissance']).' - '.number_format($prixAffiche, 0, '', ' ').' - '; + + + + + + + + + + + + '; + + foreach ($items as $it) { + $details = $this->getOrderItemDetails($it); + + if (!$details) continue; + + $prixAffiche = ($discount > 0) ? $discount : $details['prix']; + + $html .= ' + + + + + + + + '; + } + + // ✅ Fermer le tableau pour produit normal + $html .= ' + +
MARQUEDésignationN° MoteurN° ChâssisPuissance (CC)PRIX (Ar)
'.esc($details['marque']).''.esc($details['product_name']).''.esc($details['numero_moteur']).''.esc($details['numero_chassis']).''.esc($details['puissance']).''.number_format($prixAffiche, 0, '', ' ').'
'; } + + $html .= ' + + + + + + + + + + + + + +
Prix (HT) :'.number_format($totalHT, 0, '', ' ').' Ar
TVA (20%) :'.number_format($tva, 0, '', ' ').' Ar
Total (TTC) :'.number_format($totalTTC, 0, '', ' ').' Ar
+ +
+ Arrêté à la somme de :
+ '.$inWords.' +
+ +
+
L\'Acheteur

__________________
+
Le Vendeur

__________________
+
+
'; } $html .= ' - - - - - - - - - - - - - - - - -
Prix (HT) :'.number_format($totalHT, 0, '', ' ').' Ar
TVA (20%) :'.number_format($tva, 0, '', ' ').' Ar
Total (TTC) :'.number_format($totalTTC, 0, '', ' ').' Ar
- -
- Arrêté à la somme de :
- '.$inWords.'
-
-
L\'Acheteur

__________________
-
Le Vendeur

__________________
-
+ +
'; - -
-
-

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
+ // ✅ GÉNÉRER 2 CONDITIONS IDENTIQUES + for ($i = 0; $i < 2; $i++) { + $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
+
'; + } + + $html .= '
diff --git a/app/Controllers/RecouvrementController.php b/app/Controllers/RecouvrementController.php index 2baf6a17..30467509 100644 --- a/app/Controllers/RecouvrementController.php +++ b/app/Controllers/RecouvrementController.php @@ -219,84 +219,58 @@ class RecouvrementController extends AdminController public function createRecouvrement() { $this->verifyRole('createRecouvrement'); - + $data['page_title'] = $this->pageTitle; - - // echo "
";
-        // die(var_dump($this->request->getPost()));
-
+    
         // Load validation service
         $validation = \Config\Services::validation();
-
+    
         $validation->setRules([
             'send_mode' => 'required',
             'get_mode' => 'required',
             'recouvrement_montant' => 'required',
             'recouvrement_date' => 'required',
         ]);
-
+    
         $validationData = [
             'send_mode' => $this->request->getPost('send_mode'),
             'get_mode' => $this->request->getPost('get_mode'),
             'recouvrement_montant' => $this->request->getPost('recouvrement_montant'),
             'recouvrement_date' => $this->request->getPost('recouvrement_date'),
         ];
-
-        // Set validation rules
-
+    
         $Notification = new NotificationController();
         $Recouvrement = new Recouvrement();
-        // $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'];
         }
-
-        // $orders = new Orders();
-        // $Recouvrement = new Recouvrement();
-        // $paymentData = $orders->getPaymentModes();
-        // $totalRecouvrement = $Recouvrement->getTotalRecouvrements();
-        // $total_recouvrement = $totalRecouvrement->total_recouvrement;
-        // Initialisation des totaux avec 0 au cas où il n'y aurait pas de données
-        // $total_mvola1 = isset($paymentData->total_mvola1) ? $paymentData->total_mvola1 : 0;
-        // $total_mvola2 = isset($paymentData->total_mvola2) ? $paymentData->total_mvola2 : 0;
-
-        // $total_mvola = $total_mvola1 + $total_mvola2;
-        // $total_mvola1 = $total_mvola - $total_recouvrement;
-
-        // die(var_dump($data['recouvrement']))
-
-        // if ($data['recouvrement_montant'] <= $total_mvola1) {
-        //     if ($Recouvrement->addRecouvrement($data)) {
-        //         session()->setFlashdata('success', 'Créé avec succès');
-
-        //         $Notification->createNotification("Un nouveau recouvrement crée", "TOUS", 0, 'recouvrement/');
-        //         return redirect()->to('recouvrement/');
-        //     } else {
-        //         session()->setFlashdata('errors', 'Error occurred while creating the product');
-        //         return redirect()->to('recouvrement/');
-        //     }
-        // } else {
-        //     session()->setFlashdata('errors', 'Solde MVOLA insuffisant');
-        //     return redirect()->to('recouvrement/');
-        // }
-
+    
         if ($validation->run($validationData)) {
-            //     // Prepare data
-            $session = session();
-            $users = $session->get('user');
+            $send_mode = $this->request->getPost('send_mode');
+            $get_mode = $this->request->getPost('get_mode');
+            $amount = (float) $this->request->getPost('recouvrement_montant');
+    
+            // Vérifier si le recouvrement est possible
+            if (!$this->canMakeRecouvrement($send_mode, $get_mode, $amount)) {
+                $response['success'] = false;
+                $response['messages'] = 'Recouvrement impossible : solde insuffisant pour ce type de transaction';
+                return $this->response->setJSON($response);
+            }
+    
+            // Préparer les données
             $data = [
-                'recouvrement_montant' => $this->request->getPost('recouvrement_montant'),
+                'recouvrement_montant' => $amount,
                 'recouvrement_date' => $this->request->getPost('recouvrement_date'),
                 'recouvrement_personnel' => $fullname,
-                'get_money' => $this->request->getPost('get_mode'),
-                'send_money' => $this->request->getPost('send_mode'),
+                'get_money' => $get_mode,
+                'send_money' => $send_mode,
                 'user_id' => $users['id'],
                 'store_id' => $users['store_id'],
-    
             ];
-
+    
             if ($Recouvrement->addRecouvrement($data)) {
                 $Notification->createNotification("Un nouveau recouvrement a été crée", "Direction", (int)$users["store_id"], 'recouvrement');
                 $response['success'] = true;
@@ -310,12 +284,11 @@ class RecouvrementController extends AdminController
             $response['success'] = false;
             $response['messages'] = $validation->getErrors();
         }
-
+    
         return $this->response->setJSON($response);
     }
 
 
-
     public function updateRecouvrement($recouvrement_id)
     {
         $this->verifyRole('updateRecouvrement');
@@ -367,6 +340,73 @@ class RecouvrementController extends AdminController
             echo json_encode($data);
         }
     }
+    private function canMakeRecouvrement($send_mode, $get_mode, $amount): bool
+{
+    $orders = new Orders();
+    $recouvrement = new Recouvrement();
+    $sortieCaisse = new SortieCaisse();
+    $avance = new Avance();
+
+    // Récupérer les données actuelles
+    $paymentDataOrders = $orders->getPaymentModes();
+    $paymentDataAvance = $avance->getPaymentModesAvance();
+    $totalRecouvrement = $recouvrement->getTotalRecouvrements();
+    $total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse();
+
+    // === 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 ===
+    $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;
+
+    // === TOTAUX PAIEMENTS AVANCES ===
+    $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;
+
+    // === 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;
+
+    // === CALCUL DES SOLDES ACTUELS ===
+    $solde_mvola = ($mv1_orders + $mv2_orders + $mv_avances) - $me - $mb + $bm - $total_sortie_mvola;
+    $solde_espece = ($es1_orders + $es2_orders + $es_avances) + $me + $be - $total_sortie_espece;
+    $solde_banque = ($vb1_orders + $vb2_orders + $vb_avances) - $be - $bm + $mb - $total_sortie_virement;
+
+    // === VÉRIFICATION EN FONCTION DU TYPE DE RECOUVREMENT ===
+    switch ($send_mode) {
+        case 'MVOLA':
+            if ($get_mode === 'En espèce') {
+                // MVOLA → Espèce : vérifier solde MVOLA
+                return $solde_mvola >= $amount;
+            } elseif ($get_mode === 'Virement Bancaire') {
+                // MVOLA → Banque : vérifier solde MVOLA
+                return $solde_mvola >= $amount;
+            }
+            break;
+
+        case 'Virement Bancaire':
+            if ($get_mode === 'MVOLA') {
+                // Banque → MVOLA : vérifier solde banque
+                return $solde_banque >= $amount;
+            } elseif ($get_mode === 'En espèce') {
+                // Banque → Espèce : vérifier solde banque
+                return $solde_banque >= $amount;
+            }
+            break;
+    }
+
+    return false;
+}
 
     public function fetchTotalRecouvrementData() {
         helper(['url', 'form']);
diff --git a/app/Controllers/ReportController.php b/app/Controllers/ReportController.php
index 22836b87..f8540bae 100644
--- a/app/Controllers/ReportController.php
+++ b/app/Controllers/ReportController.php
@@ -305,7 +305,26 @@ class ReportController extends AdminController
             }
             return $this->response->setJSON($result);
         }
-        
+        if ($users['group_name'] === "Caissière") {
+            $orderPaid = $Orders->getPerformanceByCaissier($users['id']);
+            
+            foreach ($orderPaid as $key => $value) {
+                // Calculer le prix de vente réel
+                $prix_vente_reel = (!empty($value['discount']) && $value['discount'] > 0)
+                    ? $value['discount']
+                    : $value['prix_vente'];
+                
+                // Colonnes à afficher : Caissier, Moto vendue, Date de vente, Prix de vente
+                $result['data'][$key] = [
+                    $value['caissier_name'] ?? 'N/A',  // Nom du caissier
+                    ($value['sku'] == "" ? $value['motoname'] : $value['sku']),  // Moto
+                    (new DateTime($value['datevente']))->format('d/m/Y'),  // Date
+                    number_format($prix_vente_reel, 0, '.', ' ')  // Prix
+                ];
+            }
+            
+            return $this->response->setJSON($result);
+        }
         // Pour COMMERCIALE : uniquement ses propres ventes
         if ($users['group_name'] === "COMMERCIALE") {
             $orderPaid = $Orders->getPerformanceByOrders2();
@@ -334,4 +353,42 @@ class ReportController extends AdminController
         $result = ['data'=> []];
         
     }
-}
+
+    public function fetchCaissierPerformances()
+    {
+        $result = ['data' => []];
+        $Orders = new Orders();
+        $session = session();
+        $users = $session->get('user');
+        
+        if ($users['group_name'] !== "Caissière") {
+            return $this->response->setJSON($result);
+        }
+        
+        $startDate = $this->request->getGet('startDate');
+        $endDate = $this->request->getGet('endDate');
+        
+        // ✅ SI PAS DE DATES, PRENDRE AUJOURD'HUI PAR DÉFAUT
+        if (empty($startDate) && empty($endDate)) {
+            $startDate = date('Y-m-d');
+            $endDate = date('Y-m-d');
+        }
+        
+        $orderPaid = $Orders->getPerformanceByCaissier($users['id'], $startDate, $endDate);
+        
+        foreach ($orderPaid as $key => $value) {
+            $prix_vente_reel = (!empty($value['discount']) && $value['discount'] > 0)
+                ? $value['discount']
+                : $value['prix_vente'];
+            
+            $result['data'][$key] = [
+                $value['caissier_name'] ?? 'N/A',
+                ($value['sku'] == "" ? $value['motoname'] : $value['sku']),
+                (new DateTime($value['datevente']))->format('d/m/Y H:i'),
+                number_format($prix_vente_reel, 0, '.', ' ') . ' Ar'
+            ];
+        }
+        
+        return $this->response->setJSON($result);
+    }
+}
\ No newline at end of file
diff --git a/app/Controllers/SortieCaisseController.php b/app/Controllers/SortieCaisseController.php
index 61ba799d..218979bf 100644
--- a/app/Controllers/SortieCaisseController.php
+++ b/app/Controllers/SortieCaisseController.php
@@ -371,28 +371,29 @@ class SortieCaisseController extends AdminController
             $result = $SortieCaisse->updateSortieCaisse($id_sortie, $data);
             
             if ($result) {
-                // Créer une notification pour DAF et Direction
+                // Créer une notification pour DAF et Direction DU MÊME STORE
                 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']; + "Caissière: " . $users['firstname'] . ' ' . $users['lastname'] . "
" . + "Store: " . $this->returnStoreName($users['store_id']); - // Notifier la Direction + // ✅ Notifier la Direction DU MÊME STORE $Notification->createNotification( $message, "Direction", - (int)$users['store_id'], + (int)$users['store_id'], // ✅ Store ID de la caissière 'sortieCaisse' ); - // Notifier le DAF + // ✅ Notifier le DAF DU MÊME STORE $Notification->createNotification( $message, "DAF", - (int)$users['store_id'], + (int)$users['store_id'], // ✅ Store ID de la caissière 'sortieCaisse' ); } @@ -400,9 +401,10 @@ class SortieCaisseController extends AdminController return $this->response->setJSON([ 'success' => true, 'messages' => '✅ Décaissement marqué comme PAYÉ
' . - 'Direction et DAF ont été notifiés.
' . + 'Direction et DAF de ' . $this->returnStoreName($users['store_id']) . ' ont été notifiés.
' . 'Montant: ' . number_format($decaissement['montant_retire'], 0, ',', ' ') . ' Ar' ]); + } else { return $this->response->setJSON([ 'success' => false, @@ -719,7 +721,7 @@ class SortieCaisseController extends AdminController } // Champs supplémentaires pour montant > 1,000,000 - if ($montant_retire > 1000000) { + 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') ?? ''; @@ -765,13 +767,23 @@ class SortieCaisseController extends AdminController $result = $model->addSortieCaisse($data); if ($result) { - // Notification + // Notification UNIQUEMENT pour la Direction du même store if (class_exists('App\Controllers\NotificationController')) { $Notification = new NotificationController(); + + // ✅ Notifier UNIQUEMENT la Direction du store concerné $Notification->createNotification( "Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")", "Direction", - (int)$user['store_id'], + (int)$user['store_id'], // ✅ Store ID du créateur + 'sortieCaisse' + ); + + // ✅ Notifier aussi le DAF du même store (si vous avez des DAF par store) + $Notification->createNotification( + "Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")", + "DAF", + (int)$user['store_id'], // ✅ Store ID du créateur 'sortieCaisse' ); } @@ -780,8 +792,10 @@ class SortieCaisseController extends AdminController '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' + 'Nouveau solde ' . $mode_paiement_label . ': ' . number_format($fonds_disponible - $montant_retire, 0, ',', ' ') . ' Ar
' . + 'Notification envoyée à la Direction de ' . $this->returnStoreName($user['store_id']) . '' ]); + } else { return $this->response->setJSON([ 'success' => false, @@ -1076,24 +1090,28 @@ class SortieCaisseController extends AdminController $statut = $this->request->getPost('statut'); $message = ''; + // ✅ Récupérer le décaissement pour avoir son store_id + $decaissement = $SortieCaisse->getSortieCaisseSingle($id_sortie); + $store_id = $decaissement['store_id']; + switch ($statut) { case "Valider": - $message = "Décaissement validé avec succès"; - $Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse'); + $message = "✅ Votre décaissement a été validé par la Direction de " . $this->returnStoreName($store_id); + $Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse'); break; case "Refuser": - $message = "Un décaissement a été refusé"; - $Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse'); + $message = "❌ Votre décaissement a été refusé par la Direction de " . $this->returnStoreName($store_id) . "
Raison: " . $this->request->getPost('admin_raison'); + $Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse'); break; case "En attente": - $message = "Décaissement mis en attente"; - $Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse'); + $message = "⏳ Votre décaissement a été mis en attente par la Direction de " . $this->returnStoreName($store_id); + $Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse'); break; } return $this->response->setJSON([ 'success' => true, - 'messages' => 'Décaissement modifié avec succès !' + 'messages' => 'Décaissement modifié avec succès ! Notification envoyée à la caissière de ' . $this->returnStoreName($store_id) ]); } else { return $this->response->setJSON([ diff --git a/app/Models/Orders.php b/app/Models/Orders.php index 23689ba2..7bc3379f 100644 --- a/app/Models/Orders.php +++ b/app/Models/Orders.php @@ -16,26 +16,28 @@ class Orders extends Model protected $table = 'orders'; protected $allowedFields = [ 'bill_no', - 'customer_name', + 'customer_name', 'customer_address', 'customer_phone', 'customer_cin', 'date_time', 'gross_amount', 'service_charge_rate', - 'vat_charge_rate', + 'vat_charge_rate', 'vat_charge', 'net_amount', 'discount', 'paid_status', 'user_id', + 'validated_by', + 'validated_at', 'store_id', 'tranche_1', 'tranche_2', 'order_payment_mode', 'order_payment_mode_1', 'delivered_at', - 'delivered_by' + 'delivered_by' ]; @@ -825,4 +827,44 @@ public function getUserPerformanceByMonth(string $month) return $results; } +public function getPerformanceByCaissier(int $caissierId, $startDate = null, $endDate = null) +{ + $builder = $this->db->table('orders') + ->select(' + orders.id as orderid, + orders.net_amount, + orders.date_time as datevente, + orders.discount, + orders.validated_at, + products.price, + products.sku, + products.prix_vente, + products.name as motoname, + stores.id as store_id, + stores.name as store_name, + CONCAT(validator.firstname, " ", validator.lastname) as caissier_name + ') + ->join('stores', 'orders.store_id = stores.id', 'left') + ->join('orders_item', 'orders.id = orders_item.order_id', 'left') + ->join('products', 'products.id = orders_item.product_id', 'left') + ->join('users as validator', 'validator.id = orders.validated_by', 'left') + ->whereIn('orders.paid_status', [1, 3]) + ->where('orders.validated_by', $caissierId) + ->where('orders.validated_by IS NOT NULL', null, false); + + // ✅ AJOUT : Filtrage par dates + if ($startDate && $endDate) { + $builder->where('DATE(orders.validated_at) >=', $startDate) + ->where('DATE(orders.validated_at) <=', $endDate); + } elseif ($startDate) { + $builder->where('DATE(orders.validated_at) >=', $startDate); + } elseif ($endDate) { + $builder->where('DATE(orders.validated_at) <=', $endDate); + } + + return $builder->orderBy('orders.validated_at', 'DESC') + ->get() + ->getResultArray(); } + +} \ No newline at end of file diff --git a/app/Models/Recouvrement.php b/app/Models/Recouvrement.php index beab52b1..b4a80d9c 100644 --- a/app/Models/Recouvrement.php +++ b/app/Models/Recouvrement.php @@ -186,4 +186,29 @@ class Recouvrement extends Model{ return $reparation; } + public function getTotalByPaymentMode($start_date = null, $end_date = null) +{ + $session = session(); + $users = $session->get('user'); + $isAdmin = in_array($users['group_name'], ['DAF', 'Direction']); + + $builder = $this->db->table('recouvrement'); + + // Conditions de base selon le rôle + if (!$isAdmin) { + $builder->where('store_id', $users['store_id']); + } + + // Filtre par date si fourni + if ($start_date && $end_date) { + $builder->where("recouvrement_date BETWEEN '$start_date' AND '$end_date'"); + } + + return $builder->select(' + SUM(CASE WHEN send_money = "MVOLA" AND get_money = "En espèce" THEN recouvrement_montant ELSE 0 END) AS me, + SUM(CASE WHEN send_money = "Virement Bancaire" AND get_money = "MVOLA" THEN recouvrement_montant ELSE 0 END) AS bm, + SUM(CASE WHEN send_money = "Virement Bancaire" AND get_money = "En espèce" THEN recouvrement_montant ELSE 0 END) AS be, + SUM(CASE WHEN send_money = "MVOLA" AND get_money = "Virement Bancaire" THEN recouvrement_montant ELSE 0 END) AS mb + ')->get()->getRowObject(); +} } \ No newline at end of file diff --git a/app/Models/SortieCaisse.php b/app/Models/SortieCaisse.php index 78e1f6e9..0a6006b3 100644 --- a/app/Models/SortieCaisse.php +++ b/app/Models/SortieCaisse.php @@ -62,84 +62,116 @@ protected $allowedFields = [ 'date_visa_conseil', 'observations' ]; - public function getAllSortieCaisse() - { - 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 +public function getAllSortieCaisse() +{ + try { + $session = session(); + $users = $session->get('user'); + + // ✅ DIRECTION : Voir uniquement les décaissements de SON store + if ($users['group_name'] === 'Direction') { + return $this ->select('*') - ->where('user_id', $users['id']) - ->orderBy('date_retrait', 'DESC') + ->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE + ->orderBy('date_retrait', 'DESC') ->findAll(); - } + } + + // ✅ DAF : Voir uniquement les décaissements de SON store + if ($users['group_name'] === 'DAF') { + return $this + ->select('*') + ->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } + + // ✅ CONSEIL : Voir TOUS les décaissements (multi-stores) + // if ($users['group_name'] === 'Conseil') { + // return $this + // ->select('*') + // ->orderBy('date_retrait', 'DESC') + // ->findAll(); + // } + + // ✅ CAISSIÈRE : Voir uniquement SES décaissements + if($users["group_name"]==="Caissière"){ return $this ->select('*') ->where('user_id', $users['id']) ->orderBy('date_retrait', 'DESC') ->findAll(); - } catch (\Exception $e) { - log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage()); - return []; } + + // ✅ AUTRES : Par défaut, voir uniquement leurs décaissements + return $this + ->select('*') + ->where('user_id', $users['id']) + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } catch (\Exception $e) { + log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage()); + return []; } +} - public function getAllSortieCaisse1() - { - try { - $session = session(); - $users = $session->get('user'); - 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 +public function getAllSortieCaisse1() +{ + try { + $session = session(); + $users = $session->get('user'); + + // ✅ DIRECTION : Voir uniquement les décaissements de SON store + if ($users['group_name'] === 'Direction') { + return $this ->select('*') - ->where('user_id', $users['id']) - ->orderBy('date_retrait', 'DESC') + ->join('user_group', 'user_group.user_id = sortie_caisse.user_id') + ->where('user_group.group_id', 7) + ->where('sortie_caisse.store_id', $users['store_id']) // ✅ FILTRE PAR STORE + ->orderBy('date_retrait', 'DESC') ->findAll(); - } + } + + // ✅ DAF : Voir uniquement les décaissements de SON store + if ($users['group_name'] === 'DAF') { + return $this + ->select('*') + ->join('user_group', 'user_group.user_id = sortie_caisse.user_id') + ->where('user_group.group_id', 7) + ->where('sortie_caisse.store_id', $users['store_id']) // ✅ FILTRE PAR STORE + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } + + // ✅ CONSEIL : Voir TOUS les stores + // if ($users['group_name'] === 'Conseil') { + // return $this + // ->select('*') + // ->join('user_group', 'user_group.user_id = sortie_caisse.user_id') + // ->where('user_group.group_id', 6) + // ->orderBy('date_retrait', 'DESC') + // ->findAll(); + // } + + // ✅ CAISSIÈRE : Voir uniquement SES décaissements + if($users["group_name"]==="Caissière"){ return $this ->select('*') ->where('user_id', $users['id']) ->orderBy('date_retrait', 'DESC') ->findAll(); - } catch (\Exception $e) { - log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage()); - return []; } + + return $this + ->select('*') + ->where('user_id', $users['id']) + ->orderBy('date_retrait', 'DESC') + ->findAll(); + } catch (\Exception $e) { + log_message('error', 'Erreur lors de la récupération des sorties caisse : ' . $e->getMessage()); + return []; } - +} public function addSortieCaisse(array $data) { try { return $this->insert($data); @@ -174,7 +206,12 @@ protected $allowedFields = [ public function getTotalSortieCaisse() { $session = session(); $users = $session->get('user'); - $isAdmin = in_array($users['group_name'], ['Conseil', 'Direction']); + + // ✅ DIRECTION et DAF : Voir uniquement leur store + $isAdmin = in_array($users['group_name'], ['Direction', 'DAF']); + + // ✅ CONSEIL : Voir TOUS les stores + $isConseil = $users['group_name'] === 'Conseil'; if ($isAdmin) { try { @@ -184,6 +221,7 @@ protected $allowedFields = [ SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant_retire ELSE 0 END) AS total_virement, SUM(montant_retire) AS mr ') + ->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE ->whereIn('statut', ['Valider', 'Payé']) ->get() ->getRowObject(); @@ -196,7 +234,29 @@ protected $allowedFields = [ 'mr' => 0 ]; } + } elseif ($isConseil) { + // ✅ CONSEIL voit TOUS les stores + try { + return $this->select(' + SUM(CASE WHEN mode_paiement = "En espèce" THEN montant_retire ELSE 0 END) AS total_espece, + SUM(CASE WHEN mode_paiement = "MVOLA" THEN montant_retire ELSE 0 END) AS total_mvola, + SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant_retire ELSE 0 END) AS total_virement, + SUM(montant_retire) AS mr + ') + ->whereIn('statut', ['Valider', 'Payé']) + ->get() + ->getRowObject(); + } catch (\Exception $e) { + log_message('error', 'Erreur getTotalSortieCaisse (Conseil) : ' . $e->getMessage()); + return (object)[ + 'total_espece' => 0, + 'total_mvola' => 0, + 'total_virement' => 0, + 'mr' => 0 + ]; + } } else { + // ✅ CAISSIÈRE : Uniquement son store try { return $this->select(' SUM(CASE WHEN mode_paiement = "En espèce" THEN montant_retire ELSE 0 END) AS total_espece, diff --git a/app/Views/dashboard.php b/app/Views/dashboard.php index a505f5c0..0ffc7d78 100644 --- a/app/Views/dashboard.php +++ b/app/Views/dashboard.php @@ -712,14 +712,15 @@ - + + +
-
-

Ar

+

Ar

Totale CAISSE

@@ -728,11 +729,10 @@
-
-

Ar

+

Ar

Totale MVOLA

@@ -741,11 +741,10 @@
-
-

Ar

+

Ar

Totale en espèce

@@ -754,11 +753,10 @@
-
-

Ar

+

Ar

Totale en banque

@@ -768,7 +766,7 @@
- +
@@ -791,7 +789,7 @@
- +
@@ -824,11 +822,12 @@
+
-

Rapport de Performance du Caissier

+

📊 Mes Performances de Vente

@@ -841,33 +840,53 @@
+
+

+ Liste de mes ventes validées +

+
+
- - + +
- - + +

- - + +
- - - - - - - - - - -
MVOLA & EspèceBanque & MVOLABanque & EspèceMVOLA & BanqueMontant total
+ + + + + + + + + + + + + + + + + + + + +
CaissierMoto vendueDate de ventePrix de vente
Total :
@@ -878,88 +897,103 @@
- -
- -
- - + - - +})(); + + @@ -1151,14 +1185,14 @@
-
- - -
-
- - -
+
+ + +
+
+ + +

' + - ''; - - if (count_table_tbody_tr >= 1) { - $("#product_info_table tbody tr:last").after(html); - } else { - $("#product_info_table tbody").html(html); + var html = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + if (count_table_tbody_tr >= 1) { + $("#product_info_table tbody tr:last").after(html); + } else { + $("#product_info_table tbody").html(html); + } + + $(".product").select2(); } - }); + }); - return false; -}); + return false; + }); + }); function getTotal(row = null) { if (row) { @@ -419,52 +442,33 @@ function getProductData(row_id) { var product_id = $("#product_" + row_id).val(); - if (product_id == "") { - $("#rate_" + row_id).val(""); - $("#rate_value_" + row_id).val(""); - $("#min_price_" + row_id).val(""); - $("#puissance_" + row_id).val("1"); // ✅ Réinitialiser à 1 - $("#amount_" + row_id).val(""); - $("#amount_value_" + row_id).val(""); + $("#rate_" + row_id).val(""); + $("#rate_value_" + row_id).val(""); + $("#puissance_" + row_id).val(""); + $("#amount_" + row_id).val(""); + $("#amount_value_" + row_id).val(""); } else { - $.ajax({ - url: base_url + 'orders/getProductValueById', - type: 'post', - data: { product_id: product_id }, - dataType: 'json', - success: function(response) { - console.log('✅ Response:', response); // Debug - - var prixVente = parseFloat(response.prix_vente) || 0; - var prixMinimal = parseFloat(response.prix_minimal) || 0; - - if (prixVente < 0) prixVente = 0; - if (prixMinimal < 0) prixMinimal = 0; - - $("#rate_" + row_id).val(prixVente); - $("#rate_value_" + row_id).val(prixVente); - $("#min_price_" + row_id).val(prixMinimal); - - // ✅ CORRECTION : Remplir la puissance - var puissanceValue = response.puissance || '1'; - console.log('✅ Puissance extraite:', puissanceValue); // Debug - $("#puissance_" + row_id).val(puissanceValue); - - // ✅ Calculer le montant (prix * puissance) - var total = prixVente * 1; // Pour l'instant on garde qty=1 - total = total.toFixed(2); - $("#amount_" + row_id).val(total); - $("#amount_value_" + row_id).val(total); - - subAmount(); - }, - error: function(xhr, status, error) { - console.error('❌ Erreur AJAX:', error); - } - }); + $.ajax({ + url: base_url + 'orders/getProductValueById', + type: 'post', + data: { product_id: product_id }, + dataType: 'json', + success: function(response) { + $("#rate_" + row_id).val(response.prix_vente); + $("#rate_value_" + row_id).val(response.prix_vente); + + $("#puissance_" + row_id).val(response.puissance || ''); + + var total = Number(response.prix_vente); + total = total.toFixed(2); + $("#amount_" + row_id).val(total); + $("#amount_value_" + row_id).val(total); + subAmount(); + } + }); } -} + } function subAmount() { var service_charge = 0) ? $company_data['service_charge_value'] : 0; ?>; @@ -515,6 +519,7 @@ $("#remaining_value").val(remaning.toFixed(2)); } + // ✅ CORRECTION : Mettre à jour les tranches après chaque calcul updateMontantTranches(); } @@ -533,12 +538,14 @@ subAmount(); } + // ✅ CORRECTION : Fonction pour obtenir le montant pour les tranches function getMontantPourTranches() { var discount = parseFloat($("#discount").val()) || 0; var grossAmount = parseFloat($("#gross_amount_value").val()) || 0; return discount > 0 ? discount : grossAmount; } + // ✅ CORRECTION : Mettre à jour l'affichage du montant de référence function updateMontantTranches() { var montant = getMontantPourTranches(); var discount = parseFloat($("#discount").val()) || 0; @@ -552,11 +559,17 @@ $("#montant_source_label").text("(Montant brut)"); } - if ($("#payment_amount_1").val()) { + // ✅ CORRECTION : Mettre à jour tranche 1 si en mode 1 tranche + var paymentMode = $("#payment_mode").val(); + if (parseInt(paymentMode) === 1) { + $("#payment_amount_1").val(montant.toFixed(2)); + } else { + // ✅ En mode 2 tranches, recalculer tranche 2 calculerTranche2(); } } + // ✅ CORRECTION : Calculer tranche 2 basé sur le montant total actuel function calculerTranche2() { var montantTotal = getMontantPourTranches(); var tranche1 = parseFloat($("#payment_amount_1").val()) || 0; @@ -565,12 +578,17 @@ $("#payment_amount_2").val(tranche2.toFixed(2)); } + // ✅ CORRECTION : Écouter les changements de discount $("#discount").on('keyup', function() { - updateMontantTranches(); + subAmount(); }); + // ✅ Initialisation au chargement const net_amount_value = document.getElementById('net_amount_value'); const net_amount = document.getElementById('net_amount'); const payment_amount_1 = document.getElementById('payment_amount_1'); - payment_amount_1.value = net_amount.value; + + if (payment_amount_1 && net_amount) { + payment_amount_1.value = net_amount.value; + } \ No newline at end of file diff --git a/app/Views/recouvrement/index.php b/app/Views/recouvrement/index.php index bc60a057..5022d401 100644 --- a/app/Views/recouvrement/index.php +++ b/app/Views/recouvrement/index.php @@ -106,7 +106,7 @@

-

Total en Caisse

+

Total en Espèce

@@ -232,78 +232,171 @@ - + + - diff --git a/app/Views/sortieCaisse/index.php b/app/Views/sortieCaisse/index.php index 796a2c87..5a4e1c3a 100644 --- a/app/Views/sortieCaisse/index.php +++ b/app/Views/sortieCaisse/index.php @@ -288,22 +288,44 @@ input[readonly], select[disabled], textarea[readonly] {
-
- - -
+
+ + + + + + + + + + + + + Sélectionnez un motif prédéfini ou choisissez "Autre" pour saisir librement + +
- get('user'); - $isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil"; - $options = $isAdmin ? $admin_options : $caissier_options; - foreach ($options as $option) { - echo "\n"; - } - ?> - - - - -
-
+
+ + + + + + + + + + Le motif ne peut pas être modifié + +
+ +