01112025
This commit is contained in:
parent
25d8f834f5
commit
6a2fd61385
@ -197,12 +197,14 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
|
|||||||
$routes->get('printDivBL/(:num)', [OrderController::class, 'print7']);
|
$routes->get('printDivBL/(:num)', [OrderController::class, 'print7']);
|
||||||
$routes->get('printDivBLF/(:num)', [OrderController::class, 'print31']);
|
$routes->get('printDivBLF/(:num)', [OrderController::class, 'print31']);
|
||||||
$routes->post('remove', [OrderController::class, 'remove']);
|
$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('lookOrder/(:num)', [OrderController::class, 'lookOrder']);
|
||||||
$routes->get('createFromEspace/(:num)', [OrderController::class, 'createById']);
|
$routes->get('createFromEspace/(:num)', [OrderController::class, 'createById']);
|
||||||
$routes->get('resrevation', [ReservationController::class, 'index']);
|
$routes->get('resrevation', [ReservationController::class, 'index']);
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route for the reports
|
* route for the reports
|
||||||
*/
|
*/
|
||||||
@ -215,8 +217,10 @@ $routes->group('', ['filter' => 'auth'], function ($routes) {
|
|||||||
$routes->get('detail/fetctDataStock2/(:num)', [ReportController::class, 'fetchProductStock2']);
|
$routes->get('detail/fetctDataStock2/(:num)', [ReportController::class, 'fetchProductStock2']);
|
||||||
$routes->get('detail/performance', [ReportController::class, 'performancedetail']);
|
$routes->get('detail/performance', [ReportController::class, 'performancedetail']);
|
||||||
$routes->get('detail/fetchPerformances', [ReportController::class, 'fetchPerformances']);
|
$routes->get('detail/fetchPerformances', [ReportController::class, 'fetchPerformances']);
|
||||||
|
$routes->get('detail/fetchCaissierPerformances', [ReportController::class, 'fetchCaissierPerformances']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route for the company
|
* route for the company
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -93,14 +93,14 @@ class Dashboard extends AdminController
|
|||||||
'total_virement_bancaire' => $total_virement_bancaire_final,
|
'total_virement_bancaire' => $total_virement_bancaire_final,
|
||||||
|
|
||||||
// === POUR CAISSIÈRE (MÊME CALCUL QUE DIRECTION) ===
|
// === POUR CAISSIÈRE (MÊME CALCUL QUE DIRECTION) ===
|
||||||
'total_caisse' => $total_final, // ← Identique à 'total'
|
'total_caisse' => $total_final,
|
||||||
'total_mvola_caisse' => $total_mvola_final, // ← Identique à 'total_mvola'
|
'total_mvola_caisse' => $total_mvola_final,
|
||||||
'total_espece_caisse' => $total_espece_final, // ← Identique à 'total_espece'
|
'total_espece_caisse' => $total_espece_final,
|
||||||
'total_vb_caisse' => $total_virement_bancaire_final, // ← Identique à 'total_virement_bancaire'
|
'total_vb_caisse' => $total_virement_bancaire_final,
|
||||||
|
|
||||||
// === DÉTAIL POUR LA CAISSIÈRE ===
|
// === DÉTAIL POUR LA CAISSIÈRE ===
|
||||||
'total_orders_only' => $total_orders, // Ventes complètes uniquement
|
'total_orders_only' => $total_orders,
|
||||||
'total_avances' => $total_avances, // Avances uniquement
|
'total_avances' => $total_avances,
|
||||||
|
|
||||||
// ✅ Détails des sorties
|
// ✅ Détails des sorties
|
||||||
'total_sorties' => $total_sortie_global,
|
'total_sorties' => $total_sortie_global,
|
||||||
@ -109,10 +109,10 @@ class Dashboard extends AdminController
|
|||||||
'total_sortie_virement' => $total_sortie_virement,
|
'total_sortie_virement' => $total_sortie_virement,
|
||||||
|
|
||||||
// ✅ Détails des recouvrements
|
// ✅ Détails des recouvrements
|
||||||
'recouvrement_me' => $me, // Mvola → Espèce
|
'recouvrement_me' => $me,
|
||||||
'recouvrement_be' => $be, // Banque → Espèce
|
'recouvrement_be' => $be,
|
||||||
'recouvrement_bm' => $bm, // Banque → Mvola
|
'recouvrement_bm' => $bm,
|
||||||
'recouvrement_mb' => $mb, // Mvola → Banque
|
'recouvrement_mb' => $mb,
|
||||||
'total_recouvrements' => $me + $be + $bm + $mb,
|
'total_recouvrements' => $me + $be + $bm + $mb,
|
||||||
|
|
||||||
// Détail avances par mode de paiement
|
// Détail avances par mode de paiement
|
||||||
@ -125,7 +125,7 @@ class Dashboard extends AdminController
|
|||||||
'total_espece_orders' => $es1_orders + $es2_orders,
|
'total_espece_orders' => $es1_orders + $es2_orders,
|
||||||
'total_vb_orders' => $vb1_orders + $vb2_orders,
|
'total_vb_orders' => $vb1_orders + $vb2_orders,
|
||||||
|
|
||||||
// ✅ Montants bruts (avant recouvrements et sorties)
|
// ✅ Montants bruts
|
||||||
'total_brut' => $total_brut,
|
'total_brut' => $total_brut,
|
||||||
'total_mvola_brut' => $total_mvola_brut,
|
'total_mvola_brut' => $total_mvola_brut,
|
||||||
'total_espece_brut' => $total_espece_brut,
|
'total_espece_brut' => $total_espece_brut,
|
||||||
@ -232,9 +232,14 @@ class Dashboard extends AdminController
|
|||||||
if ($user_id['group_name'] == "Cheffe d'Agence") {
|
if ($user_id['group_name'] == "Cheffe d'Agence") {
|
||||||
$data['isChef'] = true;
|
$data['isChef'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ AJOUT POUR CAISSIER : Passer les données de performance
|
||||||
if ($user_id['group_name'] == "Caissière") {
|
if ($user_id['group_name'] == "Caissière") {
|
||||||
$data['isCaissier'] = true;
|
$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") {
|
if ($user_id['group_name'] == "MECANICIEN") {
|
||||||
$data['isMecanicien'] = true;
|
$data['isMecanicien'] = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,31 @@ class OrderController extends AdminController
|
|||||||
return $this->render_template('orders/index', $data);
|
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()
|
public function fetchOrdersData()
|
||||||
{
|
{
|
||||||
helper(['url', 'form']);
|
helper(['url', 'form']);
|
||||||
@ -335,13 +360,15 @@ class OrderController extends AdminController
|
|||||||
$session = session();
|
$session = session();
|
||||||
$users = $session->get('user');
|
$users = $session->get('user');
|
||||||
$user_id = $users['id'];
|
$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
|
// Récupération des produits
|
||||||
$posts = $this->request->getPost('product[]');
|
$posts = $this->request->getPost('product[]');
|
||||||
$rates = $this->request->getPost('rate_value[]');
|
$rates = $this->request->getPost('rate_value[]');
|
||||||
$amounts = $this->request->getPost('amount_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;
|
$discount = (float)$this->request->getPost('discount') ?? 0;
|
||||||
$gross_amount = $this->calculGross($amounts);
|
$gross_amount = $this->calculGross($amounts);
|
||||||
|
|
||||||
@ -352,14 +379,12 @@ class OrderController extends AdminController
|
|||||||
foreach ($posts as $index => $productId) {
|
foreach ($posts as $index => $productId) {
|
||||||
$productId = (int)$productId;
|
$productId = (int)$productId;
|
||||||
|
|
||||||
// Récupérer données produit + prix minimal
|
|
||||||
$productData = $Products->getProductData($productId);
|
$productData = $Products->getProductData($productId);
|
||||||
$fourchette = $FourchettePrix->getFourchettePrixByProductId($productId);
|
$fourchette = $FourchettePrix->getFourchettePrixByProductId($productId);
|
||||||
|
|
||||||
if ($fourchette) {
|
if ($fourchette) {
|
||||||
$prixMinimal = (float)$fourchette['prix_minimal'];
|
$prixMinimal = (float)$fourchette['prix_minimal'];
|
||||||
|
|
||||||
// ✅ Le rabais devient le prix de vente
|
|
||||||
if ($discount < $prixMinimal) {
|
if ($discount < $prixMinimal) {
|
||||||
$prixMinimalFormatted = number_format($prixMinimal, 0, ',', ' ');
|
$prixMinimalFormatted = number_format($prixMinimal, 0, ',', ' ');
|
||||||
$discountFormatted = number_format($discount, 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;
|
$montant_a_payer = ($discount > 0) ? $discount : $gross_amount;
|
||||||
|
|
||||||
// Récupérer les tranches
|
|
||||||
$tranche_1 = (float)$this->request->getPost('tranche_1') ?? 0;
|
$tranche_1 = (float)$this->request->getPost('tranche_1') ?? 0;
|
||||||
$tranche_2 = (float)$this->request->getPost('tranche_2') ?? 0;
|
$tranche_2 = (float)$this->request->getPost('tranche_2') ?? 0;
|
||||||
|
|
||||||
// Calculer net_amount selon les tranches
|
|
||||||
if ($tranche_1 > 0 && $tranche_2 > 0) {
|
if ($tranche_1 > 0 && $tranche_2 > 0) {
|
||||||
// Paiement en 2 tranches
|
|
||||||
$net_amount = $tranche_1 + $tranche_2;
|
$net_amount = $tranche_1 + $tranche_2;
|
||||||
} else {
|
} else {
|
||||||
// Paiement en 1 tranche ou pas de tranches
|
|
||||||
$net_amount = $montant_a_payer;
|
$net_amount = $montant_a_payer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Création de la commande avec la nouvelle logique
|
// ✅ Création de la commande
|
||||||
$data = [
|
$data = [
|
||||||
'bill_no' => $bill_no,
|
'bill_no' => $bill_no,
|
||||||
'customer_name' => $this->request->getPost('customer_name'),
|
'customer_name' => $this->request->getPost('customer_name'),
|
||||||
@ -408,7 +429,7 @@ class OrderController extends AdminController
|
|||||||
'amount_value' => $amounts,
|
'amount_value' => $amounts,
|
||||||
'gross_amount' => $gross_amount,
|
'gross_amount' => $gross_amount,
|
||||||
'rate_value' => $rates,
|
'rate_value' => $rates,
|
||||||
'puissance' => $puissances, // ✅ AJOUTER CETTE LIGNE
|
'puissance' => $puissances,
|
||||||
'store_id' => $users['store_id'],
|
'store_id' => $users['store_id'],
|
||||||
'tranche_1' => $tranche_1,
|
'tranche_1' => $tranche_1,
|
||||||
'tranche_2' => $tranche_2,
|
'tranche_2' => $tranche_2,
|
||||||
@ -423,9 +444,8 @@ class OrderController extends AdminController
|
|||||||
|
|
||||||
$Notification = new NotificationController();
|
$Notification = new NotificationController();
|
||||||
|
|
||||||
// ✅ WORKFLOW SELON LA REMISE
|
|
||||||
if ($discount > 0) {
|
if ($discount > 0) {
|
||||||
// AVEC REMISE : Créer demande + Notifier Conseil
|
// Logique demande de remise...
|
||||||
$Order_item1 = new OrderItems();
|
$Order_item1 = new OrderItems();
|
||||||
$order_item_data = $Order_item1->getOrdersItemData($order_id);
|
$order_item_data = $Order_item1->getOrdersItemData($order_id);
|
||||||
$product_ids = array_column($order_item_data, 'product_id');
|
$product_ids = array_column($order_item_data, 'product_id');
|
||||||
@ -462,7 +482,6 @@ class OrderController extends AdminController
|
|||||||
$Remise = new Remise();
|
$Remise = new Remise();
|
||||||
$id_remise = $Remise->addDemande($data1);
|
$id_remise = $Remise->addDemande($data1);
|
||||||
|
|
||||||
// Notification au CONSEIL
|
|
||||||
$Notification->createNotification(
|
$Notification->createNotification(
|
||||||
"Nouvelle demande de remise à valider - Commande " . $bill_no,
|
"Nouvelle demande de remise à valider - Commande " . $bill_no,
|
||||||
"Direction",
|
"Direction",
|
||||||
@ -471,7 +490,6 @@ class OrderController extends AdminController
|
|||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// SANS REMISE : Notifier directement la Caissière
|
|
||||||
$Notification->createNotification(
|
$Notification->createNotification(
|
||||||
"Nouvelle commande à valider - " . $bill_no,
|
"Nouvelle commande à valider - " . $bill_no,
|
||||||
"Caissière",
|
"Caissière",
|
||||||
@ -480,7 +498,6 @@ class OrderController extends AdminController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirection selon le rôle
|
|
||||||
if ($users["group_name"] != "COMMERCIALE") {
|
if ($users["group_name"] != "COMMERCIALE") {
|
||||||
$this->checkProductisNull($posts, $users['store_id']);
|
$this->checkProductisNull($posts, $users['store_id']);
|
||||||
}
|
}
|
||||||
@ -662,7 +679,7 @@ public function getTableProductRow()
|
|||||||
return $this->response->setJSON($product_data);
|
return $this->response->setJSON($product_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(int $id)
|
public function update(int $id)
|
||||||
{
|
{
|
||||||
$this->verifyRole('updateOrder');
|
$this->verifyRole('updateOrder');
|
||||||
|
|
||||||
@ -714,6 +731,22 @@ public function getTableProductRow()
|
|||||||
$paid_status = $this->request->getPost('paid_status');
|
$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');
|
$discount = $this->request->getPost('discount');
|
||||||
$original_discount = $this->request->getPost('original_discount');
|
$original_discount = $this->request->getPost('original_discount');
|
||||||
if ($discount === '' || $discount === null) {
|
if ($discount === '' || $discount === null) {
|
||||||
@ -737,11 +770,14 @@ public function getTableProductRow()
|
|||||||
'product_sold' => true,
|
'product_sold' => true,
|
||||||
'rate_value' => $this->request->getPost('rate_value'),
|
'rate_value' => $this->request->getPost('rate_value'),
|
||||||
'amount_value' => $this->request->getPost('amount_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_1' => $role !== 'COMMERCIALE' ? $this->request->getPost('tranche_1') : null,
|
||||||
'tranche_2' => $role !== 'COMMERCIALE' ? $this->request->getPost('tranche_2') : 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' => $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)) {
|
if ($Orders->updates($id, $dataUpdate)) {
|
||||||
@ -760,6 +796,16 @@ public function getTableProductRow()
|
|||||||
(int)$user['store_id'],
|
(int)$user['store_id'],
|
||||||
'orders'
|
'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) {
|
if ((float)$discount > 0) {
|
||||||
@ -1604,16 +1650,14 @@ public function print5(int $id)
|
|||||||
{
|
{
|
||||||
$this->verifyRole('viewOrder');
|
$this->verifyRole('viewOrder');
|
||||||
|
|
||||||
if (! $id) {
|
if (!$id) {
|
||||||
throw new \CodeIgniter\Exceptions\PageNotFoundException();
|
throw new \CodeIgniter\Exceptions\PageNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modèles
|
|
||||||
$Orders = new Orders();
|
$Orders = new Orders();
|
||||||
$Company = new Company();
|
$Company = new Company();
|
||||||
$OrderItems = new OrderItems();
|
$OrderItems = new OrderItems();
|
||||||
|
|
||||||
// Récupération des données
|
|
||||||
$order = $Orders->getOrdersData($id);
|
$order = $Orders->getOrdersData($id);
|
||||||
$items = $OrderItems->getOrdersItemData($id);
|
$items = $OrderItems->getOrdersItemData($id);
|
||||||
$company = $Company->getCompanyData(1);
|
$company = $Company->getCompanyData(1);
|
||||||
@ -1628,7 +1672,6 @@ public function print5(int $id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculs
|
|
||||||
$discount = (float) $order['discount'];
|
$discount = (float) $order['discount'];
|
||||||
$grossAmount = (float) $order['gross_amount'];
|
$grossAmount = (float) $order['gross_amount'];
|
||||||
$totalTTC = ($discount > 0) ? $discount : $grossAmount;
|
$totalTTC = ($discount > 0) ? $discount : $grossAmount;
|
||||||
@ -1638,164 +1681,344 @@ public function print5(int $id)
|
|||||||
|
|
||||||
$paidLabel = $order['paid_status'] == 1 ? 'Payé' : 'Non payé';
|
$paidLabel = $order['paid_status'] == 1 ? 'Payé' : 'Non payé';
|
||||||
|
|
||||||
// Début du HTML
|
|
||||||
$html = '<!DOCTYPE html>
|
$html = '<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Facture '.$order['bill_no'].'</title>
|
<title>Facture '.$order['bill_no'].'</title>
|
||||||
<style>
|
<style>
|
||||||
body { font-family: Arial, sans-serif; font-size:14px; color:#000;margin:0; padding:0; }
|
/* ✅ FORMAT A4 PAYSAGE DIVISÉ EN 2 */
|
||||||
.header { display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; }
|
@page {
|
||||||
.header .infos { line-height:1.4; }
|
size: A4 landscape;
|
||||||
.header img { max-height:80px; }
|
margin: 0;
|
||||||
.client { margin-bottom:20px; }
|
}
|
||||||
table { width:100%; border-collapse:collapse; margin-bottom:20px; }
|
|
||||||
th, td { border:1px solid #000; padding:6px; }
|
body {
|
||||||
th { background:#f0f0f0; }
|
font-family: Arial, sans-serif;
|
||||||
.right { text-align:right; }
|
font-size: 10px;
|
||||||
.signature { display:flex; justify-content:space-between; margin-top:50px; }
|
color: #000;
|
||||||
.signature div { text-align:center; }
|
margin: 0;
|
||||||
.conditions { page-break-before: always; padding:20px; line-height:1.5; }
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ✅ CONTENEUR : 2 COLONNES CÔTE À CÔTE */
|
||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
width: 297mm;
|
||||||
|
height: 210mm;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ✅ CHAQUE FACTURE = 50% DE LA LARGEUR */
|
||||||
|
.facture-box {
|
||||||
|
flex: 1;
|
||||||
|
width: 148.5mm;
|
||||||
|
padding: 10mm;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-right: 2px dashed #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.facture-box:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .infos {
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .infos h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .infos p {
|
||||||
|
margin: 2px 0;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header img {
|
||||||
|
max-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p.facture-num {
|
||||||
|
margin: 5px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client p {
|
||||||
|
margin: 3px 0;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.words-box {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.words-box strong {
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature div {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ✅ CONDITIONS SUR PAGE SÉPARÉE (VERSO) */
|
||||||
|
.conditions-page {
|
||||||
|
page-break-before: always;
|
||||||
|
display: flex;
|
||||||
|
width: 297mm;
|
||||||
|
height: 210mm;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box {
|
||||||
|
flex: 1;
|
||||||
|
width: 148.5mm;
|
||||||
|
padding: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-right: 2px dashed #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box h3 {
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box .buyer-signature {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditions-box img {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body onload="window.print()">
|
<body onload="window.print()">
|
||||||
|
|
||||||
<div class="header">
|
<!-- ✅ PAGE 1 : RECTO - 2 FACTURES CÔTE À CÔTE -->
|
||||||
<div class="infos">
|
<div class="page">';
|
||||||
<h2 style="margin:0;">'.esc($company['company_name']).'</h2>
|
|
||||||
<p style="margin:2px 0;"><strong>NIF :</strong> '.esc($company['NIF']).'</p>
|
|
||||||
<p style="margin:2px 0;"><strong>STAT :</strong> '.esc($company['STAT']).'</p>
|
|
||||||
<p style="margin:2px 0;"><strong>Contact :</strong> '.esc($company['phone']).' | '.esc($company['phone2']).'</p>
|
|
||||||
</div>
|
|
||||||
<div style="text-align:center;">
|
|
||||||
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo">
|
|
||||||
<p style="margin:5px 0; font-weight:bold;">Facture N° '.esc($order['bill_no']).'</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="client">
|
// ✅ GÉNÉRER 2 FACTURES IDENTIQUES
|
||||||
<p><strong>DOIT Nom :</strong> '.esc($order['customer_name']).'</p>
|
for ($i = 0; $i < 2; $i++) {
|
||||||
<p><strong>Adresse :</strong> '.esc($order['customer_address']).'</p>
|
|
||||||
<p><strong>CIN :</strong> '.esc($order['customer_cin']).'</p>
|
|
||||||
<p><strong>Téléphone :</strong> '.esc($order['customer_phone'] ?? '').'</p>
|
|
||||||
<p style="text-align:right;"><em>Antananarivo, le '.$today.'</em></p>
|
|
||||||
</div>';
|
|
||||||
|
|
||||||
// ✅ TABLEAU ADAPTÉ SELON LE TYPE
|
|
||||||
if ($isAvanceMere) {
|
|
||||||
// ========================================
|
|
||||||
// TABLE SIMPLIFIÉE POUR AVANCE "SUR MER"
|
|
||||||
// ========================================
|
|
||||||
$html .= '
|
$html .= '
|
||||||
<table>
|
<div class="facture-box">
|
||||||
<thead>
|
<div class="header">
|
||||||
<tr>
|
<div class="infos">
|
||||||
<th>Produit</th>
|
<h2>'.esc($company['company_name']).'</h2>
|
||||||
<th class="right">Prix (Ar)</th>
|
<p><strong>NIF :</strong> '.esc($company['NIF']).'</p>
|
||||||
</tr>
|
<p><strong>STAT :</strong> '.esc($company['STAT']).'</p>
|
||||||
</thead>
|
<p><strong>Contact :</strong> '.esc($company['phone']).' | '.esc($company['phone2']).'</p>
|
||||||
<tbody>';
|
</div>
|
||||||
|
<div style="text-align:center;">
|
||||||
|
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo">
|
||||||
|
<p class="facture-num">Facture N° '.esc($order['bill_no']).'</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
foreach ($items as $it) {
|
<div class="client">
|
||||||
$details = $this->getOrderItemDetails($it);
|
<p><strong>DOIT Nom :</strong> '.esc($order['customer_name']).'</p>
|
||||||
|
<p><strong>Adresse :</strong> '.esc($order['customer_address']).'</p>
|
||||||
|
<p><strong>CIN :</strong> '.esc($order['customer_cin']).'</p>
|
||||||
|
<p><strong>Téléphone :</strong> '.esc($order['customer_phone'] ?? '').'</p>
|
||||||
|
<p style="text-align:right;"><em>Antananarivo, le '.$today.'</em></p>
|
||||||
|
</div>';
|
||||||
|
|
||||||
if (!$details) continue;
|
// ✅ TABLEAU ADAPTÉ SELON LE TYPE
|
||||||
|
if ($isAvanceMere) {
|
||||||
|
$html .= '
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Produit</th>
|
||||||
|
<th class="right">Prix (Ar)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>';
|
||||||
|
|
||||||
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
|
foreach ($items as $it) {
|
||||||
|
$details = $this->getOrderItemDetails($it);
|
||||||
|
|
||||||
|
if (!$details) continue;
|
||||||
|
|
||||||
// Afficher le commentaire s'il existe
|
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
|
||||||
if (!empty($details['commentaire'])) {
|
|
||||||
$html .= '<br><em style="font-size:12px; color:#666;">'.esc($details['commentaire']).'</em>';
|
$html .= '<tr><td>'.esc($details['product_name']);
|
||||||
|
|
||||||
|
if (!empty($details['commentaire'])) {
|
||||||
|
$html .= '<br><em style="font-size:8px; color:#666;">'.esc($details['commentaire']).'</em>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</td>
|
||||||
|
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
|
||||||
|
</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$html .= '</td>
|
// ✅ CORRECTION : Fermer le tableau pour avance
|
||||||
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
|
|
||||||
</tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// ========================================
|
|
||||||
// TABLE COMPLÈTE POUR AVANCE "SUR TERRE" OU COMMANDE NORMALE
|
|
||||||
// ========================================
|
|
||||||
$html .= '
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>MARQUE</th>
|
|
||||||
<th>Désignation</th>
|
|
||||||
<th>N° Moteur</th>
|
|
||||||
<th>N° Châssis</th>
|
|
||||||
<th>Puissance (CC)</th>
|
|
||||||
<th class="right">PRIX (Ar)</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>';
|
|
||||||
|
|
||||||
foreach ($items as $it) {
|
|
||||||
$details = $this->getOrderItemDetails($it);
|
|
||||||
|
|
||||||
if (!$details) continue;
|
|
||||||
|
|
||||||
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
|
|
||||||
|
|
||||||
|
|
||||||
$html .= '
|
$html .= '
|
||||||
<tr>
|
</tbody>
|
||||||
<td>'.esc($details['marque']).'</td>
|
</table>';
|
||||||
<td>'.esc($details['product_name']).'</td>
|
|
||||||
<td>'.esc($details['numero_moteur']).'</td>
|
} else {
|
||||||
<td>'.esc($details['numero_chassis']).'</td>
|
$html .= '
|
||||||
<td>'.esc($details['puissance']).'</td> <!-- ✅ ICI -->
|
<table>
|
||||||
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
|
<thead>
|
||||||
</tr>';
|
<tr>
|
||||||
|
<th>MARQUE</th>
|
||||||
|
<th>Désignation</th>
|
||||||
|
<th>N° Moteur</th>
|
||||||
|
<th>N° Châssis</th>
|
||||||
|
<th>Puissance (CC)</th>
|
||||||
|
<th class="right">PRIX (Ar)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>';
|
||||||
|
|
||||||
|
foreach ($items as $it) {
|
||||||
|
$details = $this->getOrderItemDetails($it);
|
||||||
|
|
||||||
|
if (!$details) continue;
|
||||||
|
|
||||||
|
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
<tr>
|
||||||
|
<td>'.esc($details['marque']).'</td>
|
||||||
|
<td>'.esc($details['product_name']).'</td>
|
||||||
|
<td>'.esc($details['numero_moteur']).'</td>
|
||||||
|
<td>'.esc($details['numero_chassis']).'</td>
|
||||||
|
<td>'.esc($details['puissance']).'</td>
|
||||||
|
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
|
||||||
|
</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Fermer le tableau pour produit normal
|
||||||
|
$html .= '
|
||||||
|
</tbody>
|
||||||
|
</table>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Prix (HT) :</strong></td>
|
||||||
|
<td class="right">'.number_format($totalHT, 0, '', ' ').' Ar</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>TVA (20%) :</strong></td>
|
||||||
|
<td class="right">'.number_format($tva, 0, '', ' ').' Ar</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Total (TTC) :</strong></td>
|
||||||
|
<td class="right">'.number_format($totalTTC, 0, '', ' ').' Ar</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="words-box">
|
||||||
|
<strong>Arrêté à la somme de :</strong><br>
|
||||||
|
'.$inWords.'
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="signature">
|
||||||
|
<div>L\'Acheteur<br><br>__________________</div>
|
||||||
|
<div>Le Vendeur<br><br>__________________</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$html .= '
|
$html .= '
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><strong>Prix (HT) :</strong></td>
|
|
||||||
<td class="right">'.number_format($totalHT, 0, '', ' ').' Ar</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><strong>TVA (20%) :</strong></td>
|
|
||||||
<td class="right">'.number_format($tva, 0, '', ' ').' Ar</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><strong>Total (TTC) :</strong></td>
|
|
||||||
<td class="right">'.number_format($totalTTC, 0, '', ' ').' Ar</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div style="border:1px solid #000; padding:10px; margin-bottom:30px;">
|
|
||||||
<strong>Arrêté à la somme de :</strong><br>
|
|
||||||
'.$inWords.'
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="signature">
|
<!-- ✅ PAGE 2 : VERSO - 2 CONDITIONS GÉNÉRALES CÔTE À CÔTE -->
|
||||||
<div>L\'Acheteur<br><br>__________________</div>
|
<div class="conditions-page">';
|
||||||
<div>Le Vendeur<br><br>__________________</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Conditions Générales avec saut de page -->
|
// ✅ GÉNÉRER 2 CONDITIONS IDENTIQUES
|
||||||
<div class="conditions">
|
for ($i = 0; $i < 2; $i++) {
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
$html .= '
|
||||||
<h3 style="margin:0;">Conditions Générales</h3>
|
<div class="conditions-box">
|
||||||
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo" style="height:60px;">
|
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||||
</div>
|
<h3>Conditions Générales</h3>
|
||||||
<ul>
|
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo">
|
||||||
<li>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.</li>
|
</div>
|
||||||
<li>Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.</li>
|
<ul>
|
||||||
<li>Aucun service après-vente n\'est fourni.</li>
|
<li>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.</li>
|
||||||
<li>La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.</li>
|
<li>Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.</li>
|
||||||
<li>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.</li>
|
<li>Aucun service après-vente n\'est fourni.</li>
|
||||||
</ul>
|
<li>La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.</li>
|
||||||
<div style="text-align:center; margin-top:50px;">L\'Acheteur</div>
|
<li>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.</li>
|
||||||
|
</ul>
|
||||||
|
<div class="buyer-signature">L\'Acheteur</div>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -222,9 +222,6 @@ class RecouvrementController extends AdminController
|
|||||||
|
|
||||||
$data['page_title'] = $this->pageTitle;
|
$data['page_title'] = $this->pageTitle;
|
||||||
|
|
||||||
// echo "<pre>";
|
|
||||||
// die(var_dump($this->request->getPost()));
|
|
||||||
|
|
||||||
// Load validation service
|
// Load validation service
|
||||||
$validation = \Config\Services::validation();
|
$validation = \Config\Services::validation();
|
||||||
|
|
||||||
@ -242,59 +239,36 @@ class RecouvrementController extends AdminController
|
|||||||
'recouvrement_date' => $this->request->getPost('recouvrement_date'),
|
'recouvrement_date' => $this->request->getPost('recouvrement_date'),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Set validation rules
|
|
||||||
|
|
||||||
$Notification = new NotificationController();
|
$Notification = new NotificationController();
|
||||||
$Recouvrement = new Recouvrement();
|
$Recouvrement = new Recouvrement();
|
||||||
// $recouvrement_id = $this->request->getPost('recouvrement_id');
|
|
||||||
$session = session();
|
$session = session();
|
||||||
$users = $session->get('user');
|
$users = $session->get('user');
|
||||||
|
|
||||||
if ($users && isset($users['firstname'], $users['lastname'])) {
|
if ($users && isset($users['firstname'], $users['lastname'])) {
|
||||||
$fullname = $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)) {
|
if ($validation->run($validationData)) {
|
||||||
// // Prepare data
|
$send_mode = $this->request->getPost('send_mode');
|
||||||
$session = session();
|
$get_mode = $this->request->getPost('get_mode');
|
||||||
$users = $session->get('user');
|
$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 = [
|
$data = [
|
||||||
'recouvrement_montant' => $this->request->getPost('recouvrement_montant'),
|
'recouvrement_montant' => $amount,
|
||||||
'recouvrement_date' => $this->request->getPost('recouvrement_date'),
|
'recouvrement_date' => $this->request->getPost('recouvrement_date'),
|
||||||
'recouvrement_personnel' => $fullname,
|
'recouvrement_personnel' => $fullname,
|
||||||
'get_money' => $this->request->getPost('get_mode'),
|
'get_money' => $get_mode,
|
||||||
'send_money' => $this->request->getPost('send_mode'),
|
'send_money' => $send_mode,
|
||||||
'user_id' => $users['id'],
|
'user_id' => $users['id'],
|
||||||
'store_id' => $users['store_id'],
|
'store_id' => $users['store_id'],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($Recouvrement->addRecouvrement($data)) {
|
if ($Recouvrement->addRecouvrement($data)) {
|
||||||
@ -315,7 +289,6 @@ class RecouvrementController extends AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function updateRecouvrement($recouvrement_id)
|
public function updateRecouvrement($recouvrement_id)
|
||||||
{
|
{
|
||||||
$this->verifyRole('updateRecouvrement');
|
$this->verifyRole('updateRecouvrement');
|
||||||
@ -367,6 +340,73 @@ class RecouvrementController extends AdminController
|
|||||||
echo json_encode($data);
|
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() {
|
public function fetchTotalRecouvrementData() {
|
||||||
helper(['url', 'form']);
|
helper(['url', 'form']);
|
||||||
|
|||||||
@ -305,7 +305,26 @@ class ReportController extends AdminController
|
|||||||
}
|
}
|
||||||
return $this->response->setJSON($result);
|
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
|
// Pour COMMERCIALE : uniquement ses propres ventes
|
||||||
if ($users['group_name'] === "COMMERCIALE") {
|
if ($users['group_name'] === "COMMERCIALE") {
|
||||||
$orderPaid = $Orders->getPerformanceByOrders2();
|
$orderPaid = $Orders->getPerformanceByOrders2();
|
||||||
@ -334,4 +353,42 @@ class ReportController extends AdminController
|
|||||||
$result = ['data'=> []];
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -371,28 +371,29 @@ class SortieCaisseController extends AdminController
|
|||||||
$result = $SortieCaisse->updateSortieCaisse($id_sortie, $data);
|
$result = $SortieCaisse->updateSortieCaisse($id_sortie, $data);
|
||||||
|
|
||||||
if ($result) {
|
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')) {
|
if (class_exists('App\Controllers\NotificationController')) {
|
||||||
$Notification = new NotificationController();
|
$Notification = new NotificationController();
|
||||||
|
|
||||||
$montant = number_format($decaissement['montant_retire'], 0, ',', ' ');
|
$montant = number_format($decaissement['montant_retire'], 0, ',', ' ');
|
||||||
$message = "💰 Décaissement payé - " . $montant . " Ar<br>" .
|
$message = "💰 Décaissement payé - " . $montant . " Ar<br>" .
|
||||||
"Motif: " . $decaissement['motif'] . "<br>" .
|
"Motif: " . $decaissement['motif'] . "<br>" .
|
||||||
"Caissière: " . $users['firstname'] . ' ' . $users['lastname'];
|
"Caissière: " . $users['firstname'] . ' ' . $users['lastname'] . "<br>" .
|
||||||
|
"Store: " . $this->returnStoreName($users['store_id']);
|
||||||
|
|
||||||
// Notifier la Direction
|
// ✅ Notifier la Direction DU MÊME STORE
|
||||||
$Notification->createNotification(
|
$Notification->createNotification(
|
||||||
$message,
|
$message,
|
||||||
"Direction",
|
"Direction",
|
||||||
(int)$users['store_id'],
|
(int)$users['store_id'], // ✅ Store ID de la caissière
|
||||||
'sortieCaisse'
|
'sortieCaisse'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Notifier le DAF
|
// ✅ Notifier le DAF DU MÊME STORE
|
||||||
$Notification->createNotification(
|
$Notification->createNotification(
|
||||||
$message,
|
$message,
|
||||||
"DAF",
|
"DAF",
|
||||||
(int)$users['store_id'],
|
(int)$users['store_id'], // ✅ Store ID de la caissière
|
||||||
'sortieCaisse'
|
'sortieCaisse'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -400,9 +401,10 @@ class SortieCaisseController extends AdminController
|
|||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'messages' => '✅ Décaissement marqué comme <strong>PAYÉ</strong><br>' .
|
'messages' => '✅ Décaissement marqué comme <strong>PAYÉ</strong><br>' .
|
||||||
'Direction et DAF ont été notifiés.<br>' .
|
'Direction et DAF de ' . $this->returnStoreName($users['store_id']) . ' ont été notifiés.<br>' .
|
||||||
'Montant: ' . number_format($decaissement['montant_retire'], 0, ',', ' ') . ' Ar'
|
'Montant: ' . number_format($decaissement['montant_retire'], 0, ',', ' ') . ' Ar'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@ -719,7 +721,7 @@ class SortieCaisseController extends AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Champs supplémentaires pour montant > 1,000,000
|
// 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['numero_fiche'] = $this->request->getPost('numero_fiche') ?? '';
|
||||||
$data['date_fiche'] = $this->request->getPost('date_fiche') ?? null;
|
$data['date_fiche'] = $this->request->getPost('date_fiche') ?? null;
|
||||||
$data['service_demandeur'] = $this->request->getPost('service_demandeur') ?? '';
|
$data['service_demandeur'] = $this->request->getPost('service_demandeur') ?? '';
|
||||||
@ -765,13 +767,23 @@ class SortieCaisseController extends AdminController
|
|||||||
$result = $model->addSortieCaisse($data);
|
$result = $model->addSortieCaisse($data);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
// Notification
|
// Notification UNIQUEMENT pour la Direction du même store
|
||||||
if (class_exists('App\Controllers\NotificationController')) {
|
if (class_exists('App\Controllers\NotificationController')) {
|
||||||
$Notification = new NotificationController();
|
$Notification = new NotificationController();
|
||||||
|
|
||||||
|
// ✅ Notifier UNIQUEMENT la Direction du store concerné
|
||||||
$Notification->createNotification(
|
$Notification->createNotification(
|
||||||
"Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")",
|
"Nouvelle demande de décaissement de " . number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")",
|
||||||
"Direction",
|
"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'
|
'sortieCaisse'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -780,8 +792,10 @@ class SortieCaisseController extends AdminController
|
|||||||
'success' => true,
|
'success' => true,
|
||||||
'messages' => 'Décaissement de ' . number_format($montant_retire, 0, ',', ' ') . ' Ar créé avec succès<br>' .
|
'messages' => 'Décaissement de ' . number_format($montant_retire, 0, ',', ' ') . ' Ar créé avec succès<br>' .
|
||||||
'Mode de paiement: ' . $mode_paiement . '<br>' .
|
'Mode de paiement: ' . $mode_paiement . '<br>' .
|
||||||
'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<br>' .
|
||||||
|
'<em>Notification envoyée à la Direction de ' . $this->returnStoreName($user['store_id']) . '</em>'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@ -1076,24 +1090,28 @@ class SortieCaisseController extends AdminController
|
|||||||
$statut = $this->request->getPost('statut');
|
$statut = $this->request->getPost('statut');
|
||||||
$message = '';
|
$message = '';
|
||||||
|
|
||||||
|
// ✅ Récupérer le décaissement pour avoir son store_id
|
||||||
|
$decaissement = $SortieCaisse->getSortieCaisseSingle($id_sortie);
|
||||||
|
$store_id = $decaissement['store_id'];
|
||||||
|
|
||||||
switch ($statut) {
|
switch ($statut) {
|
||||||
case "Valider":
|
case "Valider":
|
||||||
$message = "Décaissement validé avec succès";
|
$message = "✅ Votre décaissement a été validé par la Direction de " . $this->returnStoreName($store_id);
|
||||||
$Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse');
|
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
|
||||||
break;
|
break;
|
||||||
case "Refuser":
|
case "Refuser":
|
||||||
$message = "Un décaissement a été refusé";
|
$message = "❌ Votre décaissement a été refusé par la Direction de " . $this->returnStoreName($store_id) . "<br>Raison: " . $this->request->getPost('admin_raison');
|
||||||
$Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse');
|
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
|
||||||
break;
|
break;
|
||||||
case "En attente":
|
case "En attente":
|
||||||
$message = "Décaissement mis en attente";
|
$message = "⏳ Votre décaissement a été mis en attente par la Direction de " . $this->returnStoreName($store_id);
|
||||||
$Notification->createNotification($message, "Caissière", (int)$users["store_id"], 'sortieCaisse');
|
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'success' => true,
|
'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 {
|
} else {
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
|
|||||||
@ -29,6 +29,8 @@ class Orders extends Model
|
|||||||
'discount',
|
'discount',
|
||||||
'paid_status',
|
'paid_status',
|
||||||
'user_id',
|
'user_id',
|
||||||
|
'validated_by',
|
||||||
|
'validated_at',
|
||||||
'store_id',
|
'store_id',
|
||||||
'tranche_1',
|
'tranche_1',
|
||||||
'tranche_2',
|
'tranche_2',
|
||||||
@ -825,4 +827,44 @@ public function getUserPerformanceByMonth(string $month)
|
|||||||
return $results;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -186,4 +186,29 @@ class Recouvrement extends Model{
|
|||||||
|
|
||||||
return $reparation;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -62,84 +62,116 @@ protected $allowedFields = [
|
|||||||
'date_visa_conseil',
|
'date_visa_conseil',
|
||||||
'observations'
|
'observations'
|
||||||
];
|
];
|
||||||
public function getAllSortieCaisse()
|
public function getAllSortieCaisse()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$session = session();
|
$session = session();
|
||||||
$users = $session->get('user');
|
$users = $session->get('user');
|
||||||
if ($users['group_name'] === 'Direction') {
|
|
||||||
return $this
|
|
||||||
->select('*')
|
|
||||||
->orderBy('date_retrait', 'DESC')
|
|
||||||
->findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($users['group_name'] === 'Conseil') {
|
// ✅ DIRECTION : Voir uniquement les décaissements de SON store
|
||||||
return $this
|
if ($users['group_name'] === 'Direction') {
|
||||||
->select('*')
|
return $this
|
||||||
->orderBy('date_retrait', 'DESC')
|
|
||||||
->findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
if($users["group_name"]==="Caissière"){
|
|
||||||
return $this
|
|
||||||
->select('*')
|
->select('*')
|
||||||
->where('user_id', $users['id'])
|
->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE
|
||||||
->orderBy('date_retrait', 'DESC')
|
->orderBy('date_retrait', 'DESC')
|
||||||
->findAll();
|
->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
|
return $this
|
||||||
->select('*')
|
->select('*')
|
||||||
->where('user_id', $users['id'])
|
->where('user_id', $users['id'])
|
||||||
->orderBy('date_retrait', 'DESC')
|
->orderBy('date_retrait', 'DESC')
|
||||||
->findAll();
|
->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()
|
public function getAllSortieCaisse1()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$session = session();
|
$session = session();
|
||||||
$users = $session->get('user');
|
$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') {
|
// ✅ DIRECTION : Voir uniquement les décaissements de SON store
|
||||||
return $this
|
if ($users['group_name'] === 'Direction') {
|
||||||
->select('*')
|
return $this
|
||||||
->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('*')
|
->select('*')
|
||||||
->where('user_id', $users['id'])
|
->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')
|
->orderBy('date_retrait', 'DESC')
|
||||||
->findAll();
|
->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
|
return $this
|
||||||
->select('*')
|
->select('*')
|
||||||
->where('user_id', $users['id'])
|
->where('user_id', $users['id'])
|
||||||
->orderBy('date_retrait', 'DESC')
|
->orderBy('date_retrait', 'DESC')
|
||||||
->findAll();
|
->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) {
|
public function addSortieCaisse(array $data) {
|
||||||
try {
|
try {
|
||||||
return $this->insert($data);
|
return $this->insert($data);
|
||||||
@ -174,7 +206,12 @@ protected $allowedFields = [
|
|||||||
public function getTotalSortieCaisse() {
|
public function getTotalSortieCaisse() {
|
||||||
$session = session();
|
$session = session();
|
||||||
$users = $session->get('user');
|
$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) {
|
if ($isAdmin) {
|
||||||
try {
|
try {
|
||||||
@ -184,6 +221,7 @@ protected $allowedFields = [
|
|||||||
SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant_retire ELSE 0 END) AS total_virement,
|
SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant_retire ELSE 0 END) AS total_virement,
|
||||||
SUM(montant_retire) AS mr
|
SUM(montant_retire) AS mr
|
||||||
')
|
')
|
||||||
|
->where('store_id', $users['store_id']) // ✅ FILTRE PAR STORE
|
||||||
->whereIn('statut', ['Valider', 'Payé'])
|
->whereIn('statut', ['Valider', 'Payé'])
|
||||||
->get()
|
->get()
|
||||||
->getRowObject();
|
->getRowObject();
|
||||||
@ -196,7 +234,29 @@ protected $allowedFields = [
|
|||||||
'mr' => 0
|
'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 {
|
} else {
|
||||||
|
// ✅ CAISSIÈRE : Uniquement son store
|
||||||
try {
|
try {
|
||||||
return $this->select('
|
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 = "En espèce" THEN montant_retire ELSE 0 END) AS total_espece,
|
||||||
|
|||||||
@ -712,14 +712,15 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<?php if ($isCaissier === true): ?>
|
<!-- ✅ SECTION CAISSIER - SIMPLIFIÉ SANS GRAPHIQUE -->
|
||||||
|
<?php if ($isCaissier === true): ?>
|
||||||
|
|
||||||
<!-- Section des totaux caisse -->
|
<!-- Section des totaux caisse -->
|
||||||
<div class="container-fluid row">
|
<div class="container-fluid row">
|
||||||
<!-- ✅ MODIFIÉ : Utiliser total_caisse au lieu de total -->
|
|
||||||
<div class="col-lg-3 col-xs-6">
|
<div class="col-lg-3 col-xs-6">
|
||||||
<div class="small-box" style="background-color: #A9A9A9;">
|
<div class="small-box" style="background-color: #A9A9A9;">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<h2><?php echo number_format($total_caisse, 0, '.', ' '); ?>Ar</h2>
|
<h2><?php echo number_format($total_caisse, 0, '.', ' '); ?> Ar</h2>
|
||||||
<p>Totale CAISSE</p>
|
<p>Totale CAISSE</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
@ -728,11 +729,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ✅ MODIFIÉ : Utiliser total_mvola_caisse -->
|
|
||||||
<div class="col-lg-3 col-xs-6">
|
<div class="col-lg-3 col-xs-6">
|
||||||
<div class="small-box" style="background-color: #A9A9A9;">
|
<div class="small-box" style="background-color: #A9A9A9;">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<h2><?php echo number_format($total_mvola_caisse, 0, '.', ' '); ?>Ar</h2>
|
<h2><?php echo number_format($total_mvola_caisse, 0, '.', ' '); ?> Ar</h2>
|
||||||
<p>Totale MVOLA</p>
|
<p>Totale MVOLA</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
@ -741,11 +741,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ✅ MODIFIÉ : Utiliser total_espece_caisse -->
|
|
||||||
<div class="col-lg-3 col-xs-6">
|
<div class="col-lg-3 col-xs-6">
|
||||||
<div class="small-box" style="background-color: #A9A9A9;">
|
<div class="small-box" style="background-color: #A9A9A9;">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<h2><?php echo number_format($total_espece_caisse, 0, '.', ' '); ?>Ar</h2>
|
<h2><?php echo number_format($total_espece_caisse, 0, '.', ' '); ?> Ar</h2>
|
||||||
<p>Totale en espèce</p>
|
<p>Totale en espèce</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
@ -754,11 +753,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ✅ MODIFIÉ : Utiliser total_vb_caisse -->
|
|
||||||
<div class="col-lg-3 col-xs-6">
|
<div class="col-lg-3 col-xs-6">
|
||||||
<div class="small-box" style="background-color: #A9A9A9;">
|
<div class="small-box" style="background-color: #A9A9A9;">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<h2><?php echo number_format($total_vb_caisse, 0, '.', ' '); ?>Ar</h2>
|
<h2><?php echo number_format($total_vb_caisse, 0, '.', ' '); ?> Ar</h2>
|
||||||
<p>Totale en banque</p>
|
<p>Totale en banque</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
@ -768,7 +766,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ✅ NOUVEAU : Afficher le détail Orders vs Avances -->
|
<!-- Détail Orders vs Avances -->
|
||||||
<div class="container-fluid row" style="margin-top: 10px;">
|
<div class="container-fluid row" style="margin-top: 10px;">
|
||||||
<div class="col-lg-6 col-xs-12">
|
<div class="col-lg-6 col-xs-12">
|
||||||
<div class="info-box bg-aqua">
|
<div class="info-box bg-aqua">
|
||||||
@ -791,7 +789,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ✅ NOUVEAU : Détail des avances par mode de paiement -->
|
<!-- Détail des avances par mode de paiement -->
|
||||||
<div class="container-fluid row" style="margin-top: 10px; margin-bottom: 20px;">
|
<div class="container-fluid row" style="margin-top: 10px; margin-bottom: 20px;">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="box box-info">
|
<div class="box box-info">
|
||||||
@ -824,11 +822,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Section Rapport de Performance -->
|
||||||
<section class="content-header">
|
<section class="content-header">
|
||||||
<h1>Rapport de Performance du Caissier</h1>
|
<h1>📊 Mes Performances de Vente</h1>
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="#"><i class="fa fa-home"></i> Accueil</a></li>
|
<li><a href="#"><i class="fa fa-home"></i> Accueil</a></li>
|
||||||
<li class="active" onclick="window.history.back()" style="cursor: pointer;">Rapports</li>
|
<li class="active">Rapports</li>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -841,33 +840,53 @@
|
|||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<div class="col-md-12 col-lg-12">
|
<div class="col-md-12 col-lg-12">
|
||||||
<div class="card shadow-sm border-0">
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h3 class="card-title m-0">
|
||||||
|
<i class="fa fa-list"></i> Liste de mes ventes validées
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<!-- Filtres -->
|
||||||
<div class="row g-3 align-items-center mb-4" style="margin: 5px 0 5px 5px;">
|
<div class="row g-3 align-items-center mb-4" style="margin: 5px 0 5px 5px;">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="startDate" class="form-label">Date de début</label>
|
<label for="startDateCaissier" class="form-label">Date de début</label>
|
||||||
<input type="date" id="startDate" class="form-control">
|
<input type="date" id="startDateCaissier" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="endDate" class="form-label">Date de fin</label>
|
<label for="endDateCaissier" class="form-label">Date de fin</label>
|
||||||
<input type="date" id="endDate" class="form-control">
|
<input type="date" id="endDateCaissier" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 d-flex align-items-end">
|
<div class="col-md-3 d-flex align-items-end">
|
||||||
<br>
|
<br>
|
||||||
<button id="filteredB1" class="btn btn-primary w-100">Filtrer 🔍</button>
|
<button id="filteredBtnCaissier" class="btn btn-primary w-100">
|
||||||
<button id="ExportBTN1" class="btn btn-success w-100">Exporter 📤</button>
|
<i class="fa fa-filter"></i> Filtrer
|
||||||
|
</button>
|
||||||
|
<button id="ExportBTNCaissier" class="btn btn-success w-100" style="margin-left: 5px;">
|
||||||
|
<i class="fa fa-file-excel-o"></i> Exporter
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table id="caissierperf" class="table table-hover table-striped">
|
|
||||||
<thead>
|
<!-- Dans la section caissière, assurez-vous que le tableau a la bonne structure -->
|
||||||
<tr>
|
<table id="caissierperf" class="table table-hover table-striped table-bordered">
|
||||||
<th id="me">MVOLA & Espèce</th>
|
<thead>
|
||||||
<th id="bm">Banque & MVOLA</th>
|
<tr>
|
||||||
<th id="be">Banque & Espèce</th>
|
<th>Caissier</th>
|
||||||
<th id="mb">MVOLA & Banque</th>
|
<th>Moto vendue</th>
|
||||||
<th id="mr">Montant total</th>
|
<th>Date de vente</th>
|
||||||
</tr>
|
<th>Prix de vente</th>
|
||||||
</thead>
|
</tr>
|
||||||
</table>
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Les données seront chargées automatiquement par DataTables -->
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3" style="text-align:right; font-weight: bold;">Total :</th>
|
||||||
|
<th style="font-weight: bold;"></th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -879,87 +898,102 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div style="width: 80%; margin: auto;">
|
<!-- ✅ SCRIPT POUR LE TABLEAU CAISSIER UNIQUEMENT -->
|
||||||
<canvas id="salesChart"></canvas>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
||||||
</div>
|
<script>
|
||||||
|
(function() {
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
var caissierTable;
|
||||||
<script>
|
|
||||||
var manageTable;
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
console.log('🔍 Initialisation du tableau caissier...');
|
||||||
|
|
||||||
|
// Configuration DataTable pour caissier
|
||||||
// Initialize the datatable
|
caissierTable = $('#caissierperf').DataTable({
|
||||||
manageTable = $('#caissierperf').DataTable({
|
'ajax': {
|
||||||
'ajax': 'recouvrement/fetchTotalRecouvrementData',
|
'url': '<?= base_url('reports/detail/fetchCaissierPerformances') ?>',
|
||||||
'order': [],
|
'data': function(d) {
|
||||||
'pageLength': 5,
|
// ✅ AJOUT : Envoyer les dates au serveur
|
||||||
'lengthMenu': [
|
d.startDate = $('#startDateCaissier').val();
|
||||||
[5, 10, 25, 50, -1],
|
d.endDate = $('#endDateCaissier').val();
|
||||||
[5, 10, 25, 50, "All"]
|
},
|
||||||
|
'dataSrc': function(json) {
|
||||||
|
console.log('📊 Données reçues:', json);
|
||||||
|
return json.data;
|
||||||
|
},
|
||||||
|
'error': function(xhr, error, thrown) {
|
||||||
|
console.error('❌ Erreur AJAX:', error, thrown);
|
||||||
|
console.error('Réponse:', xhr.responseText);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'columns': [
|
||||||
|
{ 'data': 0 }, // Caissier
|
||||||
|
{ 'data': 1 }, // Moto vendue
|
||||||
|
{ 'data': 2 }, // Date
|
||||||
|
{ 'data': 3 } // Prix
|
||||||
],
|
],
|
||||||
"columnDefs": [{
|
'order': [[2, 'desc']],
|
||||||
"targets": "_all",
|
'pageLength': 10,
|
||||||
"className": "text-left"
|
'lengthMenu': [[5, 10, 25, 50, -1], [5, 10, 25, 50, "Tout"]],
|
||||||
}],
|
'language': {
|
||||||
|
'processing': "Traitement en cours...",
|
||||||
"footerCallback": function (row, data, start, end, display) {
|
'search': "Rechercher :",
|
||||||
|
'lengthMenu': "Afficher _MENU_ éléments",
|
||||||
|
'info': "Affichage de l'élément _START_ à _END_ sur _TOTAL_ éléments",
|
||||||
|
'infoEmpty': "Affichage de l'élément 0 à 0 sur 0 élément",
|
||||||
|
'infoFiltered': "(filtré de _MAX_ éléments au total)",
|
||||||
|
'loadingRecords': "Chargement en cours...",
|
||||||
|
'zeroRecords': "Aucune vente enregistrée",
|
||||||
|
'emptyTable': "Aucune donnée disponible",
|
||||||
|
'paginate': {
|
||||||
|
'first': "Premier",
|
||||||
|
'previous': "Précédent",
|
||||||
|
'next': "Suivant",
|
||||||
|
'last': "Dernier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'footerCallback': function (row, data, start, end, display) {
|
||||||
const api = this.api();
|
const api = this.api();
|
||||||
|
|
||||||
// Helper function to parse string to float
|
|
||||||
const parseNumber = function (i) {
|
const parseNumber = function (i) {
|
||||||
return typeof i === 'string' ?
|
return typeof i === 'string'
|
||||||
parseFloat(i.replace(/[^\d.-]/g, '')) : // remove currency symbols, commas
|
? parseFloat(i.replace(/[^\d.-]/g, ''))
|
||||||
typeof i === 'number' ?
|
: typeof i === 'number' ? i : 0;
|
||||||
i : 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const totalPrixVente = api
|
||||||
|
.column(3, { page: 'current' })
|
||||||
|
.data()
|
||||||
|
.reduce((a, b) => parseNumber(a) + parseNumber(b), 0);
|
||||||
|
|
||||||
|
const totalFormate = totalPrixVente.toLocaleString('fr-FR');
|
||||||
|
|
||||||
|
$(api.column(3).footer()).html('<strong>' + totalFormate + ' Ar</strong>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#filteredB1').on('click', function () {
|
// ✅ CORRECTION : Filtrage par dates
|
||||||
const startDate = $('#startDate').val();
|
$('#filteredBtnCaissier').on('click', function () {
|
||||||
const endDate = $('#endDate').val();
|
const startDate = $('#startDateCaissier').val();
|
||||||
const pvente = $('#pvente').val(); // id de l'utilisateur ici
|
const endDate = $('#endDateCaissier').val();
|
||||||
|
|
||||||
$.ajax({
|
console.log('🔍 Filtrage:', startDate, endDate);
|
||||||
url: '<?= base_url("recouvrement/fetchTotalRecouvrementData") ?>',
|
|
||||||
type: 'GET',
|
|
||||||
data: {
|
|
||||||
start_date: startDate,
|
|
||||||
end_date: endDate
|
|
||||||
},
|
|
||||||
dataType: 'json',
|
|
||||||
success: function (response) {
|
|
||||||
if (response) {
|
|
||||||
$('#mr').text(response.tr); // par exemple
|
|
||||||
$('#me').text(response.me);
|
|
||||||
$('#bm').text(response.bm);
|
|
||||||
$('#be').text(response.be);
|
|
||||||
$('#mb').text(response.mb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Recharger les données avec les nouveaux paramètres
|
||||||
document.getElementById('ExportBTN1').addEventListener('click', function () {
|
caissierTable.ajax.reload();
|
||||||
// Select your table
|
|
||||||
var table = document.getElementById('caissierperf');
|
|
||||||
|
|
||||||
// Convert it to a workbook
|
|
||||||
var wb = XLSX.utils.table_to_book(table, {
|
|
||||||
sheet: "Feuille1"
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Export it
|
// Export Excel
|
||||||
XLSX.writeFile(wb, 'export-caissier-performance.xlsx');
|
$('#ExportBTNCaissier').on('click', function () {
|
||||||
|
const table = document.getElementById('caissierperf');
|
||||||
|
const wb = XLSX.utils.table_to_book(table, { sheet: "Performances Caissier" });
|
||||||
|
const today = new Date().toISOString().split('T')[0];
|
||||||
|
const fileName = 'performances_caissier_' + today + '.xlsx';
|
||||||
|
XLSX.writeFile(wb, fileName);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
})();
|
||||||
<!-- </div> -->
|
</script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- securite -->
|
<!-- securite -->
|
||||||
|
|
||||||
@ -1151,14 +1185,14 @@
|
|||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row g-3 align-items-center mb-4" style="margin: 5px 0 5px 5px;">
|
<div class="row g-3 align-items-center mb-4" style="margin: 5px 0 5px 5px;">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="startDate" class="form-label">Date de début</label>
|
<label for="startDateCaissier" class="form-label">Date de début</label>
|
||||||
<input type="date" id="startDate" class="form-control">
|
<input type="date" id="startDateCaissier" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="endDate" class="form-label">Date de fin</label>
|
<label for="endDateCaissier" class="form-label">Date de fin</label>
|
||||||
<input type="date" id="endDate" class="form-control">
|
<input type="date" id="endDateCaissier" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 d-flex align-items-end">
|
<div class="col-md-3 d-flex align-items-end">
|
||||||
<br>
|
<br>
|
||||||
<button id="filteredB1" class="btn btn-primary w-100">Filtrer
|
<button id="filteredB1" class="btn btn-primary w-100">Filtrer
|
||||||
|
|||||||
@ -327,22 +327,38 @@
|
|||||||
$("#mainOrdersNav").addClass('active');
|
$("#mainOrdersNav").addClass('active');
|
||||||
$("#manageOrdersNav").addClass('active');
|
$("#manageOrdersNav").addClass('active');
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Gestion des tranches de paiement
|
||||||
var paymentTranche = 1;
|
var paymentTranche = 1;
|
||||||
var netAmount = parseFloat($('#net_amount_value').val()) || 0;
|
|
||||||
|
// ✅ Fonction pour obtenir le montant à répartir (discount ou gross_amount)
|
||||||
|
function getMontantTotal() {
|
||||||
|
var discount = parseFloat($('#discount').val()) || 0;
|
||||||
|
var grossAmount = parseFloat($('#gross_amount_value').val()) || 0;
|
||||||
|
return discount > 0 ? discount : grossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
var netAmount = getMontantTotal();
|
||||||
$('#payment_amount_1').val(netAmount);
|
$('#payment_amount_1').val(netAmount);
|
||||||
|
|
||||||
function addPaymentTranche(paymentTranche) {
|
function addPaymentTranche(paymentTranche) {
|
||||||
if (parseInt(paymentTranche) === 2) {
|
if (parseInt(paymentTranche) === 2) {
|
||||||
$("#paid_status_1").show();
|
$("#paid_status_1").show();
|
||||||
$("#paid_status_2").show();
|
$("#paid_status_2").show();
|
||||||
|
$("#montant_reference").show(); // ✅ Afficher le montant de référence
|
||||||
|
|
||||||
var amount1 = parseFloat($('#payment_amount_1').val()) || 0;
|
var amount1 = parseFloat($('#payment_amount_1').val()) || 0;
|
||||||
var amount2 = netAmount - amount1;
|
var montantTotal = getMontantTotal();
|
||||||
$('#payment_amount_2').val(amount2);
|
var amount2 = montantTotal - amount1;
|
||||||
|
$('#payment_amount_2').val(amount2.toFixed(2));
|
||||||
} else {
|
} else {
|
||||||
$("#paid_status_1").show();
|
$("#paid_status_1").show();
|
||||||
$("#paid_status_2").hide();
|
$("#paid_status_2").hide();
|
||||||
|
$("#montant_reference").hide(); // ✅ Cacher le montant de référence
|
||||||
$('#payment_mode_2').val('');
|
$('#payment_mode_2').val('');
|
||||||
|
|
||||||
|
// ✅ Remplir tranche 1 avec le montant total
|
||||||
|
var montantTotal = getMontantTotal();
|
||||||
|
$('#payment_amount_1').val(montantTotal.toFixed(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,59 +367,66 @@
|
|||||||
updateMontantTranches();
|
updateMontantTranches();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Recalculer tranche 2 quand tranche 1 change
|
||||||
$('#payment_amount_1').on("input", function() {
|
$('#payment_amount_1').on("input", function() {
|
||||||
var amount1 = parseFloat($(this).val()) || 0;
|
var amount1 = parseFloat($(this).val()) || 0;
|
||||||
var amount2 = netAmount - amount1;
|
var montantTotal = getMontantTotal();
|
||||||
$('#payment_amount_2').val(amount2);
|
var amount2 = montantTotal - amount1;
|
||||||
|
|
||||||
|
if (amount2 < 0) amount2 = 0;
|
||||||
|
|
||||||
|
$('#payment_amount_2').val(amount2.toFixed(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
addPaymentTranche(paymentTranche);
|
addPaymentTranche(paymentTranche);
|
||||||
|
|
||||||
$("#add_row").unbind('click').bind('click', function() {
|
$("#add_row").unbind('click').bind('click', function() {
|
||||||
var table = $("#product_info_table");
|
var table = $("#product_info_table");
|
||||||
var count_table_tbody_tr = $("#product_info_table tbody tr").length;
|
var count_table_tbody_tr = $("#product_info_table tbody tr").length;
|
||||||
var row_id = count_table_tbody_tr + 1;
|
var row_id = count_table_tbody_tr + 1;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: base_url + '/orders/getTableProductRow/',
|
url: base_url + '/orders/getTableProductRow/',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
var html = '<tr id="row_' + row_id + '">' +
|
var html = '<tr id="row_' + row_id + '">' +
|
||||||
'<td>' +
|
'<td>' +
|
||||||
'<select class="form-control select_group product" data-row-id="' + row_id + '" id="product_' + row_id + '" name="product[]" style="width:100%;" onchange="getProductData(' + row_id + ')">' +
|
'<select class="form-control select_group product" data-row-id="' + row_id + '" id="product_' + row_id + '" name="product[]" style="width:100%;" onchange="getProductData(' + row_id + ')">' +
|
||||||
'<option value=""></option>';
|
'<option value=""></option>';
|
||||||
|
|
||||||
$.each(response, function(index, value) {
|
$.each(response, function(index, value) {
|
||||||
var displayText = value.sku + ' | ' + value.name;
|
var displayText = value.sku + ' | ' + value.name;
|
||||||
if (value.numero_de_moteur) {
|
if (value.numero_de_moteur) {
|
||||||
displayText += ' | ' + value.numero_de_moteur;
|
displayText += ' | ' + value.numero_de_moteur;
|
||||||
}
|
|
||||||
// ✅ Ne plus afficher la puissance
|
|
||||||
html += '<option value="' + value.id + '">' + displayText + '</option>';
|
|
||||||
});
|
|
||||||
|
|
||||||
html += '</select>' +
|
|
||||||
'</td>' +
|
|
||||||
// ✅ Colonne puissance visible et modifiable
|
|
||||||
'<td><input type="text" name="puissance[]" id="puissance_' + row_id + '" class="form-control" placeholder="Puissance" autocomplete="off" value="1"></td>' +
|
|
||||||
'<td><input type="text" name="rate[]" id="rate_' + row_id + '" class="form-control numeric-input" disabled min="0"><input type="hidden" name="rate_value[]" id="rate_value_' + row_id + '" class="form-control"><input type="hidden" id="min_price_' + row_id + '" name="min_price[]" value=""></td>' +
|
|
||||||
'<td><input type="text" name="amount[]" id="amount_' + row_id + '" class="form-control numeric-input" disabled min="0"><input type="hidden" name="amount_value[]" id="amount_value_' + row_id + '" class="form-control"></td>' +
|
|
||||||
'<td><button type="button" class="btn btn-default" onclick="removeRow(\'' + row_id + '\')"><i class="fa fa-close"></i></button></td>' +
|
|
||||||
'</tr>';
|
|
||||||
|
|
||||||
if (count_table_tbody_tr >= 1) {
|
|
||||||
$("#product_info_table tbody tr:last").after(html);
|
|
||||||
} else {
|
|
||||||
$("#product_info_table tbody").html(html);
|
|
||||||
}
|
}
|
||||||
|
if (value.puissance) {
|
||||||
|
displayText += ' | ' + value.puissance;
|
||||||
|
}
|
||||||
|
html += '<option value="' + value.id + '">' + displayText + '</option>';
|
||||||
|
});
|
||||||
|
|
||||||
$(".product").select2();
|
html += '</select>' +
|
||||||
|
'</td>' +
|
||||||
|
'<td><input type="text" name="puissance[]" id="puissance_' + row_id + '" class="form-control" placeholder="Puissance" autocomplete="off"></td>' +
|
||||||
|
'<td><input type="text" name="rate[]" id="rate_' + row_id + '" class="form-control" disabled><input type="hidden" name="rate_value[]" id="rate_value_' + row_id + '" class="form-control"></td>' +
|
||||||
|
'<td><input type="text" name="amount[]" id="amount_' + row_id + '" class="form-control" disabled><input type="hidden" name="amount_value[]" id="amount_value_' + row_id + '" class="form-control"></td>' +
|
||||||
|
'<td><button type="button" class="btn btn-default" onclick="removeRow(\'' + row_id + '\')"><i class="fa fa-close"></i></button></td>' +
|
||||||
|
'</tr>';
|
||||||
|
|
||||||
|
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) {
|
function getTotal(row = null) {
|
||||||
if (row) {
|
if (row) {
|
||||||
@ -419,52 +442,33 @@
|
|||||||
|
|
||||||
function getProductData(row_id) {
|
function getProductData(row_id) {
|
||||||
var product_id = $("#product_" + row_id).val();
|
var product_id = $("#product_" + row_id).val();
|
||||||
|
|
||||||
if (product_id == "") {
|
if (product_id == "") {
|
||||||
$("#rate_" + row_id).val("");
|
$("#rate_" + row_id).val("");
|
||||||
$("#rate_value_" + row_id).val("");
|
$("#rate_value_" + row_id).val("");
|
||||||
$("#min_price_" + row_id).val("");
|
$("#puissance_" + row_id).val("");
|
||||||
$("#puissance_" + row_id).val("1"); // ✅ Réinitialiser à 1
|
$("#amount_" + row_id).val("");
|
||||||
$("#amount_" + row_id).val("");
|
$("#amount_value_" + row_id).val("");
|
||||||
$("#amount_value_" + row_id).val("");
|
|
||||||
} else {
|
} else {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: base_url + 'orders/getProductValueById',
|
url: base_url + 'orders/getProductValueById',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: { product_id: product_id },
|
data: { product_id: product_id },
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
console.log('✅ Response:', response); // Debug
|
$("#rate_" + row_id).val(response.prix_vente);
|
||||||
|
$("#rate_value_" + row_id).val(response.prix_vente);
|
||||||
|
|
||||||
var prixVente = parseFloat(response.prix_vente) || 0;
|
$("#puissance_" + row_id).val(response.puissance || '');
|
||||||
var prixMinimal = parseFloat(response.prix_minimal) || 0;
|
|
||||||
|
|
||||||
if (prixVente < 0) prixVente = 0;
|
var total = Number(response.prix_vente);
|
||||||
if (prixMinimal < 0) prixMinimal = 0;
|
total = total.toFixed(2);
|
||||||
|
$("#amount_" + row_id).val(total);
|
||||||
$("#rate_" + row_id).val(prixVente);
|
$("#amount_value_" + row_id).val(total);
|
||||||
$("#rate_value_" + row_id).val(prixVente);
|
subAmount();
|
||||||
$("#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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function subAmount() {
|
function subAmount() {
|
||||||
var service_charge = <?php echo ($company_data['service_charge_value'] > 0) ? $company_data['service_charge_value'] : 0; ?>;
|
var service_charge = <?php echo ($company_data['service_charge_value'] > 0) ? $company_data['service_charge_value'] : 0; ?>;
|
||||||
@ -515,6 +519,7 @@
|
|||||||
$("#remaining_value").val(remaning.toFixed(2));
|
$("#remaining_value").val(remaning.toFixed(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Mettre à jour les tranches après chaque calcul
|
||||||
updateMontantTranches();
|
updateMontantTranches();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,12 +538,14 @@
|
|||||||
subAmount();
|
subAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Fonction pour obtenir le montant pour les tranches
|
||||||
function getMontantPourTranches() {
|
function getMontantPourTranches() {
|
||||||
var discount = parseFloat($("#discount").val()) || 0;
|
var discount = parseFloat($("#discount").val()) || 0;
|
||||||
var grossAmount = parseFloat($("#gross_amount_value").val()) || 0;
|
var grossAmount = parseFloat($("#gross_amount_value").val()) || 0;
|
||||||
return discount > 0 ? discount : grossAmount;
|
return discount > 0 ? discount : grossAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Mettre à jour l'affichage du montant de référence
|
||||||
function updateMontantTranches() {
|
function updateMontantTranches() {
|
||||||
var montant = getMontantPourTranches();
|
var montant = getMontantPourTranches();
|
||||||
var discount = parseFloat($("#discount").val()) || 0;
|
var discount = parseFloat($("#discount").val()) || 0;
|
||||||
@ -552,11 +559,17 @@
|
|||||||
$("#montant_source_label").text("(Montant brut)");
|
$("#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();
|
calculerTranche2();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Calculer tranche 2 basé sur le montant total actuel
|
||||||
function calculerTranche2() {
|
function calculerTranche2() {
|
||||||
var montantTotal = getMontantPourTranches();
|
var montantTotal = getMontantPourTranches();
|
||||||
var tranche1 = parseFloat($("#payment_amount_1").val()) || 0;
|
var tranche1 = parseFloat($("#payment_amount_1").val()) || 0;
|
||||||
@ -565,12 +578,17 @@
|
|||||||
$("#payment_amount_2").val(tranche2.toFixed(2));
|
$("#payment_amount_2").val(tranche2.toFixed(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Écouter les changements de discount
|
||||||
$("#discount").on('keyup', function() {
|
$("#discount").on('keyup', function() {
|
||||||
updateMontantTranches();
|
subAmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ✅ Initialisation au chargement
|
||||||
const net_amount_value = document.getElementById('net_amount_value');
|
const net_amount_value = document.getElementById('net_amount_value');
|
||||||
const net_amount = document.getElementById('net_amount');
|
const net_amount = document.getElementById('net_amount');
|
||||||
const payment_amount_1 = document.getElementById('payment_amount_1');
|
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;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -106,7 +106,7 @@
|
|||||||
<div class="small-box" style="background-color:#A9A9A9;">
|
<div class="small-box" style="background-color:#A9A9A9;">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<h3 id="total_espece"><?php echo number_format($total_espece, 0, '.', ' '); ?></h3>
|
<h3 id="total_espece"><?php echo number_format($total_espece, 0, '.', ' '); ?></h3>
|
||||||
<p>Total en Caisse</p>
|
<p>Total en Espèce</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<i class="ion ion-cash"></i>
|
<i class="ion ion-cash"></i>
|
||||||
@ -232,78 +232,171 @@
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Inclure SweetAlert2 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("#motos").select2();
|
$("#motos").select2();
|
||||||
$("#motos_edit").select2();
|
$("#motos_edit").select2();
|
||||||
$('.mecanic').select2();
|
$('.mecanic').select2();
|
||||||
$("#recouvrement").addClass('active');
|
$("#recouvrement").addClass('active');
|
||||||
|
|
||||||
// Check if the URL contains the _ parameter
|
// Check if the URL contains the _ parameter
|
||||||
if (window.location.search.startsWith("?_=")) {
|
if (window.location.search.startsWith("?_=")) {
|
||||||
window.location.href = window.location.origin + window.location.pathname;
|
window.location.href = window.location.origin + window.location.pathname;
|
||||||
}
|
}
|
||||||
// datatable-fr.js
|
|
||||||
$.extend(true, $.fn.dataTable.defaults, {
|
// Configuration DataTable en français
|
||||||
|
$.extend(true, $.fn.dataTable.defaults, {
|
||||||
language: {
|
language: {
|
||||||
sProcessing: "Traitement en cours...",
|
sProcessing: "Traitement en cours...",
|
||||||
sSearch: "Rechercher :",
|
sSearch: "Rechercher :",
|
||||||
sLengthMenu: "Afficher _MENU_ éléments",
|
sLengthMenu: "Afficher _MENU_ éléments",
|
||||||
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||||
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
||||||
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||||
sLoadingRecords: "Chargement en cours...",
|
sLoadingRecords: "Chargement en cours...",
|
||||||
sZeroRecords: "Aucun élément à afficher",
|
sZeroRecords: "Aucun élément à afficher",
|
||||||
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
||||||
oPaginate: {
|
oPaginate: {
|
||||||
sFirst: "Premier",
|
sFirst: "Premier",
|
||||||
sPrevious: "Précédent",
|
sPrevious: "Précédent",
|
||||||
sNext: "Suivant",
|
sNext: "Suivant",
|
||||||
sLast: "Dernier"
|
sLast: "Dernier"
|
||||||
},
|
},
|
||||||
oAria: {
|
oAria: {
|
||||||
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
||||||
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
sSortDescending: ": activer pour trier la colonne par ordre décroissant"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialisation du DataTable
|
||||||
|
manageTable = $('#manageTable').DataTable({
|
||||||
|
'ajax': '<?= base_url('recouvrement/fetchRecouvrementData') ?>',
|
||||||
|
'order': [],
|
||||||
|
'columnDefs': [{
|
||||||
|
targets: 1,
|
||||||
|
className: 'text-right rowmontant'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: 4,
|
||||||
|
className: 'rowtype'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: 5,
|
||||||
|
className: 'rowdestination'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
manageTable = $('#manageTable').DataTable({
|
// ====== FONCTION DE RAFRAÎCHISSEMENT AUTOMATIQUE ======
|
||||||
'ajax': '<?= base_url('recouvrement/fetchRecouvrementData') ?>',
|
function refreshData() {
|
||||||
'order': [],
|
// Recharger le DataTable
|
||||||
'columnDefs': [{
|
manageTable.ajax.reload(null, false);
|
||||||
targets: 1,
|
|
||||||
className: 'text-right rowmontant'
|
// Recharger les totaux
|
||||||
},
|
fetchTotalData();
|
||||||
{
|
}
|
||||||
targets: 4,
|
|
||||||
className: 'rowtype'
|
// Rafraîchir automatiquement toutes les 5 secondes
|
||||||
},
|
setInterval(refreshData, 5000);
|
||||||
{
|
|
||||||
targets: 5,
|
})
|
||||||
className: 'rowdestination'
|
|
||||||
|
// ====== CRÉATION DE RECOUVREMENT ======
|
||||||
|
$("#create_form").unbind('submit').on('submit', function() {
|
||||||
|
var form = $(this);
|
||||||
|
|
||||||
|
// Supprimer les messages d'erreur
|
||||||
|
$(".text-danger").remove();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: form.attr('action'),
|
||||||
|
type: form.attr('method'),
|
||||||
|
data: form.serialize(),
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success === true) {
|
||||||
|
// Recharger immédiatement les données
|
||||||
|
manageTable.ajax.reload(null, false);
|
||||||
|
fetchTotalData();
|
||||||
|
|
||||||
|
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||||
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
|
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
||||||
|
'</div>');
|
||||||
|
|
||||||
|
// Fermer le modal
|
||||||
|
$("#createModal").modal('hide');
|
||||||
|
|
||||||
|
// Réinitialiser le formulaire
|
||||||
|
$("#create_form")[0].reset();
|
||||||
|
$("#create_form .form-group").removeClass('has-error').removeClass('has-success');
|
||||||
|
|
||||||
|
// Faire disparaître le message après 3 secondes
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#messages").fadeOut('slow', function() {
|
||||||
|
$(this).html('').show();
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Afficher une alerte SweetAlert pour les erreurs de solde
|
||||||
|
if (response.messages === 'Recouvrement impossible : solde insuffisant pour ce type de transaction') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Recouvrement impossible',
|
||||||
|
text: 'Le solde est insuffisant pour effectuer ce recouvrement. Veuillez vérifier les totaux disponibles.',
|
||||||
|
confirmButtonColor: '#3085d6',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
} else if (response.messages instanceof Object) {
|
||||||
|
$.each(response.messages, function(index, value) {
|
||||||
|
var id = $("#" + index);
|
||||||
|
id.closest('.form-group')
|
||||||
|
.removeClass('has-error')
|
||||||
|
.removeClass('has-success')
|
||||||
|
.addClass(value.length > 0 ? 'has-error' : 'has-success');
|
||||||
|
id.after(value);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||||
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
|
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
||||||
|
'</div>');
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
});
|
},
|
||||||
|
error: function() {
|
||||||
|
$("#messages").html('<div class="alert alert-danger alert-dismissible" role="alert">' +
|
||||||
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
|
'<strong>Erreur!</strong> Une erreur est survenue lors de la création.' +
|
||||||
|
'</div>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
})
|
return false;
|
||||||
|
});
|
||||||
$("#create_form").unbind('submit').on('submit', function() {
|
// ====== SUPPRESSION DE RECOUVREMENT ======
|
||||||
|
function removeFunc(id) {
|
||||||
|
if (id) {
|
||||||
|
$("#removeForm").unbind('submit').on('submit', function() {
|
||||||
var form = $(this);
|
var form = $(this);
|
||||||
|
|
||||||
// remove the text-danger
|
|
||||||
$(".text-danger").remove();
|
$(".text-danger").remove();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: form.attr('action'),
|
url: form.attr('action'),
|
||||||
type: form.attr('method'),
|
type: form.attr('method'),
|
||||||
data: form.serialize(), // /converting the form data into array and sending it to server
|
data: {
|
||||||
|
recouvrement_id: id
|
||||||
|
},
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
// Recharger immédiatement les données
|
||||||
manageTable.ajax.reload(null, false);
|
manageTable.ajax.reload(null, false);
|
||||||
|
fetchTotalData();
|
||||||
|
|
||||||
if (response.success === true) {
|
if (response.success === true) {
|
||||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||||
@ -311,170 +404,157 @@ $.extend(true, $.fn.dataTable.defaults, {
|
|||||||
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
||||||
'</div>');
|
'</div>');
|
||||||
|
|
||||||
|
$("#removeModal").modal('hide');
|
||||||
|
|
||||||
// hide the modal
|
// Faire disparaître le message après 3 secondes
|
||||||
$("#createModal").modal('hide');
|
setTimeout(function() {
|
||||||
|
$("#messages").fadeOut('slow', function() {
|
||||||
// reset the form
|
$(this).html('').show();
|
||||||
$("#create_form")[0].reset();
|
});
|
||||||
$("#create_form .form-group").removeClass('has-error').removeClass('has-success');
|
}, 3000);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||||
if (response.messages instanceof Object) {
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
$.each(response.messages, function(index, value) {
|
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
||||||
var id = $("#" + index);
|
'</div>');
|
||||||
|
|
||||||
id.closest('.form-group')
|
|
||||||
.removeClass('has-error')
|
|
||||||
.removeClass('has-success')
|
|
||||||
.addClass(value.length > 0 ? 'has-error' : 'has-success');
|
|
||||||
|
|
||||||
id.after(value);
|
|
||||||
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
|
||||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
|
||||||
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
|
||||||
'</div>');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$("#messages").html('<div class="alert alert-danger alert-dismissible" role="alert">' +
|
||||||
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
|
'<strong>Erreur!</strong> Une erreur est survenue lors de la suppression.' +
|
||||||
|
'</div>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function removeFunc(id) {
|
// ====== MODIFICATION DE RECOUVREMENT ======
|
||||||
if (id) {
|
function editFunc(id) {
|
||||||
$("#removeForm").on('submit', function() {
|
$.ajax({
|
||||||
|
url: '<?= base_url('recouvrement/fetchRecouvrementSingle') ?>/' + id,
|
||||||
|
type: 'post',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
$("#recouvrement_montant_edit").val(response.recouvrement_montant).change();
|
||||||
|
$("#recouvrement_date_edit").val(response.recouvrement_date).change();
|
||||||
|
|
||||||
|
// Soumettre le formulaire de modification
|
||||||
|
$("#update_form").unbind('submit').bind('submit', function() {
|
||||||
var form = $(this);
|
var form = $(this);
|
||||||
|
|
||||||
// remove the text-danger
|
|
||||||
$(".text-danger").remove();
|
$(".text-danger").remove();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: form.attr('action'),
|
url: form.attr('action').replace(/\/?$/, '/') + id,
|
||||||
type: form.attr('method'),
|
type: form.attr('method'),
|
||||||
data: {
|
data: form.serialize(),
|
||||||
recouvrement_id: id
|
|
||||||
},
|
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
// Recharger immédiatement les données
|
||||||
manageTable.ajax.reload(null, false);
|
manageTable.ajax.reload(null, false);
|
||||||
|
fetchTotalData();
|
||||||
|
|
||||||
|
$("#updateModal").modal('hide');
|
||||||
|
|
||||||
if (response.success === true) {
|
if (response.success === true) {
|
||||||
|
|
||||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
||||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
||||||
'</div>');
|
'</div>');
|
||||||
|
|
||||||
// hide the modal
|
$("#updateForm .form-group").removeClass('has-error').removeClass('has-success');
|
||||||
$("#removeModal").modal('hide');
|
|
||||||
|
// Faire disparaître le message après 3 secondes
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#messages").fadeOut('slow', function() {
|
||||||
|
$(this).html('').show();
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (response.messages instanceof Object) {
|
||||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
$.each(response.messages, function(index, value) {
|
||||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
var id = $("#" + index);
|
||||||
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
id.closest('.form-group')
|
||||||
'</div>');
|
.removeClass('has-error')
|
||||||
|
.removeClass('has-success')
|
||||||
|
.addClass(value.length > 0 ? 'has-error' : 'has-success');
|
||||||
|
id.after(value);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
||||||
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
|
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
||||||
|
'</div>');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$("#updateModal").modal('hide');
|
||||||
|
$("#messages").html('<div class="alert alert-danger alert-dismissible" role="alert">' +
|
||||||
|
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
||||||
|
'<strong>Erreur!</strong> Une erreur est survenue lors de la modification.' +
|
||||||
|
'</div>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// update function
|
// ====== MISE À JOUR DES TOTAUX ======
|
||||||
function editFunc(id) {
|
const endpoint = '<?= base_url('recouvrement/fetchTotalData') ?>';
|
||||||
$.ajax({
|
|
||||||
url: '<?= base_url('recouvrement/fetchRecouvrementSingle') ?>/' + id,
|
|
||||||
type: 'post',
|
|
||||||
dataType: 'json',
|
|
||||||
success: function(response) {
|
|
||||||
$("#recouvrement_montant_edit").val(response.recouvrement_montant).change();
|
|
||||||
$("#recouvrement_date_edit").val(response.recouvrement_date).change();
|
|
||||||
// submit the edit from
|
|
||||||
$("#update_form").unbind('submit').bind('submit', function() {
|
|
||||||
var form = $(this);
|
|
||||||
|
|
||||||
// remove the text-danger
|
|
||||||
$(".text-danger").remove();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: form.attr('action').replace(/\/?$/, '/') + id,
|
|
||||||
type: form.attr('method'),
|
|
||||||
data: form.serialize(), // /converting the form data into array and sending it to server
|
|
||||||
dataType: 'json',
|
|
||||||
success: function(response) {
|
|
||||||
manageTable.ajax.reload(null, false);
|
|
||||||
$("#updateModal").modal('hide');
|
|
||||||
if (response.success === true) {
|
|
||||||
$("#messages").html('<div class="alert alert-success alert-dismissible" role="alert">' +
|
|
||||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
|
||||||
'<strong> <span class="glyphicon glyphicon-ok-sign"></span> </strong>' + response.messages +
|
|
||||||
'</div>');
|
|
||||||
|
|
||||||
|
|
||||||
// reset the form
|
|
||||||
$("#updateForm .form-group").removeClass('has-error').removeClass('has-success');
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$("#updateModal").modal('hide');
|
|
||||||
if (response.messages instanceof Object) {
|
|
||||||
$.each(response.messages, function(index, value) {
|
|
||||||
var id = $("#" + index);
|
|
||||||
|
|
||||||
id.closest('.form-group')
|
|
||||||
.removeClass('has-error')
|
|
||||||
.removeClass('has-success')
|
|
||||||
.addClass(value.length > 0 ? 'has-error' : 'has-success');
|
|
||||||
|
|
||||||
id.after(value);
|
|
||||||
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$("#updateModal").modal('hide');
|
|
||||||
$("#messages").html('<div class="alert alert-warning alert-dismissible" role="alert">' +
|
|
||||||
'<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' +
|
|
||||||
'<strong> <span class="glyphicon glyphicon-exclamation-sign"></span> </strong>' + response.messages +
|
|
||||||
'</div>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
async function fetchTotalData() {
|
||||||
|
try {
|
||||||
|
let res = await fetch(endpoint, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!res.ok) throw new Error(res.status);
|
||||||
|
let data = await res.json();
|
||||||
|
|
||||||
|
// Mettre à jour les totaux avec animation
|
||||||
|
updateValueWithAnimation('#total', data.total);
|
||||||
|
updateValueWithAnimation('#total_mvola', data.total_mvola);
|
||||||
|
updateValueWithAnimation('#total_espece', data.total_espece);
|
||||||
|
updateValueWithAnimation('#total_banque', data.total_virement_bancaire);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('fetchTotalData error', e);
|
||||||
}
|
}
|
||||||
</script>
|
}
|
||||||
<script>
|
|
||||||
const endpoint = '<?= base_url('recouvrement/fetchTotalData') ?>';
|
// Fonction pour mettre à jour une valeur avec animation
|
||||||
async function fetchTotalData() {
|
function updateValueWithAnimation(selector, newValue) {
|
||||||
try {
|
const element = $(selector);
|
||||||
let res = await fetch(endpoint, { headers:{'Content-Type':'application/json'} });
|
const currentText = element.text().replace(/\s/g, '');
|
||||||
if (!res.ok) throw new Error(res.status);
|
const currentValue = parseFloat(currentText) || 0;
|
||||||
let data = await res.json();
|
const formattedNewValue = formatNum(newValue);
|
||||||
$('#total').text(formatNum(data.total));
|
|
||||||
$('#total_mvola').text(formatNum(data.total_mvola));
|
// Si la valeur a changé, ajouter une animation
|
||||||
$('#total_caisse').text(formatNum(data.total_espece));
|
if (currentText !== formattedNewValue.replace(/\s/g, '')) {
|
||||||
$('#total_banque').text(formatNum(data.total_virement_bancaire));
|
element.fadeOut(200, function() {
|
||||||
} catch(e) {
|
$(this).text(formattedNewValue).fadeIn(200);
|
||||||
console.error('fetchTotalData error', e);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function formatNum(n){
|
|
||||||
n = parseFloat(n)||0; return n.toLocaleString('fr-FR').replace(/\s/g,' ');
|
function formatNum(n) {
|
||||||
}
|
n = parseFloat(n) || 0;
|
||||||
fetchTotalData();
|
return n.toLocaleString('fr-FR').replace(/\s/g, ' ');
|
||||||
setInterval(fetchTotalData, 1000);
|
}
|
||||||
|
|
||||||
|
// Charger les totaux au démarrage
|
||||||
|
fetchTotalData();
|
||||||
|
|
||||||
|
// Rafraîchir les totaux toutes les 3 secondes
|
||||||
|
setInterval(fetchTotalData, 3000);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -288,22 +288,44 @@ input[readonly], select[disabled], textarea[readonly] {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="motif_select" class="form-label">Motif</label>
|
<label for="motif_select" class="form-label">Motif</label>
|
||||||
<select class="form-control" id="motif_select" name="motif_select" required>
|
|
||||||
<?php
|
<!-- Select avec liste + option "Autre" -->
|
||||||
$session = session();
|
<select class="form-control" id="motif_select_dropdown" name="motif_select_dropdown">
|
||||||
$users = $session->get('user');
|
<?php
|
||||||
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
|
$session = session();
|
||||||
$isCaissier = $users['group_name'] == "Caissière";
|
$users = $session->get('user');
|
||||||
$options = $isAdmin ? $admin_options : $caissier_options;
|
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
|
||||||
foreach ($options as $option) {
|
$isCaissier = $users['group_name'] == "Caissière";
|
||||||
echo "<option value=\"" . htmlspecialchars($option) . "\">" . htmlspecialchars($option) . "</option>\n";
|
$options = $isAdmin ? $admin_options : $caissier_options;
|
||||||
}
|
|
||||||
?>
|
echo "<option value=''>Veuillez sélectionner une raison</option>\n";
|
||||||
<option value="" selected>Veuillez sélectionner une raison</option>
|
|
||||||
</select>
|
foreach ($options as $option) {
|
||||||
</div>
|
echo "<option value=\"" . htmlspecialchars($option) . "\">" . htmlspecialchars($option) . "</option>\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<option value="AUTRE">✏️ Autre (saisir manuellement)</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Input caché qui s'affiche quand on choisit "Autre" -->
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="motif_select_custom"
|
||||||
|
name="motif_select_custom"
|
||||||
|
placeholder="Saisissez votre motif personnalisé"
|
||||||
|
style="display: none; margin-top: 10px;"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- Champ caché pour envoyer la valeur finale -->
|
||||||
|
<input type="hidden" id="motif_select" name="motif_select" required>
|
||||||
|
|
||||||
|
<small class="text-muted" style="display: block; margin-top: 5px;">
|
||||||
|
<i class="fa fa-info-circle"></i> Sélectionnez un motif prédéfini ou choisissez "Autre" pour saisir librement
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="mode_paiement" class="form-label">Mode de paiement</label>
|
<label for="mode_paiement" class="form-label">Mode de paiement</label>
|
||||||
<select class="form-control" id="mode_paiement" name="mode_paiement" required>
|
<select class="form-control" id="mode_paiement" name="mode_paiement" required>
|
||||||
@ -573,24 +595,36 @@ input[readonly], select[disabled], textarea[readonly] {
|
|||||||
|
|
||||||
<!-- Motif et Mode de paiement -->
|
<!-- Motif et Mode de paiement -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="motif_select_edit" class="form-label"><i class="fa fa-lock text-muted"></i> Motif</label>
|
<label for="motif_select_edit" class="form-label">
|
||||||
<select class="form-control" id="motif_select_edit" name="motif_select_edit" required disabled style="background-color: #e9ecef; cursor: not-allowed;">
|
<i class="fa fa-lock text-muted"></i> Motif
|
||||||
<?php
|
</label>
|
||||||
$session = session();
|
|
||||||
$users = $session->get('user');
|
<!-- Select désactivé pour l'affichage -->
|
||||||
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
|
<select class="form-control" id="motif_select_edit_dropdown" disabled style="background-color: #e9ecef; cursor: not-allowed;">
|
||||||
$options = $isAdmin ? $admin_options : $caissier_options;
|
<?php
|
||||||
foreach ($options as $option) {
|
$session = session();
|
||||||
echo "<option value=\"" . htmlspecialchars($option) . "\">" . htmlspecialchars($option) . "</option>\n";
|
$users = $session->get('user');
|
||||||
}
|
$isAdmin = $users['group_name'] == "Direction" || $users['group_name'] == "Conseil";
|
||||||
?>
|
$options = $isAdmin ? $admin_options : $caissier_options;
|
||||||
<option value="" selected>Veuillez sélectionner une raison</option>
|
|
||||||
</select>
|
echo "<option value=''>Veuillez sélectionner une raison</option>\n";
|
||||||
<!-- Champ caché pour envoyer la valeur du motif -->
|
|
||||||
<input type="hidden" id="motif_select_edit_hidden" name="motif_select_edit">
|
foreach ($options as $option) {
|
||||||
</div>
|
echo "<option value=\"" . htmlspecialchars($option) . "\">" . htmlspecialchars($option) . "</option>\n";
|
||||||
<div class="col-md-6 mb-3">
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Champ caché pour envoyer la valeur (car le select est disabled) -->
|
||||||
|
<input type="hidden" id="motif_select_edit" name="motif_select_edit">
|
||||||
|
|
||||||
|
<small class="motif-help-text">
|
||||||
|
<i class="fa fa-info-circle"></i> Le motif ne peut pas être modifié
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
<label for="mode_paiement_edit" class="form-label"><i class="fa fa-edit text-primary"></i> Mode de paiement</label>
|
<label for="mode_paiement_edit" class="form-label"><i class="fa fa-edit text-primary"></i> Mode de paiement</label>
|
||||||
<select class="form-control" id="mode_paiement_edit" name="mode_paiement_edit" required>
|
<select class="form-control" id="mode_paiement_edit" name="mode_paiement_edit" required>
|
||||||
<option value="En espèce">En espèce</option>
|
<option value="En espèce">En espèce</option>
|
||||||
@ -745,6 +779,7 @@ input[readonly], select[disabled], textarea[readonly] {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// ============================================
|
// ============================================
|
||||||
// CONVERSION NOMBRE EN LETTRES (FRANÇAIS)
|
// CONVERSION NOMBRE EN LETTRES (FRANÇAIS)
|
||||||
@ -891,6 +926,31 @@ $(document).ready(function() {
|
|||||||
$('#date_demande').val(today);
|
$('#date_demande').val(today);
|
||||||
$('#date_fiche').val(today);
|
$('#date_fiche').val(today);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// ✅ GESTION DU CHAMP MOTIF SELECT MODIFIABLE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Gestion du select motif avec option personnalisée
|
||||||
|
$('#motif_select_dropdown').on('change', function() {
|
||||||
|
const selectedValue = $(this).val();
|
||||||
|
|
||||||
|
if (selectedValue === 'AUTRE') {
|
||||||
|
// Afficher le champ de saisie libre
|
||||||
|
$('#motif_select_custom').show().focus();
|
||||||
|
$('#motif_select').val(''); // Vider le champ caché
|
||||||
|
} else {
|
||||||
|
// Cacher le champ de saisie et utiliser la valeur sélectionnée
|
||||||
|
$('#motif_select_custom').hide().val('');
|
||||||
|
$('#motif_select').val(selectedValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mettre à jour le champ caché quand on tape dans le champ personnalisé
|
||||||
|
$('#motif_select_custom').on('input', function() {
|
||||||
|
const customValue = $(this).val();
|
||||||
|
$('#motif_select').val(customValue);
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// GESTION DU MONTANT ET CONVERSION EN LETTRES
|
// GESTION DU MONTANT ET CONVERSION EN LETTRES
|
||||||
// ============================================
|
// ============================================
|
||||||
@ -911,8 +971,8 @@ $(document).ready(function() {
|
|||||||
const words = numberToFrenchWords(num);
|
const words = numberToFrenchWords(num);
|
||||||
montantLettreInput.val(words + ' ariary');
|
montantLettreInput.val(words + ' ariary');
|
||||||
|
|
||||||
// Gestion du basculement de formulaire
|
// ✅ Gestion du basculement de formulaire - CORRIGÉ >= 1000000
|
||||||
if (num > 1000000) {
|
if (num >= 1000000) {
|
||||||
$('#nom_demandeur').val($('#nom').val());
|
$('#nom_demandeur').val($('#nom').val());
|
||||||
$('#fonction_demandeur').val($('#fonction').val());
|
$('#fonction_demandeur').val($('#fonction').val());
|
||||||
$('#montant_estime').val(this.value);
|
$('#montant_estime').val(this.value);
|
||||||
@ -930,7 +990,7 @@ $(document).ready(function() {
|
|||||||
$('#back-to-im1').on('click', function() {
|
$('#back-to-im1').on('click', function() {
|
||||||
$('#form-im23').hide();
|
$('#form-im23').hide();
|
||||||
$('#form-im1').show();
|
$('#form-im1').show();
|
||||||
montantInput.val('500000');
|
montantInput.val('999999');
|
||||||
montantInput.trigger('input');
|
montantInput.trigger('input');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -944,7 +1004,7 @@ $(document).ready(function() {
|
|||||||
montantInput.trigger('input');
|
montantInput.trigger('input');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Réinitialisation du modal
|
// ✅ Réinitialisation du modal - AVEC RÉINITIALISATION DU MOTIF
|
||||||
$('#createModal').on('hidden.bs.modal', function () {
|
$('#createModal').on('hidden.bs.modal', function () {
|
||||||
$('#form-im1').show();
|
$('#form-im1').show();
|
||||||
$('#form-im23').hide();
|
$('#form-im23').hide();
|
||||||
@ -952,6 +1012,11 @@ $(document).ready(function() {
|
|||||||
$('#date_demande').val(today);
|
$('#date_demande').val(today);
|
||||||
$('#date_fiche').val(today);
|
$('#date_fiche').val(today);
|
||||||
montantLettreInput.val('');
|
montantLettreInput.val('');
|
||||||
|
|
||||||
|
// ✅ Réinitialiser les champs motif
|
||||||
|
$('#motif_select_dropdown').val('');
|
||||||
|
$('#motif_select_custom').hide().val('');
|
||||||
|
$('#motif_select').val('');
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
@ -1006,12 +1071,26 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// CRÉATION AVEC SWEETALERT2 - CORRIGÉ
|
// ✅ CRÉATION AVEC SWEETALERT2 + VALIDATION MOTIF
|
||||||
// ============================================
|
// ============================================
|
||||||
$("#create_form_sortie").unbind('submit').on('submit', function(e) {
|
$("#create_form_sortie").unbind('submit').on('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
$(".text-danger").remove();
|
$(".text-danger").remove();
|
||||||
|
|
||||||
|
// ✅ Validation du motif
|
||||||
|
const motifValue = $('#motif_select').val();
|
||||||
|
|
||||||
|
if (!motifValue || motifValue.trim() === '') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'warning',
|
||||||
|
title: 'Motif requis',
|
||||||
|
text: 'Veuillez sélectionner ou saisir un motif',
|
||||||
|
confirmButtonColor: '#f39c12'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var formData = new FormData(this);
|
var formData = new FormData(this);
|
||||||
|
|
||||||
// CORRECTION : Nettoyer le montant des espaces avant validation
|
// CORRECTION : Nettoyer le montant des espaces avant validation
|
||||||
@ -1071,6 +1150,11 @@ $(document).ready(function() {
|
|||||||
$("#create_form_sortie .form-group").removeClass('has-error has-success');
|
$("#create_form_sortie .form-group").removeClass('has-error has-success');
|
||||||
$('#date_demande').val(today);
|
$('#date_demande').val(today);
|
||||||
$('#date_fiche').val(today);
|
$('#date_fiche').val(today);
|
||||||
|
|
||||||
|
// ✅ Réinitialiser les champs motif
|
||||||
|
$('#motif_select_dropdown').val('');
|
||||||
|
$('#motif_select_custom').hide().val('');
|
||||||
|
$('#motif_select').val('');
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -1133,7 +1217,7 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// MODIFICATION AVEC SWEETALERT2 - CORRIGÉ
|
// ✅ MODIFICATION AVEC SWEETALERT2 + GESTION MOTIF PERSONNALISÉ
|
||||||
// ============================================
|
// ============================================
|
||||||
window.editFunc = function(id) {
|
window.editFunc = function(id) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -1158,20 +1242,24 @@ $(document).ready(function() {
|
|||||||
const initialModePaiement = response.mode_paiement || 'En espèce';
|
const initialModePaiement = response.mode_paiement || 'En espèce';
|
||||||
$("#mode_paiement_edit").val(initialModePaiement);
|
$("#mode_paiement_edit").val(initialModePaiement);
|
||||||
|
|
||||||
// Gérer le motif
|
// ✅ Gérer le motif - AVEC SUPPORT DES MOTIFS PERSONNALISÉS
|
||||||
const motif = response.motif;
|
const motif = response.motif;
|
||||||
const motifSelect = $("#motif_select_edit");
|
const motifSelectDropdown = $("#motif_select_edit_dropdown");
|
||||||
const optionExists = motifSelect.find('option').filter(function() {
|
|
||||||
|
// Vérifier si le motif existe dans la liste
|
||||||
|
const optionExists = motifSelectDropdown.find('option').filter(function() {
|
||||||
return $(this).val().trim().toLowerCase() === motif.trim().toLowerCase();
|
return $(this).val().trim().toLowerCase() === motif.trim().toLowerCase();
|
||||||
}).length > 0;
|
}).length > 0;
|
||||||
|
|
||||||
if (optionExists) {
|
if (optionExists) {
|
||||||
motifSelect.val(motif);
|
motifSelectDropdown.val(motif);
|
||||||
} else {
|
} else {
|
||||||
motifSelect.val("");
|
// ✅ Si le motif n'existe pas, l'afficher quand même avec un indicateur
|
||||||
|
motifSelectDropdown.append('<option value="' + motif + '" selected>🔸 ' + motif + ' (personnalisé)</option>');
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#motif_select_edit_hidden").val(motif);
|
// Mettre la valeur dans le champ caché
|
||||||
|
$("#motif_select_edit").val(motif);
|
||||||
|
|
||||||
if (response.preuve_achat) {
|
if (response.preuve_achat) {
|
||||||
$("#current_preuve_text").html('<br><i class="fa fa-file"></i> Fichier actuel: ' + response.preuve_achat);
|
$("#current_preuve_text").html('<br><i class="fa fa-file"></i> Fichier actuel: ' + response.preuve_achat);
|
||||||
@ -1400,6 +1488,10 @@ $(document).ready(function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// MARQUER COMME PAYÉ
|
||||||
|
// ============================================
|
||||||
window.markAsPaidFunc = function(id_sortie) {
|
window.markAsPaidFunc = function(id_sortie) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '💰 Confirmer le paiement',
|
title: '💰 Confirmer le paiement',
|
||||||
@ -1508,3 +1600,92 @@ window.markAsPaidFunc = function(id_sortie) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
/* Style pour les champs en lecture seule */
|
||||||
|
input[readonly], select[disabled], textarea[readonly] {
|
||||||
|
background-color: #e9ecef !important;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style pour la légende */
|
||||||
|
.field-legend {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-left: 4px solid #007bff;
|
||||||
|
padding: 10px 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-legend i {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-legend .legend-item {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Effet hover sur les champs éditables */
|
||||||
|
.form-control:not([readonly]):not([disabled]):hover {
|
||||||
|
border-color: #007bff;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.15);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icône d'édition */
|
||||||
|
.fa-edit {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================ */
|
||||||
|
/* ✅ STYLES POUR LE CHAMP MOTIF MODIFIABLE */
|
||||||
|
/* ============================================ */
|
||||||
|
|
||||||
|
/* Animation pour l'apparition du champ personnalisé */
|
||||||
|
#motif_select_custom {
|
||||||
|
animation: slideDown 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style pour l'option "Autre" */
|
||||||
|
#motif_select_dropdown option[value="AUTRE"] {
|
||||||
|
background-color: #fff3cd;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #856404;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mettre en évidence le champ actif */
|
||||||
|
#motif_select_custom:focus {
|
||||||
|
border-color: #28a745;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style pour le champ personnalisé */
|
||||||
|
#motif_select_custom {
|
||||||
|
border-left: 4px solid #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style pour le texte d'aide */
|
||||||
|
.motif-help-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6c757d;
|
||||||
|
margin-top: 5px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.motif-help-text i {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user