[
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Achat équipement de sécurité" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Achat mobilier de bureau" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement salaire des collaborateurs" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Cotisation sociales" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Remboursement d'avance moto" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Payement prime ou endemnité" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement sous-traitant" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Frais de formation" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement loyer" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Frais de formation externe" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Abonnement internet" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Entretien locaux" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement fournisseur" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Remboursement de frais" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Paiement assurance" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Réparation immobilisation" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"DVD" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Déclaration fiscale - Déclaration d'impôts" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
"Enregistrement des contrats de bail au centre fiscal" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'SuperAdmin'
],
// ----- Raisons Admin -----
"Achat de matériel informatique" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Achat équipement de sécurité" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Achat mobilier de bureau" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Paiement salaire des collaborateurs" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Cotisation sociales" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Remboursement d'avance moto" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Payement prime ou endemnité" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Paiement sous-traitant" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Frais de formation" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Paiement loyer" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Frais de formation externe" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Abonnement internet" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Entretien locaux" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Paiement fournisseur" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Remboursement de frais" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Paiement assurance" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Réparation immobilisation" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"DVD" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Déclaration fiscale - Déclaration d'impôts" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
"Enregistrement des contrats de bail au centre fiscal" => [
'source_fond' => 'Budget Directionnel',
'initiateur_demande' => 'Direction'
],
// ----- Raisons Caissier -----
"Achat materiel - Réparation immobilisation" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Réparation matériel" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Maintenance équipement" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Achats de Matériaux et Fournitures" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Achat produits de nettoyage" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Achat consommable informatique" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Achat petit outillage" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Avance à un prestataire" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Payement prestataire" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Payement éléctricité" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Frais de mission - Déplacement" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Achat de carburant" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Paiement transport marchandise" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
"Achat pièces pour réparation moto" => [
'source_fond' => 'Caisse Courante',
'initiateur_demande' => 'Caissière'
],
];
private $pageTitle = 'Décaissement';
public function index()
{
$this->verifyRole('viewSortieCaisse');
$admin_options = [
"Achat de matériel informatique",
"Achat équipement de sécurité",
"Achat mobilier de bureau",
"Paiement salaire des collaborateurs",
"Cotisation sociales",
"Remboursement d'avance moto",
"Payement prime ou endemnité",
"Paiement sous-traitant",
"Frais de formation",
"Paiement loyer",
"Frais de formation externe",
"Abonnement internet",
"Entretien locaux",
"Paiement fournisseur",
"Remboursement de frais",
"Paiement assurance",
"Réparation immobilisation",
"DVD",
"Déclaration fiscale - Déclaration d'impôts",
"Enregistrement des contrats de bail au centre fiscal"
];
$caissier_options = [
"Achat materiel - Réparation immobilisation",
"Réparation matériel",
"Maintenance équipement",
"Achats de Matériaux et Fournitures",
"Achat produits de nettoyage",
"Achat consommable informatique",
"Achat petit outillage",
"Avance à un prestataire",
"Payement prestataire",
"Payement éléctricité",
"Frais de mission - Déplacement",
"Achat de carburant",
"Paiement transport marchandise",
"Achat pièces pour réparation moto"
];
$Stores = new Stores();
$stor = $Stores->getActiveStore();
$data = json_decode($this->fetchTotal(),true);
$data['admin_options'] = $admin_options;
$data['caissier_options'] = $caissier_options;
$data['stores'] = $stor;
$this->render_template('sortieCaisse/index', $data);
}
// Create an AJAX endpoint to access the fetchTotal() function
public function fetchTotal(){
$data = [
'user_permission' => $this->permission,
'page_title' => $this->pageTitle
];
return json_encode($data);
}
public function fetchSortieCaisseData()
{
helper(['url', 'form']);
$SortieCaisse = new SortieCaisse();
// Initialiser les variables pour DataTables
$draw = intval($this->request->getVar('draw'));
$data = $SortieCaisse->getAllSortieCaisse();
$totalRecords = count($data);
$session = session();
$users = $session->get('user');
// ✅ Vérifier si l'utilisateur est DAF ou Direction UNIQUEMENT
$canManage = in_array($users['group_name'], ['Direction', 'DAF','SuperAdmin']);
$isCaissiere = $users['group_name'] === 'Caissière';
$result = [
"draw" => $draw,
"recordsTotal" => $totalRecords,
"recordsFiltered" => $totalRecords,
"data" => []
];
foreach ($data as $key => $value) {
$buttons = '';
// ✅ BOUTON MODIFICATION : Seulement pour DAF et Direction
if (in_array('updateSortieCaisse', $this->permission) && $canManage) {
$buttons .= '';
}
// ✅ BOUTON VALIDATION : Seulement pour DAF et Direction
if (in_array('validateSortieCaisse', $this->permission) && $canManage) {
$buttons .= '';
}
// ✅ NOUVEAU : BOUTON "MARQUER COMME PAYÉ" pour Caissière
if ($isCaissiere && $value['statut'] === 'Valider') {
$buttons .= '';
}
// Afficher un badge si déjà payé
if ($value['statut'] === 'Payé') {
$buttons .= ' Payé';
}
// ✅ CORRECTION : Enlever 'admin_raison' pour les caissières
if($users["group_name"] === "Caissière"){
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
$value['date_retrait'],
$value['sortie_personnel'],
$value['motif'],
$value['statut'],
$value['admin_raison'],
$buttons // Les caissières ne verront PAS les boutons (car $canManage = false)
];
}
elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF" ||$users["group_name"] === "SuperAdmin" ) {
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
$value['date_retrait'],
$value['sortie_personnel'],
$value['motif'],
$value['source_fond'],
$value['initiateur_demande'],
$this->returnStoreName($value['store_id']),
$value['commentaire'],
$value['statut'],
$buttons // DAF et Direction verront les boutons
];
}
elseif ($users["group_name"] === "Conseil") {
// ✅ Conseil peut voir les données mais SANS boutons d'action
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
$value['date_retrait'],
$value['sortie_personnel'],
$value['motif'],
$value['source_fond'],
$value['initiateur_demande'],
$this->returnStoreName($value['store_id']),
$value['commentaire'],
$value['statut'],
'' // Pas de boutons pour Conseil
];
}
}
return $this->response->setJSON($result);
}
/**
* ✅ NOUVELLE MÉTHODE : Marquer un décaissement comme payé
* Accessible uniquement par les caissières
*/
/**
* ✅ CORRECTION COMPLÈTE : Marquer un décaissement comme payé
* Accessible uniquement par les caissières
*/
public function markAsPaid($id_sortie)
{
// Vérifier que l'utilisateur est une caissière
$session = session();
$users = $session->get('user');
if ($users['group_name'] !== 'Caissière') {
return $this->response->setJSON([
'success' => false,
'messages' => 'Accès refusé. Seules les caissières peuvent marquer un décaissement comme payé.'
]);
}
try {
$SortieCaisse = new SortieCaisse();
// Récupérer le décaissement
$decaissement = $SortieCaisse->getSortieCaisseSingle($id_sortie);
if (!$decaissement) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Décaissement introuvable.'
]);
}
// Vérifier que le statut est "Valider"
if ($decaissement['statut'] !== 'Valider') {
return $this->response->setJSON([
'success' => false,
'messages' => 'Ce décaissement ne peut pas être marqué comme payé.
Statut actuel : ' . $decaissement['statut']
]);
}
// Mettre à jour le statut
$data = [
'statut' => 'Payé',
'date_paiement_effectif' => date('Y-m-d H:i:s')
];
$result = $SortieCaisse->updateSortieCaisse($id_sortie, $data);
if ($result) {
// ✅ Créer une notification pour TOUS les DAF, Direction et SuperAdmin
try {
if (class_exists('App\Controllers\NotificationController')) {
$Notification = new NotificationController();
$montant = number_format($decaissement['montant_retire'], 0, ',', ' ');
$message = "💰 Décaissement payé - " . $montant . " Ar
" .
"Motif: " . $decaissement['motif'] . "
" .
"Caissière: " . $users['firstname'] . ' ' . $users['lastname'] . "
" .
"Store: " . $this->returnStoreName($decaissement['store_id']);
// ✅ Récupérer TOUS les stores
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
// Notifier Direction
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'sortieCaisse'
);
// Notifier DAF
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'sortieCaisse'
);
// Notifier SuperAdmin
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'sortieCaisse'
);
}
}
}
} catch (\Exception $e) {
// Logger l'erreur mais continuer
log_message('error', 'Erreur notification markAsPaid: ' . $e->getMessage());
}
return $this->response->setJSON([
'success' => true,
'messages' => '✅ Décaissement marqué comme PAYÉ
' .
'Tous les Direction, DAF et SuperAdmin ont été notifiés.
' .
'Montant: ' . number_format($decaissement['montant_retire'], 0, ',', ' ') . ' Ar'
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de la mise à jour du statut.'
]);
}
} catch (\Exception $e) {
log_message('error', 'Erreur markAsPaid: ' . $e->getMessage() . ' | Ligne: ' . $e->getLine());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur serveur: ' . $e->getMessage()
]);
}
}
public function fetchSortieCaisseData1()
{
helper(['url', 'form']);
$SortieCaisse = new SortieCaisse();
// Initialiser les variables pour DataTables
$draw = intval($this->request->getVar('draw'));
$data = $SortieCaisse->getAllSortieCaisse1();
$totalRecords = count($data);
$session = session();
$users = $session->get('user');
// ✅ Vérifier si l'utilisateur est DAF ou Direction UNIQUEMENT
$canManage = in_array($users['group_name'], ['Direction', 'DAF','SuperAdmin']);
$isCaissiere = $users['group_name'] === 'Caissière';
$result = [
"draw" => $draw,
"recordsTotal" => $totalRecords,
"recordsFiltered" => $totalRecords,
"data" => []
];
foreach ($data as $key => $value) {
$buttons = '';
// ✅ BOUTON MODIFICATION : Seulement pour DAF et Direction
if (in_array('updateSortieCaisse', $this->permission) && $canManage) {
$buttons .= '';
}
// ✅ BOUTON VALIDATION : Seulement pour DAF et Direction
if (in_array('validateSortieCaisse', $this->permission) && $canManage) {
$buttons .= '';
}
// ✅ NOUVEAU : BOUTON "MARQUER COMME PAYÉ" pour Caissière
if ($isCaissiere && $value['statut'] === 'Valider') {
$buttons .= '';
}
// Afficher un badge si déjà payé
if ($value['statut'] === 'Payé') {
$buttons .= ' Payé';
}
if($users["group_name"] === "Caissière"){
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
$value['date_retrait'],
$value['sortie_personnel'],
$value['motif'],
$value['statut'],
$value['admin_raison'],
$buttons // Les caissières ne verront PAS les boutons
];
}
elseif ($users["group_name"] === "Direction" || $users["group_name"] === "DAF" || $users["group_name"] === "SuperAdmin") {
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
$value['date_retrait'],
$value['sortie_personnel'],
$value['motif'],
$value['source_fond'],
$value['initiateur_demande'],
$this->returnStoreName($value['store_id']),
$value['commentaire'],
$value['statut'],
$buttons // DAF et Direction verront les boutons
];
}
elseif ($users["group_name"] === "Conseil") {
// ✅ Conseil peut voir les données mais SANS boutons d'action
$result['data'][$key] = [
$value['id_sortie'],
number_format($value['montant_retire'], 0, '.', ' '),
$value['date_retrait'],
$value['sortie_personnel'],
$value['motif'],
$value['source_fond'],
$value['initiateur_demande'],
$this->returnStoreName($value['store_id']),
$value['commentaire'],
$value['statut'],
'' // Pas de boutons pour Conseil
];
}
}
return $this->response->setJSON($result);
}
private function returnStoreName(int $id)
{
$Stores = new Stores();
$stor = $Stores->getActiveStore();
$Storename = "";
foreach ($stor as $key => $value) {
if ($value['id'] == $id) {
$Storename = $value['name'];
}
}
return $Storename;
}
public function createSortieCaisse()
{
$this->verifyRole('createSortieCaisse');
$validation = \Config\Services::validation();
$validation->setRules([
'montant_retire' => 'required|numeric',
'motif_select' => 'required',
'nom' => 'required',
'fonction' => 'required',
'mode_paiement' => 'required',
'montant_lettre' => 'required',
'date_demande' => 'required'
]);
if (!$validation->withRequest($this->request)->run()) {
return $this->response->setJSON([
'success' => false,
'messages' => $validation->getErrors()
]);
}
try {
$session = session();
$user = $session->get('user');
// Nettoyage et conversion du montant
$montant_retire_raw = $this->request->getPost('montant_retire');
$montant_retire = (float) str_replace([' ', ','], ['', '.'], $montant_retire_raw);
// Récupérer le mode de paiement
$mode_paiement = $this->request->getPost('mode_paiement');
if ($montant_retire <= 0) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Le montant doit être supérieur à 0'
]);
}
// RÉCUPÉRATION DES DONNÉES FINANCIÈRES
$orders = new Orders();
$Recouvrement = new Recouvrement();
$sortieCaisse = new SortieCaisse();
$paymentData = $orders->getPaymentModes();
$totalRecouvrement = $Recouvrement->getTotalRecouvrements();
$total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse();
// EXTRACTION DES TOTAUX DES SORTIES PAR MODE DE PAIEMENT
$total_sortie_espece = 0;
$total_sortie_mvola = 0;
$total_sortie_virement = 0;
if (is_object($total_sortie_caisse)) {
$total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0;
$total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0;
$total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0;
}
// Recouvrements
$total_recouvrement_me = 0;
$total_recouvrement_be = 0;
$total_recouvrement_bm = 0;
$total_recouvrement_mb = 0;
if (is_object($totalRecouvrement)) {
$total_recouvrement_me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0;
$total_recouvrement_be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0;
$total_recouvrement_bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0;
$total_recouvrement_mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0;
}
// Orders
$total_espece1 = 0;
$total_espece2 = 0;
$total_mvola1 = 0;
$total_mvola2 = 0;
$total_virement1 = 0;
$total_virement2 = 0;
if (is_object($paymentData)) {
$total_espece1 = isset($paymentData->total_espece1) ? (float) $paymentData->total_espece1 : 0;
$total_espece2 = isset($paymentData->total_espece2) ? (float) $paymentData->total_espece2 : 0;
$total_mvola1 = isset($paymentData->total_mvola1) ? (float) $paymentData->total_mvola1 : 0;
$total_mvola2 = isset($paymentData->total_mvola2) ? (float) $paymentData->total_mvola2 : 0;
$total_virement1 = isset($paymentData->total_virement_bancaire1) ? (float) $paymentData->total_virement_bancaire1 : 0;
$total_virement2 = isset($paymentData->total_virement_bancaire2) ? (float) $paymentData->total_virement_bancaire2 : 0;
}
// CALCUL DES SOLDES DISPONIBLES PAR MODE DE PAIEMENT
$total_espece_disponible = $total_espece1 +
$total_espece2 +
$total_recouvrement_me +
$total_recouvrement_be -
$total_sortie_espece;
$total_mvola_disponible = $total_mvola1 +
$total_mvola2 -
$total_recouvrement_me -
$total_recouvrement_mb +
$total_recouvrement_bm -
$total_sortie_mvola;
$total_virement_disponible = $total_virement1 +
$total_virement2 -
$total_recouvrement_be -
$total_recouvrement_bm +
$total_recouvrement_mb -
$total_sortie_virement;
// VÉRIFICATION SELON LE MODE DE PAIEMENT CHOISI
$fonds_disponible = 0;
$mode_paiement_label = '';
switch ($mode_paiement) {
case 'En espèce':
$fonds_disponible = $total_espece_disponible;
$mode_paiement_label = 'en espèce';
break;
case 'MVOLA':
$fonds_disponible = $total_mvola_disponible;
$mode_paiement_label = 'MVOLA';
break;
case 'Virement Bancaire':
$fonds_disponible = $total_virement_disponible;
$mode_paiement_label = 'virement bancaire';
break;
default:
return $this->response->setJSON([
'success' => false,
'messages' => 'Mode de paiement invalide'
]);
}
// Vérification des fonds
if ($montant_retire > $fonds_disponible) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Décaissement échoué — fonds ' . $mode_paiement_label . ' insuffisants.
' .
'Disponible: ' . number_format($fonds_disponible, 0, ',', ' ') . ' Ar
' .
'Demandé: ' . number_format($montant_retire, 0, ',', ' ') . ' Ar
' .
'Soldes actuels:
' .
'• Espèce: ' . number_format($total_espece_disponible, 0, ',', ' ') . ' Ar
' .
'• MVOLA: ' . number_format($total_mvola_disponible, 0, ',', ' ') . ' Ar
' .
'• Virement: ' . number_format($total_virement_disponible, 0, ',', ' ') . ' Ar'
]);
}
// PRÉPARATION DES DONNÉES
$motif = $this->request->getPost('motif_select');
$data = [
'montant_retire' => $montant_retire,
'date_retrait' => date('Y-m-d H:i:s'),
'motif' => $motif,
'commentaire' => $this->request->getPost('sortie_commentaire') ?? '',
'fournisseur' => $this->request->getPost('sortie_fournisseur') ?? '',
'nif_cin' => $this->request->getPost('sortie_nif') ?? '',
'statistique' => $this->request->getPost('sortie_statistique') ?? '',
'telephone' => $this->request->getPost('sortie_phone') ?? '',
'code_postal' => $this->request->getPost('sortie_adresse') ?? '',
'statut' => 'En attente',
'user_id' => $user['id'],
'store_id' => $user['store_id'],
'admin_raison' => '',
'sortie_personnel' => $user['firstname'] . ' ' . $user['lastname'],
'nom_demandeur' => $this->request->getPost('nom'),
'fonction_demandeur' => $this->request->getPost('fonction'),
'mode_paiement' => $mode_paiement,
'montant_lettre' => $this->request->getPost('montant_lettre'),
'date_demande' => $this->request->getPost('date_demande'),
'reference' => $this->request->getPost('reference') ?? ''
];
// Mapping source_fond et initiateur
if (isset($this->mapping[$motif])) {
$data['source_fond'] = $this->mapping[$motif]['source_fond'];
$data['initiateur_demande'] = $this->mapping[$motif]['initiateur_demande'];
} else {
$data['source_fond'] = 'Caisse Courante';
$data['initiateur_demande'] = 'Caissière';
}
// Champs supplémentaires pour montant > 1,000,000
if ($montant_retire >= 1000000) {
$data['numero_fiche'] = $this->request->getPost('numero_fiche') ?? '';
$data['date_fiche'] = $this->request->getPost('date_fiche') ?? null;
$data['service_demandeur'] = $this->request->getPost('service_demandeur') ?? '';
$data['objet_depense'] = $this->request->getPost('objet_depense') ?? '';
$data['nature_depense'] = $this->request->getPost('nature_depense') ?? '';
$data['montant_estime'] = $this->request->getPost('montant_estime') ?? '';
$data['mode_reglement'] = $this->request->getPost('mode_reglement') ?? '';
$data['date_paiement_prevue'] = $this->request->getPost('date_paiement_prevue') ?? null;
$justificatifs = $this->request->getPost('justificatifs');
if (is_array($justificatifs) && count($justificatifs) > 0) {
$data['justificatifs'] = json_encode($justificatifs);
}
$data['visa_demandeur'] = $this->request->getPost('visa_demandeur') ?? '';
$data['date_visa_demandeur'] = $this->request->getPost('date_visa_demandeur') ?? null;
$data['visa_chef_service'] = $this->request->getPost('visa_chef_service') ?? '';
$data['date_visa_chef_service'] = $this->request->getPost('date_visa_chef_service') ?? null;
$data['visa_direction'] = $this->request->getPost('visa_direction') ?? '';
$data['date_visa_direction'] = $this->request->getPost('date_visa_direction') ?? null;
$data['visa_superAdmin'] = $this->request->getPost('visa_superAdmin') ?? '';
$data['date_visa_superAdmin'] = $this->request->getPost('date_visa_superAdmin') ?? null;
$data['observations'] = $this->request->getPost('observations') ?? '';
}
// Gestion du fichier
$preuveFile = $this->request->getFile('sortie_preuve');
if ($preuveFile && $preuveFile->isValid() && !$preuveFile->hasMoved()) {
$newName = $preuveFile->getRandomName();
$uploadPath = WRITEPATH . 'uploads/sortie_caisse';
if (!is_dir($uploadPath)) {
mkdir($uploadPath, 0755, true);
}
$preuveFile->move($uploadPath, $newName);
$data['preuve_achat'] = $newName;
$data['mime_type'] = $preuveFile->getClientMimeType();
}
// INSERTION EN BASE
$model = new SortieCaisse();
$result = $model->addSortieCaisse($data);
if ($result) {
// ✅ Notification pour TOUS les Direction, DAF et SuperAdmin
try {
if (class_exists('App\Controllers\NotificationController')) {
$Notification = new NotificationController();
$message = "Nouvelle demande de décaissement de " .
number_format($montant_retire, 0, ',', ' ') . " Ar (" . $mode_paiement . ")
" .
"Store: " . $this->returnStoreName($user['store_id']) . "
" .
"Demandeur: " . $user['firstname'] . ' ' . $user['lastname'];
// ✅ Récupérer TOUS les stores
$Stores = new Stores();
$allStores = $Stores->getActiveStore();
// ✅ Notifier Direction, DAF et SuperAdmin de TOUS les stores
if (is_array($allStores) && count($allStores) > 0) {
foreach ($allStores as $store) {
$Notification->createNotification(
$message,
"Direction",
(int)$store['id'],
'sortieCaisse'
);
$Notification->createNotification(
$message,
"DAF",
(int)$store['id'],
'sortieCaisse'
);
$Notification->createNotification(
$message,
"SuperAdmin",
(int)$store['id'],
'sortieCaisse'
);
}
}
}
} catch (\Exception $e) {
log_message('error', 'Erreur notification createSortieCaisse: ' . $e->getMessage());
// Continue même si la notification échoue
}
return $this->response->setJSON([
'success' => true,
'messages' => 'Décaissement de ' . number_format($montant_retire, 0, ',', ' ') . ' Ar créé avec succès
' .
'Mode de paiement: ' . $mode_paiement . '
' .
'Nouveau solde ' . $mode_paiement_label . ': ' . number_format($fonds_disponible - $montant_retire, 0, ',', ' ') . ' Ar
' .
'Notification envoyée à tous les Direction, DAF et SuperAdmin'
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de l\'enregistrement en base de données'
]);
}
} catch (\Exception $e) {
log_message('error', 'Erreur createSortieCaisse: ' . $e->getMessage() . ' | Ligne: ' . $e->getLine());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur serveur: ' . $e->getMessage()
]);
}
}
public function updateSortieCaisse($id_sortie)
{
$this->verifyRole('updateSortieCaisse');
$data['page_title'] = $this->pageTitle;
if ($this->request->getMethod() === 'post') {
$validation = \Config\Services::validation();
$validation->setRules([
'montant_retire_edit' => 'required|numeric',
'nom_edit' => 'required',
'fonction_edit' => 'required',
'date_demande_edit' => 'required'
// Suppression des règles de validation pour les champs qui seront en hidden
// La preuve d'achat devient facultative
]);
if (!$validation->withRequest($this->request)->run()) {
return $this->response->setJSON([
'success' => false,
'messages' => $validation->getErrors()
]);
}
try {
$session = session();
$users = $session->get('user');
// Récupérer le décaissement actuel
$sortieCaisse = new SortieCaisse();
$currentSortie = $sortieCaisse->getSortieCaisseSingle($id_sortie);
if (!$currentSortie) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Décaissement introuvable'
]);
}
// Nettoyage et conversion du montant
$montant_retire_raw = $this->request->getPost('montant_retire_edit');
$montant_retire = (float) str_replace([' ', ','], ['', '.'], $montant_retire_raw);
// Récupérer le motif (disabled dans le form, donc on le récupère du champ hidden)
$motif = $this->request->getPost('motif_select_edit') ?: $this->request->getPost('motif_select_edit_hidden');
// Récupérer le mode de paiement (maintenant éditable, donc directement du formulaire)
$mode_paiement = $this->request->getPost('mode_paiement_edit');
if ($montant_retire <= 0) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Le montant doit être supérieur à 0'
]);
}
// RÉCUPÉRATION DES DONNÉES FINANCIÈRES
$orders = new Orders();
$Recouvrement = new Recouvrement();
$paymentData = $orders->getPaymentModes();
$totalRecouvrement = $Recouvrement->getTotalRecouvrements();
$total_sortie_caisse = $sortieCaisse->getTotalSortieCaisse();
// EXTRACTION DES TOTAUX DES SORTIES PAR MODE DE PAIEMENT
$total_sortie_espece = 0;
$total_sortie_mvola = 0;
$total_sortie_virement = 0;
if (is_object($total_sortie_caisse)) {
$total_sortie_espece = isset($total_sortie_caisse->total_espece) ? (float) $total_sortie_caisse->total_espece : 0;
$total_sortie_mvola = isset($total_sortie_caisse->total_mvola) ? (float) $total_sortie_caisse->total_mvola : 0;
$total_sortie_virement = isset($total_sortie_caisse->total_virement) ? (float) $total_sortie_caisse->total_virement : 0;
}
// IMPORTANT : Ajouter le montant actuel du décaissement aux sorties
// pour avoir le solde réel avant modification
if ($currentSortie['statut'] === 'Valider') {
switch ($currentSortie['mode_paiement']) {
case 'En espèce':
$total_sortie_espece -= (float) $currentSortie['montant_retire'];
break;
case 'MVOLA':
$total_sortie_mvola -= (float) $currentSortie['montant_retire'];
break;
case 'Virement Bancaire':
$total_sortie_virement -= (float) $currentSortie['montant_retire'];
break;
}
}
// Recouvrements
$total_recouvrement_me = 0;
$total_recouvrement_be = 0;
$total_recouvrement_bm = 0;
$total_recouvrement_mb = 0;
if (is_object($totalRecouvrement)) {
$total_recouvrement_me = isset($totalRecouvrement->me) ? (float) $totalRecouvrement->me : 0;
$total_recouvrement_be = isset($totalRecouvrement->be) ? (float) $totalRecouvrement->be : 0;
$total_recouvrement_bm = isset($totalRecouvrement->bm) ? (float) $totalRecouvrement->bm : 0;
$total_recouvrement_mb = isset($totalRecouvrement->mb) ? (float) $totalRecouvrement->mb : 0;
}
// Orders
$total_espece1 = 0;
$total_espece2 = 0;
$total_mvola1 = 0;
$total_mvola2 = 0;
$total_virement1 = 0;
$total_virement2 = 0;
if (is_object($paymentData)) {
$total_espece1 = isset($paymentData->total_espece1) ? (float) $paymentData->total_espece1 : 0;
$total_espece2 = isset($paymentData->total_espece2) ? (float) $paymentData->total_espece2 : 0;
$total_mvola1 = isset($paymentData->total_mvola1) ? (float) $paymentData->total_mvola1 : 0;
$total_mvola2 = isset($paymentData->total_mvola2) ? (float) $paymentData->total_mvola2 : 0;
$total_virement1 = isset($paymentData->total_virement_bancaire1) ? (float) $paymentData->total_virement_bancaire1 : 0;
$total_virement2 = isset($paymentData->total_virement_bancaire2) ? (float) $paymentData->total_virement_bancaire2 : 0;
}
// CALCUL DES SOLDES DISPONIBLES
$total_espece_disponible = $total_espece1 +
$total_espece2 +
$total_recouvrement_me +
$total_recouvrement_be -
$total_sortie_espece;
$total_mvola_disponible = $total_mvola1 +
$total_mvola2 -
$total_recouvrement_me -
$total_recouvrement_mb +
$total_recouvrement_bm -
$total_sortie_mvola;
$total_virement_disponible = $total_virement1 +
$total_virement2 -
$total_recouvrement_be -
$total_recouvrement_bm +
$total_recouvrement_mb -
$total_sortie_virement;
// VÉRIFICATION SELON LE MODE DE PAIEMENT CHOISI
$fonds_disponible = 0;
$mode_paiement_label = '';
switch ($mode_paiement) {
case 'En espèce':
$fonds_disponible = $total_espece_disponible;
$mode_paiement_label = 'en espèce';
break;
case 'MVOLA':
$fonds_disponible = $total_mvola_disponible;
$mode_paiement_label = 'MVOLA';
break;
case 'Virement Bancaire':
$fonds_disponible = $total_virement_disponible;
$mode_paiement_label = 'virement bancaire';
break;
default:
return $this->response->setJSON([
'success' => false,
'messages' => 'Mode de paiement invalide'
]);
}
// Vérification des fonds
if ($montant_retire > $fonds_disponible) {
return $this->response->setJSON([
'success' => false,
'messages' => 'Modification échouée — fonds ' . $mode_paiement_label . ' insuffisants.
' .
'Disponible: ' . number_format($fonds_disponible, 0, ',', ' ') . ' Ar
' .
'Demandé: ' . number_format($montant_retire, 0, ',', ' ') . ' Ar'
]);
}
// PRÉPARATION DES DONNÉES
$data = [
'montant_retire' => $montant_retire,
'date_retrait' => date('Y-m-d H:i:s'),
'motif' => $motif,
'commentaire' => $this->request->getPost('sortie_commentaire_edit') ?? '',
'fournisseur' => $this->request->getPost('sortie_fournisseur_edit') ?? '',
'nif_cin' => $this->request->getPost('sortie_nif_edit') ?? '',
'statistique' => $this->request->getPost('sortie_statistique_edit') ?? '',
'telephone' => $this->request->getPost('sortie_phone_edit') ?? '',
'code_postal' => $this->request->getPost('sortie_adresse_edit') ?? '',
'sortie_personnel' => $users['firstname'] . ' ' . $users['lastname'],
'store_id' => $users['store_id'],
'nom_demandeur' => $this->request->getPost('nom_edit'),
'fonction_demandeur' => $this->request->getPost('fonction_edit'),
'mode_paiement' => $mode_paiement,
'montant_lettre' => $this->request->getPost('montant_lettre_edit'),
'date_demande' => $this->request->getPost('date_demande_edit'),
'reference' => $this->request->getPost('reference_edit') ?? ''
];
// Mapping source_fond et initiateur
if (isset($this->mapping[$motif])) {
$data['source_fond'] = $this->mapping[$motif]['source_fond'];
$data['initiateur_demande'] = $this->mapping[$motif]['initiateur_demande'];
} else {
$data['source_fond'] = 'Caisse Courante';
$data['initiateur_demande'] = 'Caissière';
}
// Gestion du fichier (FACULTATIF)
$preuveFile = $this->request->getFile('sortie_preuve_edit');
if ($preuveFile && $preuveFile->isValid() && !$preuveFile->hasMoved()) {
$newName = $preuveFile->getRandomName();
$uploadPath = WRITEPATH . 'uploads/sortie_caisse';
if (!is_dir($uploadPath)) {
mkdir($uploadPath, 0755, true);
}
$preuveFile->move($uploadPath, $newName);
$data['preuve_achat'] = $newName;
$data['mime_type'] = $preuveFile->getClientMimeType();
}
// Si aucun nouveau fichier n'est uploadé, on garde l'ancien (pas de modification)
// MISE À JOUR EN BASE
if ($sortieCaisse->updateSortieCaisse($id_sortie, $data)) {
return $this->response->setJSON([
'success' => true,
'messages' => 'Décaissement modifié avec succès !
' .
'Nouveau montant: ' . number_format($montant_retire, 0, ',', ' ') . ' Ar (' . $mode_paiement . ')'
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de la modification en base de données'
]);
}
} catch (\Exception $e) {
log_message('error', 'Erreur updateSortieCaisse: ' . $e->getMessage() . ' | Ligne: ' . $e->getLine());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur serveur: ' . $e->getMessage()
]);
}
}
}
public function fetchSortieCaisseSingle($id)
{
if ($id) {
$SortieCaisse = new SortieCaisse();
$data = $SortieCaisse->getSortieCaisseSingle($id);
echo json_encode($data);
}
}
public function validateSortieCaisse($id_sortie) {
$this->verifyRole('validateSortieCaisse');
$data['page_title'] = $this->pageTitle;
if ($this->request->getMethod() === 'post') {
$data = [
'admin_raison' => $this->request->getPost('admin_raison'),
'statut' => $this->request->getPost('statut'),
];
$session = session();
$users = $session->get('user');
$SortieCaisse = new SortieCaisse();
$Notification = new NotificationController();
if ($SortieCaisse->updateSortieCaisse($id_sortie, $data)) {
$statut = $this->request->getPost('statut');
$message = '';
// ✅ Récupérer le décaissement pour avoir son store_id
$decaissement = $SortieCaisse->getSortieCaisseSingle($id_sortie);
$store_id = $decaissement['store_id'];
switch ($statut) {
case "Valider":
$message = "✅ Votre décaissement a été validé par la Direction
Store: " . $this->returnStoreName($store_id);
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
break;
case "Refuser":
$message = "❌ Votre décaissement a été refusé par la Direction
Store: " . $this->returnStoreName($store_id) . "
Raison: " . $this->request->getPost('admin_raison');
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
break;
case "En attente":
$message = "⏳ Votre décaissement a été mis en attente par la Direction
Store: " . $this->returnStoreName($store_id);
$Notification->createNotification($message, "Caissière", (int)$store_id, 'sortieCaisse');
break;
}
return $this->response->setJSON([
'success' => true,
'messages' => 'Décaissement modifié avec succès ! Notification envoyée à la caissière de ' . $this->returnStoreName($store_id)
]);
} else {
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de la modification du décaissement. Veuillez réessayer.'
]);
}
}
}
/**
* Exporter les décaissements en Excel
*/
public function exportExcel()
{
$this->verifyRole('viewSortieCaisse');
try {
$SortieCaisse = new SortieCaisse();
$data = $SortieCaisse->getAllSortieCaisse();
$session = session();
$user = $session->get('user');
// Créer un nouveau Spreadsheet
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Définir le titre
$sheet->setTitle('Décaissements');
// En-têtes selon le groupe de l'utilisateur
if ($user["group_name"] === "Caissière") {
$headers = [
'ID',
'Montant Retiré (Ar)',
'Date Retrait',
'Personnel',
'Motif',
'Statut',
'Raison Admin'
];
} else {
$headers = [
'ID',
'Montant Retiré (Ar)',
'Date Retrait',
'Personnel',
'Motif',
'Source Fond',
'Initiateur',
'Magasin',
'Commentaire',
'Statut'
];
}
// Écrire les en-têtes
$col = 'A';
foreach ($headers as $header) {
$sheet->setCellValue($col . '1', $header);
$sheet->getStyle($col . '1')->applyFromArray([
'font' => [
'bold' => true,
'color' => ['rgb' => 'FFFFFF']
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => '4472C4']
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER
]
]);
$col++;
}
// Remplir les données
$row = 2;
foreach ($data as $item) {
if ($user["group_name"] === "Caissière") {
$sheet->setCellValue('A' . $row, $item['id_sortie']);
$sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' '));
$sheet->setCellValue('C' . $row, $item['date_retrait']);
$sheet->setCellValue('D' . $row, $item['sortie_personnel']);
$sheet->setCellValue('E' . $row, $item['motif']);
$sheet->setCellValue('F' . $row, $item['statut']);
$sheet->setCellValue('G' . $row, $item['admin_raison']);
} else {
$sheet->setCellValue('A' . $row, $item['id_sortie']);
$sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' '));
$sheet->setCellValue('C' . $row, $item['date_retrait']);
$sheet->setCellValue('D' . $row, $item['sortie_personnel']);
$sheet->setCellValue('E' . $row, $item['motif']);
$sheet->setCellValue('F' . $row, $item['source_fond']);
$sheet->setCellValue('G' . $row, $item['initiateur_demande']);
$sheet->setCellValue('H' . $row, $this->returnStoreName($item['store_id']));
$sheet->setCellValue('I' . $row, $item['commentaire']);
$sheet->setCellValue('J' . $row, $item['statut']);
}
$row++;
}
// Auto-dimensionner les colonnes
foreach (range('A', $col) as $columnID) {
$sheet->getColumnDimension($columnID)->setAutoSize(true);
}
// Ajouter des bordures
$styleArray = [
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => '000000']
]
]
];
$sheet->getStyle('A1:' . $col . ($row - 1))->applyFromArray($styleArray);
// Générer le fichier
$writer = new Xlsx($spreadsheet);
$filename = 'decaissements_' . date('Y-m-d_His') . '.xlsx';
// Envoyer le fichier au navigateur
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit;
} catch (\Exception $e) {
log_message('error', 'Erreur exportExcel: ' . $e->getMessage());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de l\'exportation: ' . $e->getMessage()
]);
}
}
/**
* Exporter les décaissements en CSV
*/
public function exportCsv()
{
$this->verifyRole('viewSortieCaisse');
try {
$SortieCaisse = new SortieCaisse();
$data = $SortieCaisse->getAllSortieCaisse();
$session = session();
$user = $session->get('user');
// Créer un nouveau Spreadsheet
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// En-têtes selon le groupe de l'utilisateur
if ($user["group_name"] === "Caissière") {
$headers = [
'ID',
'Montant Retiré (Ar)',
'Date Retrait',
'Personnel',
'Motif',
'Statut',
'Raison Admin'
];
} else {
$headers = [
'ID',
'Montant Retiré (Ar)',
'Date Retrait',
'Personnel',
'Motif',
'Source Fond',
'Initiateur',
'Magasin',
'Commentaire',
'Statut'
];
}
// Écrire les en-têtes
$col = 'A';
foreach ($headers as $header) {
$sheet->setCellValue($col . '1', $header);
$col++;
}
// Remplir les données
$row = 2;
foreach ($data as $item) {
if ($user["group_name"] === "Caissière") {
$sheet->setCellValue('A' . $row, $item['id_sortie']);
$sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' '));
$sheet->setCellValue('C' . $row, $item['date_retrait']);
$sheet->setCellValue('D' . $row, $item['sortie_personnel']);
$sheet->setCellValue('E' . $row, $item['motif']);
$sheet->setCellValue('F' . $row, $item['statut']);
$sheet->setCellValue('G' . $row, $item['admin_raison']);
} else {
$sheet->setCellValue('A' . $row, $item['id_sortie']);
$sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' '));
$sheet->setCellValue('C' . $row, $item['date_retrait']);
$sheet->setCellValue('D' . $row, $item['sortie_personnel']);
$sheet->setCellValue('E' . $row, $item['motif']);
$sheet->setCellValue('F' . $row, $item['source_fond']);
$sheet->setCellValue('G' . $row, $item['initiateur_demande']);
$sheet->setCellValue('H' . $row, $this->returnStoreName($item['store_id']));
$sheet->setCellValue('I' . $row, $item['commentaire']);
$sheet->setCellValue('J' . $row, $item['statut']);
}
$row++;
}
// Générer le fichier CSV
$writer = new CsvWriter($spreadsheet);
$writer->setDelimiter(';');
$writer->setEnclosure('"');
$writer->setLineEnding("\r\n");
$writer->setSheetIndex(0);
$filename = 'decaissements_' . date('Y-m-d_His') . '.csv';
// Envoyer le fichier au navigateur
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit;
} catch (\Exception $e) {
log_message('error', 'Erreur exportCsv: ' . $e->getMessage());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de l\'exportation: ' . $e->getMessage()
]);
}
}
/**
* Exporter avec filtres (optionnel)
*/
public function exportWithFilters()
{
$this->verifyRole('viewSortieCaisse');
$format = $this->request->getGet('format'); // 'excel' ou 'csv'
$dateDebut = $this->request->getGet('date_debut');
$dateFin = $this->request->getGet('date_fin');
$statut = $this->request->getGet('statut');
try {
$SortieCaisse = new SortieCaisse();
// Récupérer les données avec filtres
$builder = $SortieCaisse->builder();
if ($dateDebut) {
$builder->where('date_retrait >=', $dateDebut);
}
if ($dateFin) {
$builder->where('date_retrait <=', $dateFin);
}
if ($statut) {
$builder->where('statut', $statut);
}
$data = $builder->get()->getResultArray();
$session = session();
$user = $session->get('user');
// Créer le Spreadsheet
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Décaissements Filtrés');
// En-têtes
if ($user["group_name"] === "Caissière") {
$headers = ['ID', 'Montant Retiré (Ar)', 'Date Retrait', 'Personnel', 'Motif', 'Statut', 'Raison Admin'];
} else {
$headers = ['ID', 'Montant Retiré (Ar)', 'Date Retrait', 'Personnel', 'Motif', 'Source Fond', 'Initiateur', 'Magasin', 'Commentaire', 'Statut'];
}
$col = 'A';
foreach ($headers as $header) {
$sheet->setCellValue($col . '1', $header);
$sheet->getStyle($col . '1')->applyFromArray([
'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']],
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '4472C4']],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER]
]);
$col++;
}
// Remplir les données
$row = 2;
foreach ($data as $item) {
if ($user["group_name"] === "Caissière") {
$sheet->setCellValue('A' . $row, $item['id_sortie']);
$sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' '));
$sheet->setCellValue('C' . $row, $item['date_retrait']);
$sheet->setCellValue('D' . $row, $item['sortie_personnel']);
$sheet->setCellValue('E' . $row, $item['motif']);
$sheet->setCellValue('F' . $row, $item['statut']);
$sheet->setCellValue('G' . $row, $item['admin_raison']);
} else {
$sheet->setCellValue('A' . $row, $item['id_sortie']);
$sheet->setCellValue('B' . $row, number_format($item['montant_retire'], 0, '.', ' '));
$sheet->setCellValue('C' . $row, $item['date_retrait']);
$sheet->setCellValue('D' . $row, $item['sortie_personnel']);
$sheet->setCellValue('E' . $row, $item['motif']);
$sheet->setCellValue('F' . $row, $item['source_fond']);
$sheet->setCellValue('G' . $row, $item['initiateur_demande']);
$sheet->setCellValue('H' . $row, $this->returnStoreName($item['store_id']));
$sheet->setCellValue('I' . $row, $item['commentaire']);
$sheet->setCellValue('J' . $row, $item['statut']);
}
$row++;
}
// Auto-dimensionner
foreach (range('A', $col) as $columnID) {
$sheet->getColumnDimension($columnID)->setAutoSize(true);
}
// Générer selon le format
if ($format === 'csv') {
$writer = new CsvWriter($spreadsheet);
$writer->setDelimiter(';');
$filename = 'decaissements_filtres_' . date('Y-m-d_His') . '.csv';
header('Content-Type: text/csv');
} else {
$writer = new Xlsx($spreadsheet);
$filename = 'decaissements_filtres_' . date('Y-m-d_His') . '.xlsx';
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
}
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit;
} catch (\Exception $e) {
log_message('error', 'Erreur exportWithFilters: ' . $e->getMessage());
return $this->response->setJSON([
'success' => false,
'messages' => 'Erreur lors de l\'exportation: ' . $e->getMessage()
]);
}
}
}