You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1598 lines
70 KiB
1598 lines
70 KiB
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Controllers\AdminController;
|
|
use App\Models\SortieCaisse;
|
|
use App\Models\Orders;
|
|
use App\Models\Recouvrement;
|
|
use App\Models\Stores;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
use PhpOffice\PhpSpreadsheet\Writer\Csv as CsvWriter;
|
|
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
|
|
|
class SortieCaisseController extends AdminController
|
|
{
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
private $mapping = [
|
|
// superadmin
|
|
"Achat de matériel informatique" => [
|
|
'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 .= '<button type="button" class="btn btn-default" onclick="editFunc(' . $value['id_sortie'] . ')" data-toggle="modal" data-target="#updateModal"><i class="fa fa-pencil"></i></button>';
|
|
}
|
|
|
|
// ✅ BOUTON VALIDATION : Seulement pour DAF et Direction
|
|
if (in_array('validateSortieCaisse', $this->permission) && $canManage) {
|
|
$buttons .= '<button type="button" class="btn btn-default" onclick="validateFunc(' . $value['id_sortie'] . ')"><i class="fa fa-check-circle"></i></button>';
|
|
}
|
|
|
|
// ✅ NOUVEAU : BOUTON "MARQUER COMME PAYÉ" pour Caissière
|
|
if ($isCaissiere && $value['statut'] === 'Valider') {
|
|
$buttons .= '<button type="button" class="btn btn-success btn-sm" onclick="markAsPaidFunc(' . $value['id_sortie'] . ')" title="Marquer comme payé"><i class="fa fa-money"></i> Payé</button>';
|
|
}
|
|
|
|
// Afficher un badge si déjà payé
|
|
if ($value['statut'] === 'Payé') {
|
|
$buttons .= '<span class="label label-success"><i class="fa fa-check"></i> Payé</span>';
|
|
}
|
|
|
|
// ✅ 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é.<br>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<br>" .
|
|
"Motif: " . $decaissement['motif'] . "<br>" .
|
|
"Caissière: " . $users['firstname'] . ' ' . $users['lastname'] . "<br>" .
|
|
"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 <strong>PAYÉ</strong><br>' .
|
|
'Tous les Direction, DAF et SuperAdmin ont été notifiés.<br>' .
|
|
'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 .= '<button type="button" class="btn btn-default" onclick="editFunc(' . $value['id_sortie'] . ')" data-toggle="modal" data-target="#updateModal"><i class="fa fa-pencil"></i></button>';
|
|
}
|
|
|
|
// ✅ BOUTON VALIDATION : Seulement pour DAF et Direction
|
|
if (in_array('validateSortieCaisse', $this->permission) && $canManage) {
|
|
$buttons .= '<button type="button" class="btn btn-default" onclick="validateFunc(' . $value['id_sortie'] . ')"><i class="fa fa-check-circle"></i></button>';
|
|
}
|
|
|
|
// ✅ NOUVEAU : BOUTON "MARQUER COMME PAYÉ" pour Caissière
|
|
if ($isCaissiere && $value['statut'] === 'Valider') {
|
|
$buttons .= '<button type="button" class="btn btn-success btn-sm" onclick="markAsPaidFunc(' . $value['id_sortie'] . ')" title="Marquer comme payé"><i class="fa fa-money"></i> Payé</button>';
|
|
}
|
|
|
|
// Afficher un badge si déjà payé
|
|
if ($value['statut'] === 'Payé') {
|
|
$buttons .= '<span class="label label-success"><i class="fa fa-check"></i> Payé</span>';
|
|
}
|
|
|
|
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.<br>' .
|
|
'<strong>Disponible:</strong> ' . number_format($fonds_disponible, 0, ',', ' ') . ' Ar<br>' .
|
|
'<strong>Demandé:</strong> ' . number_format($montant_retire, 0, ',', ' ') . ' Ar<br><br>' .
|
|
'<em>Soldes actuels:</em><br>' .
|
|
'• Espèce: ' . number_format($total_espece_disponible, 0, ',', ' ') . ' Ar<br>' .
|
|
'• MVOLA: ' . number_format($total_mvola_disponible, 0, ',', ' ') . ' Ar<br>' .
|
|
'• 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 . ")<br>" .
|
|
"Store: " . $this->returnStoreName($user['store_id']) . "<br>" .
|
|
"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<br>' .
|
|
'Mode de paiement: ' . $mode_paiement . '<br>' .
|
|
'Nouveau solde ' . $mode_paiement_label . ': ' . number_format($fonds_disponible - $montant_retire, 0, ',', ' ') . ' Ar<br>' .
|
|
'<em>Notification envoyée à tous les Direction, DAF et SuperAdmin</em>'
|
|
]);
|
|
|
|
} 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.<br>' .
|
|
'<strong>Disponible:</strong> ' . number_format($fonds_disponible, 0, ',', ' ') . ' Ar<br>' .
|
|
'<strong>Demandé:</strong> ' . 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 !<br>' .
|
|
'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<br>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<br>Store: " . $this->returnStoreName($store_id) . "<br>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<br>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()
|
|
]);
|
|
}
|
|
}
|
|
}
|