25 changed files with 5883 additions and 2047 deletions
@ -0,0 +1,571 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Controllers; |
||||
|
|
||||
|
use App\Models\AutresEncaissements; |
||||
|
use App\Models\Stores; |
||||
|
use App\Models\Users; |
||||
|
|
||||
|
class AutresEncaissementsController extends AdminController |
||||
|
{ |
||||
|
public function __construct() |
||||
|
{ |
||||
|
parent::__construct(); |
||||
|
} |
||||
|
|
||||
|
private $pageTitle = 'Autres Encaissements'; |
||||
|
|
||||
|
/** |
||||
|
* Page principale - Liste des encaissements |
||||
|
*/ |
||||
|
public function index() |
||||
|
{ |
||||
|
$this->verifyRole('viewEncaissement'); |
||||
|
|
||||
|
$session = session(); |
||||
|
$user = $session->get('user'); |
||||
|
|
||||
|
$data['page_title'] = $this->pageTitle; |
||||
|
$data['user_role'] = $user['group_name']; |
||||
|
$data['user_permission'] = $this->permission; |
||||
|
|
||||
|
// Récupérer les magasins pour le filtre |
||||
|
$storeModel = new Stores(); |
||||
|
$data['stores'] = $storeModel->getActiveStore(); |
||||
|
|
||||
|
return $this->render_template('autres_encaissements/index', $data); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Ajouter un encaissement |
||||
|
*/ |
||||
|
public function create() |
||||
|
{ |
||||
|
if ($this->request->getMethod() !== 'post') { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Méthode non autorisée' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
$this->verifyRole('createEncaissement'); |
||||
|
|
||||
|
$session = session(); |
||||
|
$user = $session->get('user'); |
||||
|
|
||||
|
// Validation des données |
||||
|
$validation = \Config\Services::validation(); |
||||
|
$validation->setRules([ |
||||
|
'type_encaissement' => 'required', |
||||
|
'montant' => 'required|decimal', |
||||
|
'mode_paiement' => 'required|in_list[Espèces,MVola,Virement Bancaire]', |
||||
|
'commentaire' => 'permit_empty|string' |
||||
|
]); |
||||
|
|
||||
|
if (!$validation->withRequest($this->request)->run()) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => $validation->getErrors() |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
$Notification = new NotificationController(); |
||||
|
|
||||
|
$typeEncaissement = $this->request->getPost('type_encaissement'); |
||||
|
$autreType = $this->request->getPost('autre_type'); |
||||
|
|
||||
|
// Si "Autre" est sélectionné, utiliser le champ texte |
||||
|
$finalType = ($typeEncaissement === 'Autre' && !empty($autreType)) |
||||
|
? $autreType |
||||
|
: $typeEncaissement; |
||||
|
|
||||
|
$data = [ |
||||
|
'type_encaissement' => $finalType, |
||||
|
'autre_type' => $autreType, |
||||
|
'montant' => $this->request->getPost('montant'), |
||||
|
'mode_paiement' => $this->request->getPost('mode_paiement'), |
||||
|
'commentaire' => $this->request->getPost('commentaire'), |
||||
|
'user_id' => $user['id'], |
||||
|
'store_id' => $user['store_id'] |
||||
|
]; |
||||
|
|
||||
|
if ($encaissementId = $encaissementModel->insert($data)) { |
||||
|
|
||||
|
// ✅ Récupérer tous les stores pour les notifications |
||||
|
$db = \Config\Database::connect(); |
||||
|
$storesQuery = $db->table('stores')->select('id')->get(); |
||||
|
$allStores = $storesQuery->getResultArray(); |
||||
|
|
||||
|
$montantFormate = number_format((float)$this->request->getPost('montant'), 0, ',', ' '); |
||||
|
|
||||
|
$notificationMessage = "Nouvel encaissement {$finalType} créé par {$user['firstname']} {$user['lastname']} - Montant: {$montantFormate} Ar"; |
||||
|
|
||||
|
// ✅ Envoyer notification à DAF, Direction et SuperAdmin de TOUS les stores |
||||
|
foreach ($allStores as $store) { |
||||
|
$storeId = (int)$store['id']; |
||||
|
|
||||
|
// Notification pour DAF |
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"DAF", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
|
||||
|
// Notification pour Direction |
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"Direction", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
|
||||
|
// Notification pour SuperAdmin |
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"SuperAdmin", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
log_message('info', "✅ Encaissement {$encaissementId} créé - Notifications envoyées à DAF/Direction/SuperAdmin de tous les stores"); |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'messages' => 'Encaissement enregistré avec succès' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur lors de l\'enregistrement' |
||||
|
]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', "Erreur création encaissement: " . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Une erreur interne est survenue: ' . $e->getMessage() |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Modifier un encaissement |
||||
|
*/ |
||||
|
public function update($id) |
||||
|
{ |
||||
|
try { |
||||
|
$this->verifyRole('updateEncaissement'); |
||||
|
|
||||
|
$session = session(); |
||||
|
$user = $session->get('user'); |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
$Notification = new NotificationController(); |
||||
|
|
||||
|
// Récupérer l'ancien encaissement pour comparaison |
||||
|
$oldEncaissement = $encaissementModel->find($id); |
||||
|
|
||||
|
if (!$oldEncaissement) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Encaissement non trouvé' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
$typeEncaissement = $this->request->getPost('type_encaissement'); |
||||
|
$autreType = $this->request->getPost('autre_type'); |
||||
|
|
||||
|
$finalType = ($typeEncaissement === 'Autre' && !empty($autreType)) |
||||
|
? $autreType |
||||
|
: $typeEncaissement; |
||||
|
|
||||
|
$data = [ |
||||
|
'type_encaissement' => $finalType, |
||||
|
'autre_type' => $autreType, |
||||
|
'montant' => $this->request->getPost('montant'), |
||||
|
'mode_paiement' => $this->request->getPost('mode_paiement'), |
||||
|
'commentaire' => $this->request->getPost('commentaire') |
||||
|
]; |
||||
|
|
||||
|
if ($encaissementModel->update($id, $data)) { |
||||
|
|
||||
|
// ✅ Envoyer notification de modification à tous les stores |
||||
|
$db = \Config\Database::connect(); |
||||
|
$storesQuery = $db->table('stores')->select('id')->get(); |
||||
|
$allStores = $storesQuery->getResultArray(); |
||||
|
|
||||
|
$montantFormate = number_format((float)$this->request->getPost('montant'), 0, ',', ' '); |
||||
|
$ancienMontant = number_format((float)$oldEncaissement['montant'], 0, ',', ' '); |
||||
|
|
||||
|
$notificationMessage = "Encaissement {$finalType} modifié par {$user['firstname']} {$user['lastname']} - Ancien montant: {$ancienMontant} Ar → Nouveau: {$montantFormate} Ar"; |
||||
|
|
||||
|
foreach ($allStores as $store) { |
||||
|
$storeId = (int)$store['id']; |
||||
|
|
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"DAF", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
|
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"Direction", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
|
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"SuperAdmin", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
log_message('info', "✅ Encaissement {$id} modifié - Notifications envoyées"); |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'messages' => 'Encaissement modifié avec succès' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur lors de la modification' |
||||
|
]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', 'Erreur dans update(): ' . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur serveur: ' . $e->getMessage() |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Supprimer un encaissement |
||||
|
*/ |
||||
|
public function delete($id) |
||||
|
{ |
||||
|
try { |
||||
|
$this->verifyRole('deleteEncaissement'); |
||||
|
|
||||
|
$session = session(); |
||||
|
$user = $session->get('user'); |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
$Notification = new NotificationController(); |
||||
|
|
||||
|
// Récupérer l'encaissement avant suppression pour la notification |
||||
|
$encaissement = $encaissementModel->find($id); |
||||
|
|
||||
|
if (!$encaissement) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Encaissement non trouvé' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
if ($encaissementModel->delete($id)) { |
||||
|
|
||||
|
// ✅ Envoyer notification de suppression à tous les stores |
||||
|
$db = \Config\Database::connect(); |
||||
|
$storesQuery = $db->table('stores')->select('id')->get(); |
||||
|
$allStores = $storesQuery->getResultArray(); |
||||
|
|
||||
|
$montantFormate = number_format((float)$encaissement['montant'], 0, ',', ' '); |
||||
|
$type = $encaissement['type_encaissement']; |
||||
|
|
||||
|
$notificationMessage = "⚠️ Encaissement {$type} supprimé par {$user['firstname']} {$user['lastname']} - Montant: {$montantFormate} Ar"; |
||||
|
|
||||
|
foreach ($allStores as $store) { |
||||
|
$storeId = (int)$store['id']; |
||||
|
|
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"DAF", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
|
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"Direction", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
|
||||
|
$Notification->createNotification( |
||||
|
$notificationMessage, |
||||
|
"SuperAdmin", |
||||
|
$storeId, |
||||
|
'encaissements' |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
log_message('info', "✅ Encaissement {$id} supprimé - Notifications envoyées"); |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'messages' => 'Encaissement supprimé avec succès' |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur lors de la suppression' |
||||
|
]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', 'Erreur dans delete(): ' . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'messages' => 'Erreur serveur: ' . $e->getMessage() |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Récupérer les données pour DataTables |
||||
|
*/ |
||||
|
public function fetchEncaissements() |
||||
|
{ |
||||
|
try { |
||||
|
$this->verifyRole('viewEncaissement'); |
||||
|
|
||||
|
$session = session(); |
||||
|
$user = $session->get('user'); |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
|
||||
|
// Récupérer les filtres |
||||
|
$startDate = $this->request->getGet('startDate'); |
||||
|
$endDate = $this->request->getGet('endDate'); |
||||
|
$storeId = $this->request->getGet('store_id'); |
||||
|
|
||||
|
// Si l'utilisateur n'est pas admin, limiter à son magasin |
||||
|
if (!in_array($user['group_name'], ['SuperAdmin', 'DAF', 'Direction'])) { |
||||
|
$storeId = $user['store_id']; |
||||
|
} |
||||
|
|
||||
|
$encaissements = $encaissementModel->getEncaissementsWithDetails($storeId); |
||||
|
|
||||
|
// Filtrer par dates |
||||
|
if ($startDate || $endDate) { |
||||
|
$encaissements = array_filter($encaissements, function($item) use ($startDate, $endDate) { |
||||
|
$itemDate = date('Y-m-d', strtotime($item['created_at'])); |
||||
|
|
||||
|
if ($startDate && $itemDate < $startDate) { |
||||
|
return false; |
||||
|
} |
||||
|
if ($endDate && $itemDate > $endDate) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
$result = []; |
||||
|
|
||||
|
foreach ($encaissements as $item) { |
||||
|
// Construire les boutons avec permissions |
||||
|
$buttons = ''; |
||||
|
|
||||
|
if (in_array('viewEncaissement', $this->permission)) { |
||||
|
$buttons .= '<button class="btn btn-info btn-sm" onclick="viewDetails(' . $item['id'] . ')"> |
||||
|
<i class="fa fa-eye"></i> Détails |
||||
|
</button> '; |
||||
|
} |
||||
|
|
||||
|
if (in_array('updateEncaissement', $this->permission)) { |
||||
|
$buttons .= '<button class="btn btn-warning btn-sm" onclick="editEncaissement(' . $item['id'] . ')"> |
||||
|
<i class="fa fa-edit"></i> |
||||
|
</button> '; |
||||
|
} |
||||
|
|
||||
|
if (in_array('deleteEncaissement', $this->permission)) { |
||||
|
$buttons .= '<button class="btn btn-danger btn-sm" onclick="deleteEncaissement(' . $item['id'] . ')"> |
||||
|
<i class="fa fa-trash"></i> |
||||
|
</button>'; |
||||
|
} |
||||
|
|
||||
|
// ✅ Badge coloré pour le mode de paiement |
||||
|
$badgeClass = match($item['mode_paiement']) { |
||||
|
'Espèces' => 'success', |
||||
|
'MVola' => 'warning', |
||||
|
'Virement Bancaire' => 'info', |
||||
|
default => 'default' |
||||
|
}; |
||||
|
|
||||
|
$modePaiementBadge = '<span class="label label-' . $badgeClass . '">' . $item['mode_paiement'] . '</span>'; |
||||
|
|
||||
|
$result[] = [ |
||||
|
$item['id'], |
||||
|
$item['type_encaissement'], |
||||
|
number_format($item['montant'], 0, '.', ' ') . ' Ar', |
||||
|
$modePaiementBadge, // ✅ NOUVELLE COLONNE |
||||
|
$item['commentaire'] ?: 'Aucun', |
||||
|
$item['user_name'] ?: 'N/A', |
||||
|
$item['store_name'] ?: 'N/A', |
||||
|
date('d/m/Y H:i', strtotime($item['created_at'])), |
||||
|
$buttons |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
return $this->response->setJSON(['data' => $result]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', 'Erreur dans fetchEncaissements(): ' . $e->getMessage()); |
||||
|
return $this->response->setJSON(['data' => []]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Récupérer les détails d'un encaissement |
||||
|
*/ |
||||
|
public function getDetails($id) |
||||
|
{ |
||||
|
try { |
||||
|
$this->verifyRole('viewEncaissement'); |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
$encaissement = $encaissementModel->getEncaissementById($id); |
||||
|
|
||||
|
if ($encaissement) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'data' => $encaissement |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'message' => 'Encaissement non trouvé' |
||||
|
]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', 'Erreur dans getDetails(): ' . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'message' => 'Erreur serveur' |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Récupérer un encaissement pour édition |
||||
|
*/ |
||||
|
public function getEncaissement($id) |
||||
|
{ |
||||
|
try { |
||||
|
$this->verifyRole('updateEncaissement'); |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
$encaissement = $encaissementModel->find($id); |
||||
|
|
||||
|
if ($encaissement) { |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'data' => $encaissement |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'message' => 'Encaissement non trouvé' |
||||
|
]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', 'Erreur dans getEncaissement(): ' . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'message' => 'Erreur serveur' |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Récupérer les statistiques |
||||
|
*/ |
||||
|
public function getStatistics() |
||||
|
{ |
||||
|
try { |
||||
|
$this->verifyRole('viewEncaissement'); |
||||
|
|
||||
|
$session = session(); |
||||
|
$user = $session->get('user'); |
||||
|
|
||||
|
$encaissementModel = new AutresEncaissements(); |
||||
|
|
||||
|
$startDate = $this->request->getGet('startDate'); |
||||
|
$endDate = $this->request->getGet('endDate'); |
||||
|
$storeId = $this->request->getGet('store_id'); |
||||
|
|
||||
|
// ✅ LOG pour débogage |
||||
|
log_message('info', "📊 getStatistics appelé - Dates: {$startDate} à {$endDate}, Store: {$storeId}"); |
||||
|
|
||||
|
// Si l'utilisateur n'est pas admin, limiter à son magasin |
||||
|
if (!in_array($user['group_name'], ['SuperAdmin', 'DAF', 'Direction'])) { |
||||
|
$storeId = $user['store_id']; |
||||
|
} |
||||
|
|
||||
|
// ✅ IMPORTANT : Convertir $storeId en NULL si vide |
||||
|
$storeIdFilter = (!empty($storeId) && $storeId !== '') ? (int)$storeId : null; |
||||
|
|
||||
|
// Récupérer les totaux |
||||
|
$totalMontant = $encaissementModel->getTotalEncaissements($storeIdFilter, $startDate, $endDate); |
||||
|
$totalCount = $encaissementModel->getTotalCount($storeIdFilter, $startDate, $endDate); |
||||
|
$todayCount = $encaissementModel->getTodayCount($storeIdFilter); |
||||
|
|
||||
|
// Récupérer les totaux par mode de paiement |
||||
|
$totauxParMode = $encaissementModel->getTotalEncaissementsByMode($storeIdFilter, $startDate, $endDate); |
||||
|
|
||||
|
// Statistiques par type |
||||
|
$statsByType = $encaissementModel->getStatsByType($storeIdFilter, $startDate, $endDate); |
||||
|
|
||||
|
// ✅ LOG pour vérifier les valeurs |
||||
|
log_message('info', "✅ Résultats - Total: {$totalMontant}, Count: {$totalCount}, Today: {$todayCount}"); |
||||
|
|
||||
|
return $this->response->setJSON([ |
||||
|
'success' => true, |
||||
|
'total_montant' => number_format($totalMontant, 0, ',', ' ') . ' Ar', |
||||
|
'total_count' => $totalCount, |
||||
|
'today_count' => $todayCount, |
||||
|
'total_espece' => number_format($totauxParMode['total_espece'], 0, ',', ' ') . ' Ar', |
||||
|
'total_mvola' => number_format($totauxParMode['total_mvola'], 0, ',', ' ') . ' Ar', |
||||
|
'total_virement' => number_format($totauxParMode['total_virement'], 0, ',', ' ') . ' Ar', |
||||
|
'stats_by_type' => $statsByType, |
||||
|
// ✅ Ajouter ces infos pour débogage |
||||
|
'debug' => [ |
||||
|
'store_id' => $storeIdFilter, |
||||
|
'start_date' => $startDate, |
||||
|
'end_date' => $endDate |
||||
|
] |
||||
|
]); |
||||
|
|
||||
|
} catch (\Exception $e) { |
||||
|
log_message('error', '❌ Erreur dans getStatistics(): ' . $e->getMessage()); |
||||
|
return $this->response->setJSON([ |
||||
|
'success' => false, |
||||
|
'total_montant' => '0 Ar', |
||||
|
'total_count' => 0, |
||||
|
'today_count' => 0, |
||||
|
'total_espece' => '0 Ar', |
||||
|
'total_mvola' => '0 Ar', |
||||
|
'total_virement' => '0 Ar', |
||||
|
'stats_by_type' => [], |
||||
|
'error' => $e->getMessage() |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,302 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Models; |
||||
|
|
||||
|
use CodeIgniter\Model; |
||||
|
|
||||
|
class AutresEncaissements extends Model |
||||
|
{ |
||||
|
protected $table = 'autres_encaissements'; |
||||
|
protected $primaryKey = 'id'; |
||||
|
protected $allowedFields = [ |
||||
|
'type_encaissement', |
||||
|
'autre_type', |
||||
|
'montant', |
||||
|
'mode_paiement', |
||||
|
'commentaire', |
||||
|
'user_id', |
||||
|
'store_id', |
||||
|
'created_at', |
||||
|
'updated_at' |
||||
|
]; |
||||
|
protected $useTimestamps = true; |
||||
|
protected $createdField = 'created_at'; |
||||
|
protected $updatedField = 'updated_at'; |
||||
|
|
||||
|
/** |
||||
|
* Récupérer tous les encaissements avec les infos utilisateur et magasin |
||||
|
* |
||||
|
* @param int|null $storeId ID du magasin (optionnel) |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getEncaissementsWithDetails($storeId = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->select(' |
||||
|
autres_encaissements.*, |
||||
|
CONCAT(users.firstname, " ", users.lastname) as user_name, |
||||
|
users.email as user_email, |
||||
|
stores.name as store_name |
||||
|
'); |
||||
|
$builder->join('users', 'users.id = autres_encaissements.user_id', 'left'); |
||||
|
$builder->join('stores', 'stores.id = autres_encaissements.store_id', 'left'); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('autres_encaissements.store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
$builder->orderBy('autres_encaissements.created_at', 'DESC'); |
||||
|
|
||||
|
return $builder->get()->getResultArray(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Récupérer un encaissement par ID avec détails |
||||
|
* |
||||
|
* @param int $id ID de l'encaissement |
||||
|
* @return array|null |
||||
|
*/ |
||||
|
public function getTotalEncaissementsByMode($storeId = null, $startDate = null, $endDate = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->select(' |
||||
|
SUM(CASE WHEN mode_paiement = "Espèces" THEN montant ELSE 0 END) as total_espece, |
||||
|
SUM(CASE WHEN mode_paiement = "MVola" THEN montant ELSE 0 END) as total_mvola, |
||||
|
SUM(CASE WHEN mode_paiement = "Virement Bancaire" THEN montant ELSE 0 END) as total_virement |
||||
|
'); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
if ($startDate) { |
||||
|
$builder->where('DATE(created_at) >=', $startDate); |
||||
|
} |
||||
|
|
||||
|
if ($endDate) { |
||||
|
$builder->where('DATE(created_at) <=', $endDate); |
||||
|
} |
||||
|
|
||||
|
$result = $builder->get()->getRow(); |
||||
|
|
||||
|
return [ |
||||
|
'total_espece' => $result ? (float)$result->total_espece : 0, |
||||
|
'total_mvola' => $result ? (float)$result->total_mvola : 0, |
||||
|
'total_virement' => $result ? (float)$result->total_virement : 0, |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function getEncaissementById($id) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->select(' |
||||
|
autres_encaissements.*, |
||||
|
CONCAT(users.firstname, " ", users.lastname) as user_name, |
||||
|
users.email as user_email, |
||||
|
stores.name as store_name |
||||
|
'); |
||||
|
$builder->join('users', 'users.id = autres_encaissements.user_id', 'left'); |
||||
|
$builder->join('stores', 'stores.id = autres_encaissements.store_id', 'left'); |
||||
|
$builder->where('autres_encaissements.id', $id); |
||||
|
|
||||
|
return $builder->get()->getRowArray(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Statistiques par type d'encaissement |
||||
|
* |
||||
|
* @param int|null $storeId ID du magasin |
||||
|
* @param string|null $startDate Date de début (Y-m-d) |
||||
|
* @param string|null $endDate Date de fin (Y-m-d) |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getStatsByType($storeId = null, $startDate = null, $endDate = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
|
||||
|
// ✅ SÉLECTION DES CHAMPS (sans commentaire dans le SELECT) |
||||
|
$builder->select(' |
||||
|
type_encaissement, |
||||
|
mode_paiement, |
||||
|
COUNT(*) as total_count, |
||||
|
SUM(montant) as total_montant |
||||
|
'); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
if ($startDate) { |
||||
|
$builder->where('DATE(created_at) >=', $startDate); |
||||
|
} |
||||
|
|
||||
|
if ($endDate) { |
||||
|
$builder->where('DATE(created_at) <=', $endDate); |
||||
|
} |
||||
|
|
||||
|
// ✅ GROUPER PAR type_encaissement ET mode_paiement |
||||
|
$builder->groupBy('type_encaissement, mode_paiement'); |
||||
|
|
||||
|
return $builder->get()->getResultArray(); |
||||
|
} |
||||
|
/** |
||||
|
* Total des encaissements |
||||
|
* |
||||
|
* @param int|null $storeId ID du magasin |
||||
|
* @param string|null $startDate Date de début (Y-m-d) |
||||
|
* @param string|null $endDate Date de fin (Y-m-d) |
||||
|
* @return float |
||||
|
*/ |
||||
|
public function getTotalEncaissements($storeId = null, $startDate = null, $endDate = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->selectSum('montant', 'total'); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
if ($startDate) { |
||||
|
$builder->where('DATE(created_at) >=', $startDate); |
||||
|
} |
||||
|
|
||||
|
if ($endDate) { |
||||
|
$builder->where('DATE(created_at) <=', $endDate); |
||||
|
} |
||||
|
|
||||
|
$result = $builder->get()->getRow(); |
||||
|
return $result ? (float)$result->total : 0; |
||||
|
} |
||||
|
/** |
||||
|
* Nombre d'encaissements aujourd'hui |
||||
|
* |
||||
|
* @param int|null $storeId ID du magasin |
||||
|
* @return int |
||||
|
*/ |
||||
|
public function getTodayCount($storeId = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->where('DATE(created_at)', date('Y-m-d')); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
return $builder->countAllResults(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Nombre total d'encaissements |
||||
|
* |
||||
|
* @param int|null $storeId ID du magasin |
||||
|
* @param string|null $startDate Date de début (Y-m-d) |
||||
|
* @param string|null $endDate Date de fin (Y-m-d) |
||||
|
* @return int |
||||
|
*/ |
||||
|
public function getTotalCount($storeId = null, $startDate = null, $endDate = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
if ($startDate) { |
||||
|
$builder->where('DATE(created_at) >=', $startDate); |
||||
|
} |
||||
|
|
||||
|
if ($endDate) { |
||||
|
$builder->where('DATE(created_at) <=', $endDate); |
||||
|
} |
||||
|
|
||||
|
return $builder->countAllResults(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Encaissements récents (7 derniers jours) |
||||
|
* |
||||
|
* @param int|null $storeId ID du magasin |
||||
|
* @param int $limit Nombre de résultats |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getRecentEncaissements($storeId = null, $limit = 10) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->select(' |
||||
|
autres_encaissements.*, |
||||
|
CONCAT(users.firstname, " ", users.lastname) as user_name, |
||||
|
stores.name as store_name |
||||
|
'); |
||||
|
$builder->join('users', 'users.id = autres_encaissements.user_id', 'left'); |
||||
|
$builder->join('stores', 'stores.id = autres_encaissements.store_id', 'left'); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('autres_encaissements.store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
$builder->where('DATE(autres_encaissements.created_at) >=', date('Y-m-d', strtotime('-7 days'))); |
||||
|
$builder->orderBy('autres_encaissements.created_at', 'DESC'); |
||||
|
$builder->limit($limit); |
||||
|
|
||||
|
return $builder->get()->getResultArray(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Encaissements par utilisateur |
||||
|
* |
||||
|
* @param int $userId ID de l'utilisateur |
||||
|
* @param string|null $startDate Date de début |
||||
|
* @param string|null $endDate Date de fin |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getEncaissementsByUser($userId, $startDate = null, $endDate = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->where('user_id', $userId); |
||||
|
|
||||
|
if ($startDate) { |
||||
|
$builder->where('DATE(created_at) >=', $startDate); |
||||
|
} |
||||
|
|
||||
|
if ($endDate) { |
||||
|
$builder->where('DATE(created_at) <=', $endDate); |
||||
|
} |
||||
|
|
||||
|
$builder->orderBy('created_at', 'DESC'); |
||||
|
|
||||
|
return $builder->get()->getResultArray(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Recherche d'encaissements |
||||
|
* |
||||
|
* @param string $keyword Mot-clé de recherche |
||||
|
* @param int|null $storeId ID du magasin |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function searchEncaissements($keyword, $storeId = null) |
||||
|
{ |
||||
|
$builder = $this->db->table($this->table); |
||||
|
$builder->select(' |
||||
|
autres_encaissements.*, |
||||
|
CONCAT(users.firstname, " ", users.lastname) as user_name, |
||||
|
stores.name as store_name |
||||
|
'); |
||||
|
$builder->join('users', 'users.id = autres_encaissements.user_id', 'left'); |
||||
|
$builder->join('stores', 'stores.id = autres_encaissements.store_id', 'left'); |
||||
|
|
||||
|
$builder->groupStart() |
||||
|
->like('autres_encaissements.type_encaissement', $keyword) |
||||
|
->orLike('autres_encaissements.commentaire', $keyword) |
||||
|
->orLike('CONCAT(users.firstname, " ", users.lastname)', $keyword) |
||||
|
->groupEnd(); |
||||
|
|
||||
|
if ($storeId) { |
||||
|
$builder->where('autres_encaissements.store_id', $storeId); |
||||
|
} |
||||
|
|
||||
|
$builder->orderBy('autres_encaissements.created_at', 'DESC'); |
||||
|
|
||||
|
return $builder->get()->getResultArray(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,978 @@ |
|||||
|
<!-- application/Views/autres_encaissements/index.php --> |
||||
|
<style> |
||||
|
.encaissement-header { |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
border-radius: 15px; |
||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); |
||||
|
overflow: hidden; |
||||
|
margin: 20px 0; |
||||
|
color: white; |
||||
|
padding: 30px; |
||||
|
} |
||||
|
|
||||
|
.encaissement-header h1 { |
||||
|
margin: 0; |
||||
|
font-size: 28px; |
||||
|
font-weight: 700; |
||||
|
color : white; |
||||
|
} |
||||
|
|
||||
|
.stat-card { |
||||
|
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%); |
||||
|
border-radius: 12px; |
||||
|
padding: 25px; |
||||
|
transition: all 0.3s ease; |
||||
|
border-left: 5px solid #2196f3; |
||||
|
text-align: center; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.stat-card:hover { |
||||
|
transform: translateY(-5px); |
||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.stat-number { |
||||
|
font-size: 32px; |
||||
|
font-weight: 800; |
||||
|
color: #1976d2; |
||||
|
margin: 10px 0; |
||||
|
} |
||||
|
|
||||
|
.stat-label { |
||||
|
font-size: 14px; |
||||
|
font-weight: 600; |
||||
|
text-transform: uppercase; |
||||
|
color: #1565c0; |
||||
|
letter-spacing: 0.5px; |
||||
|
} |
||||
|
|
||||
|
.form-section { |
||||
|
background: white; |
||||
|
border-radius: 12px; |
||||
|
padding: 30px; |
||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
|
||||
|
.form-section h3 { |
||||
|
color: #667eea; |
||||
|
font-weight: 700; |
||||
|
margin-bottom: 25px; |
||||
|
padding-bottom: 15px; |
||||
|
border-bottom: 3px solid #667eea; |
||||
|
} |
||||
|
|
||||
|
.form-control:focus { |
||||
|
border-color: #667eea; |
||||
|
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25); |
||||
|
} |
||||
|
|
||||
|
.btn-primary-custom { |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
border: none; |
||||
|
padding: 12px 30px; |
||||
|
font-weight: 600; |
||||
|
border-radius: 8px; |
||||
|
transition: all 0.3s ease; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.btn-primary-custom:hover { |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
#historyTable thead { |
||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
#historyTable thead th { |
||||
|
border: none !important; |
||||
|
font-weight: 600; |
||||
|
text-transform: uppercase; |
||||
|
font-size: 13px; |
||||
|
letter-spacing: 0.5px; |
||||
|
} |
||||
|
|
||||
|
#historyTable tbody tr { |
||||
|
transition: all 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
#historyTable tbody tr:hover { |
||||
|
background-color: #f5f5f5; |
||||
|
transform: scale(1.01); |
||||
|
} |
||||
|
|
||||
|
.filter-section { |
||||
|
background: #f8f9fa; |
||||
|
padding: 20px; |
||||
|
border-radius: 10px; |
||||
|
margin-bottom: 20px; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
||||
|
} |
||||
|
|
||||
|
#autreTypeGroup { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.alert-notification { |
||||
|
position: fixed; |
||||
|
top: 20px; |
||||
|
right: 20px; |
||||
|
z-index: 9999; |
||||
|
min-width: 300px; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<div class="content-wrapper"> |
||||
|
<!-- Content Header --> |
||||
|
<section class="content-header"> |
||||
|
<div class="encaissement-header"> |
||||
|
<h1> |
||||
|
<i class="fa fa-money"></i> Autres Encaissements |
||||
|
</h1> |
||||
|
<p style="margin: 10px 0 0 0; font-size: 16px; opacity: 0.9;"> |
||||
|
Gestion des encaissements divers (Plastification, Duplicata, etc.) |
||||
|
</p> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<!-- Main content --> |
||||
|
<section class="content"> |
||||
|
<div id="messages"></div> |
||||
|
|
||||
|
<!-- Statistiques --> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-4"> |
||||
|
<div class="stat-card"> |
||||
|
<i class="fa fa-money" style="font-size: 40px; color: #2196f3;"></i> |
||||
|
<div class="stat-number" id="totalMontant">0 Ar</div> |
||||
|
<div class="stat-label">Total Encaissements</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-4"> |
||||
|
<div class="stat-card"> |
||||
|
<i class="fa fa-calendar" style="font-size: 40px; color: #2196f3;"></i> |
||||
|
<div class="stat-number" id="todayCount">0</div> |
||||
|
<div class="stat-label">Encaissements Aujourd'hui</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Formulaire d'ajout --> |
||||
|
<?php if (in_array('createEncaissement', $user_permission)): ?> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="form-section"> |
||||
|
<h3><i class="fa fa-plus-circle"></i> Ajouter un Encaissement</h3> |
||||
|
|
||||
|
<form id="formEncaissement"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="form-group"> |
||||
|
<label for="typeEncaissement">Type d'Encaissement <span class="text-danger">*</span></label> |
||||
|
<select class="form-control" id="typeEncaissement" name="type_encaissement" required> |
||||
|
<option value="">-- Sélectionner --</option> |
||||
|
<option value="Plastification">Plastification</option> |
||||
|
<option value="Duplicata">Duplicata</option> |
||||
|
<option value="Retour Décaissement">Retour Décaissement</option> |
||||
|
<option value="Vente de Pièce">Vente de Pièce</option> |
||||
|
<option value="Autre">Autre (Spécifier)</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-3" id="autreTypeGroup"> |
||||
|
<div class="form-group"> |
||||
|
<label for="autreType">Spécifier le type <span class="text-danger">*</span></label> |
||||
|
<input type="text" class="form-control" id="autreType" name="autre_type" placeholder="Ex: Frais de dossier"> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-3"> |
||||
|
<div class="form-group"> |
||||
|
<label for="montant">Montant (Ar) <span class="text-danger">*</span></label> |
||||
|
<input type="number" class="form-control" id="montant" name="montant" placeholder="Ex: 50000" required min="0" step="0.01"> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ✅ NOUVEAU CHAMP MODE DE PAIEMENT --> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="form-group"> |
||||
|
<label for="modePaiement">Mode de Paiement <span class="text-danger">*</span></label> |
||||
|
<select class="form-control" id="modePaiement" name="mode_paiement" required> |
||||
|
<option value="">-- Sélectionner --</option> |
||||
|
<option value="Espèces">Espèces</option> |
||||
|
<option value="MVola">MVola</option> |
||||
|
<option value="Virement Bancaire">Virement Bancaire</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="form-group"> |
||||
|
<label for="commentaire">Commentaire</label> |
||||
|
<textarea class="form-control" id="commentaire" name="commentaire" rows="3" placeholder="Ajouter un commentaire (optionnel)"></textarea> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="text-right"> |
||||
|
<button type="reset" class="btn btn-default"> |
||||
|
<i class="fa fa-refresh"></i> Réinitialiser |
||||
|
</button> |
||||
|
<button type="submit" class="btn btn-primary"> |
||||
|
<i class="fa fa-save"></i> Enregistrer l'Encaissement |
||||
|
</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<?php endif; ?> |
||||
|
|
||||
|
<!-- Filtres --> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="filter-section"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-3"> |
||||
|
<label for="filterStartDate">Date de début</label> |
||||
|
<input type="date" id="filterStartDate" class="form-control"> |
||||
|
</div> |
||||
|
<div class="col-md-3"> |
||||
|
<label for="filterEndDate">Date de fin</label> |
||||
|
<input type="date" id="filterEndDate" class="form-control"> |
||||
|
</div> |
||||
|
<?php if (in_array($user_role, ['SuperAdmin', 'DAF', 'Direction'])): ?> |
||||
|
<div class="col-md-3"> |
||||
|
<label for="filterStore">Magasin</label> |
||||
|
<select id="filterStore" class="form-control"> |
||||
|
<option value="">Tous les magasins</option> |
||||
|
<?php foreach ($stores as $store): ?> |
||||
|
<option value="<?= $store['id'] ?>"><?= $store['name'] ?></option> |
||||
|
<?php endforeach; ?> |
||||
|
</select> |
||||
|
</div> |
||||
|
<?php endif; ?> |
||||
|
<div class="col-md-3"> |
||||
|
<label> </label><br> |
||||
|
<button id="btnFilter" class="btn btn-primary"> |
||||
|
<i class="fa fa-filter"></i> Filtrer |
||||
|
</button> |
||||
|
<button id="btnReset" class="btn btn-warning"> |
||||
|
<i class="fa fa-refresh"></i> Réinitialiser |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Tableau historique --> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="box"> |
||||
|
<div class="box-header with-border"> |
||||
|
<h3 class="box-title"> |
||||
|
<i class="fa fa-history"></i> Historique des Encaissements |
||||
|
</h3> |
||||
|
</div> |
||||
|
<div class="box-body"> |
||||
|
<div class="table-responsive"> |
||||
|
<table id="historyTable" class="table table-bordered table-striped table-hover"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>ID</th> |
||||
|
<th>Type</th> |
||||
|
<th>Montant</th> |
||||
|
<th>Mode</th> <!-- ✅ NOUVELLE COLONNE --> |
||||
|
<th>Commentaire</th> |
||||
|
<th>Créé par</th> |
||||
|
<th>Magasin</th> |
||||
|
<th>Date</th> |
||||
|
<th>Actions</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<!-- Les données seront chargées par DataTables --> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Modal Détails --> |
||||
|
<div class="modal fade" id="detailsModal" tabindex="-1"> |
||||
|
<div class="modal-dialog modal-lg"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;"> |
||||
|
<button type="button" class="close" data-dismiss="modal" style="color: white;">×</button> |
||||
|
<h4 class="modal-title"> |
||||
|
<i class="fa fa-info-circle"></i> Détails de l'Encaissement |
||||
|
</h4> |
||||
|
</div> |
||||
|
<div class="modal-body"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6"> |
||||
|
<h4 style="border-bottom: 2px solid #667eea; padding-bottom: 10px; margin-bottom: 20px;"> |
||||
|
<i class="fa fa-money"></i> Informations Encaissement |
||||
|
</h4> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>ID:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailId"></span></div> |
||||
|
</div> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Type:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailType" style="font-weight: 600; color: #667eea;"></span></div> |
||||
|
</div> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Montant:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailMontant" style="font-size: 22px; font-weight: bold; color: #2196f3;"></span></div> |
||||
|
</div> |
||||
|
<!-- ✅ NOUVEAU CHAMP --> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Mode de paiement:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailMode"></span></div> |
||||
|
</div> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Date:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailDate"></span></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<h4 style="border-bottom: 2px solid #667eea; padding-bottom: 10px; margin-bottom: 20px;"> |
||||
|
<i class="fa fa-user"></i> Informations Utilisateur |
||||
|
</h4> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Créé par:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailUser"></span></div> |
||||
|
</div> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Email:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailEmail"></span></div> |
||||
|
</div> |
||||
|
<div class="row" style="margin-bottom: 15px;"> |
||||
|
<div class="col-xs-5"><strong>Magasin:</strong></div> |
||||
|
<div class="col-xs-7"><span id="detailStore"></span></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="row" style="margin-top: 20px;"> |
||||
|
<div class="col-md-12"> |
||||
|
<h4 style="border-bottom: 2px solid #667eea; padding-bottom: 10px; margin-bottom: 20px;"> |
||||
|
<i class="fa fa-comment"></i> Commentaire |
||||
|
</h4> |
||||
|
<p id="detailCommentaire" style="padding: 15px; background: #f8f9fa; border-radius: 8px; min-height: 60px;"></p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-default" data-dismiss="modal"> |
||||
|
<i class="fa fa-times"></i> Fermer |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Modal Édition --> |
||||
|
<?php if (in_array('updateEncaissement', $user_permission)): ?> |
||||
|
<div class="modal fade" id="editModal" tabindex="-1"> |
||||
|
<div class="modal-dialog"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;"> |
||||
|
<button type="button" class="close" data-dismiss="modal" style="color: white;">×</button> |
||||
|
<h4 class="modal-title"> |
||||
|
<i class="fa fa-edit"></i> Modifier l'Encaissement |
||||
|
</h4> |
||||
|
</div> |
||||
|
<form id="formEditEncaissement"> |
||||
|
<div class="modal-body"> |
||||
|
<input type="hidden" id="editId" name="id"> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label for="editTypeEncaissement">Type d'Encaissement <span class="text-danger">*</span></label> |
||||
|
<select class="form-control" id="editTypeEncaissement" name="type_encaissement" required> |
||||
|
<option value="">-- Sélectionner --</option> |
||||
|
<option value="Plastification">Plastification</option> |
||||
|
<option value="Duplicata">Duplicata</option> |
||||
|
<option value="Retour Décaissement">Retour Décaissement</option> |
||||
|
<option value="Vente de Pièce">Vente de Pièce</option> |
||||
|
<option value="Autre">Autre (Spécifier)</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group" id="editAutreTypeGroup" style="display: none;"> |
||||
|
<label for="editAutreType">Spécifier le type</label> |
||||
|
<input type="text" class="form-control" id="editAutreType" name="autre_type" placeholder="Ex: Frais de dossier"> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label for="editMontant">Montant (Ar) <span class="text-danger">*</span></label> |
||||
|
<input type="number" class="form-control" id="editMontant" name="montant" required min="0" step="0.01"> |
||||
|
</div> |
||||
|
|
||||
|
<!-- ✅ NOUVEAU CHAMP MODE DE PAIEMENT --> |
||||
|
<div class="form-group"> |
||||
|
<label for="editModePaiement">Mode de Paiement <span class="text-danger">*</span></label> |
||||
|
<select class="form-control" id="editModePaiement" name="mode_paiement" required> |
||||
|
<option value="">-- Sélectionner --</option> |
||||
|
<option value="Espèces">Espèces</option> |
||||
|
<option value="MVola">MVola</option> |
||||
|
<option value="Virement Bancaire">Virement Bancaire</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label for="editCommentaire">Commentaire</label> |
||||
|
<textarea class="form-control" id="editCommentaire" name="commentaire" rows="3"></textarea> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-default" data-dismiss="modal"> |
||||
|
<i class="fa fa-times"></i> Annuler |
||||
|
</button> |
||||
|
<button type="submit" class="btn btn-primary-custom"> |
||||
|
<i class="fa fa-save"></i> Enregistrer les modifications |
||||
|
</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<?php endif; ?> |
||||
|
|
||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> |
||||
|
|
||||
|
<script> |
||||
|
// ✅ DÉCLARER LA FONCTION EN DEHORS DU $(function) POUR LA RENDRE GLOBALE |
||||
|
function loadStatisticsForFilteredPeriod(startDate, endDate, store_id) { |
||||
|
console.log('📊 [STATS] Début chargement statistiques'); |
||||
|
console.log('📊 [STATS] Paramètres:', { |
||||
|
startDate: startDate, |
||||
|
endDate: endDate, |
||||
|
store_id: store_id |
||||
|
}); |
||||
|
|
||||
|
$.ajax({ |
||||
|
url: '<?= base_url('encaissements/statistics') ?>', |
||||
|
type: 'GET', |
||||
|
data: { |
||||
|
startDate: startDate || '', |
||||
|
endDate: endDate || '', |
||||
|
store_id: store_id || '' |
||||
|
}, |
||||
|
beforeSend: function() { |
||||
|
console.log('📊 [STATS] Requête envoyée...'); |
||||
|
// Afficher un loader |
||||
|
$('#totalMontant').html('<i class="fa fa-spinner fa-spin"></i>'); |
||||
|
$('#todayCount').html('<i class="fa fa-spinner fa-spin"></i>'); |
||||
|
}, |
||||
|
success: function(response) { |
||||
|
console.log('✅ [STATS] Réponse reçue:', response); |
||||
|
|
||||
|
if (response.success) { |
||||
|
console.log('✅ [STATS] Mise à jour des valeurs:', { |
||||
|
total_montant: response.total_montant, |
||||
|
today_count: response.today_count, |
||||
|
total_count: response.total_count |
||||
|
}); |
||||
|
|
||||
|
// ✅ MISE À JOUR DES CARTES |
||||
|
$('#totalMontant').text(response.total_montant); |
||||
|
$('#todayCount').text(response.today_count); |
||||
|
|
||||
|
// Si tu ajoutes une carte pour total_count |
||||
|
if ($('#totalCount').length) { |
||||
|
$('#totalCount').text(response.total_count); |
||||
|
} |
||||
|
|
||||
|
// ✅ Si tu as des éléments pour afficher les totaux par mode |
||||
|
if ($('#totalEspece').length) { |
||||
|
$('#totalEspece').text(response.total_espece); |
||||
|
} |
||||
|
if ($('#totalMvola').length) { |
||||
|
$('#totalMvola').text(response.total_mvola); |
||||
|
} |
||||
|
if ($('#totalVirement').length) { |
||||
|
$('#totalVirement').text(response.total_virement); |
||||
|
} |
||||
|
|
||||
|
console.log('✅ [STATS] DOM mis à jour avec succès'); |
||||
|
|
||||
|
// Afficher les infos de debug si disponibles |
||||
|
if (response.debug) { |
||||
|
console.log('🔍 [DEBUG]', response.debug); |
||||
|
} |
||||
|
} else { |
||||
|
console.error('❌ [STATS] Erreur: response.success = false', response); |
||||
|
$('#totalMontant').text('Erreur'); |
||||
|
$('#todayCount').text('Erreur'); |
||||
|
} |
||||
|
}, |
||||
|
error: function(xhr, status, error) { |
||||
|
console.error('❌ [STATS] Erreur AJAX complète:', { |
||||
|
status: status, |
||||
|
error: error, |
||||
|
responseText: xhr.responseText, |
||||
|
statusCode: xhr.status |
||||
|
}); |
||||
|
|
||||
|
// Essayer de parser la réponse |
||||
|
try { |
||||
|
var errorResponse = JSON.parse(xhr.responseText); |
||||
|
console.error('❌ [STATS] Détails erreur:', errorResponse); |
||||
|
} catch(e) { |
||||
|
console.error('❌ [STATS] Impossible de parser la réponse'); |
||||
|
} |
||||
|
|
||||
|
$('#totalMontant').text('0 Ar'); |
||||
|
$('#todayCount').text('0'); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
$(function() { |
||||
|
console.log('🚀 [INIT] Initialisation de la page...'); |
||||
|
|
||||
|
$("#encaissementNav").addClass('active'); |
||||
|
|
||||
|
// Afficher/masquer le champ "Autre type" - Formulaire principal |
||||
|
$('#typeEncaissement').on('change', function() { |
||||
|
if ($(this).val() === 'Autre') { |
||||
|
$('#autreTypeGroup').slideDown(); |
||||
|
$('#autreType').prop('required', true); |
||||
|
} else { |
||||
|
$('#autreTypeGroup').slideUp(); |
||||
|
$('#autreType').prop('required', false).val(''); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Afficher/masquer le champ "Autre type" - Modal édition |
||||
|
$('#editTypeEncaissement').on('change', function() { |
||||
|
if ($(this).val() === 'Autre') { |
||||
|
$('#editAutreTypeGroup').slideDown(); |
||||
|
$('#editAutreType').prop('required', true); |
||||
|
} else { |
||||
|
$('#editAutreTypeGroup').slideUp(); |
||||
|
$('#editAutreType').prop('required', false).val(''); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Configuration DataTable en français |
||||
|
$.extend(true, $.fn.dataTable.defaults, { |
||||
|
language: { |
||||
|
sProcessing: "Traitement en cours...", |
||||
|
sSearch: "Rechercher :", |
||||
|
sLengthMenu: "Afficher _MENU_ éléments", |
||||
|
sInfo: "Affichage de _START_ à _END_ sur _TOTAL_ éléments", |
||||
|
sInfoEmpty: "Aucun élément à afficher", |
||||
|
sInfoFiltered: "(filtré de _MAX_ éléments au total)", |
||||
|
sLoadingRecords: "Chargement en cours...", |
||||
|
sZeroRecords: "Aucun encaissement trouvé", |
||||
|
sEmptyTable: "Aucune donnée disponible", |
||||
|
oPaginate: { |
||||
|
sFirst: "Premier", |
||||
|
sPrevious: "Précédent", |
||||
|
sNext: "Suivant", |
||||
|
sLast: "Dernier" |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// ✅ INITIALISER LES DATES À AUJOURD'HUI PAR DÉFAUT |
||||
|
var today = new Date().toISOString().split('T')[0]; |
||||
|
$('#filterStartDate').val(today); |
||||
|
$('#filterEndDate').val(today); |
||||
|
|
||||
|
console.log('📅 [INIT] Dates initialisées:', { |
||||
|
startDate: $('#filterStartDate').val(), |
||||
|
endDate: $('#filterEndDate').val() |
||||
|
}); |
||||
|
|
||||
|
// ✅ FONCTION : CHARGER LES STATISTIQUES DU MOIS EN COURS |
||||
|
function loadStatisticsForCurrentMonth() { |
||||
|
console.log('📊 [INIT] Chargement des statistiques du mois en cours...'); |
||||
|
|
||||
|
var now = new Date(); |
||||
|
var firstDay = new Date(now.getFullYear(), now.getMonth(), 1); |
||||
|
var lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0); |
||||
|
|
||||
|
var startDate = firstDay.toISOString().split('T')[0]; |
||||
|
var endDate = lastDay.toISOString().split('T')[0]; |
||||
|
var store_id = $('#filterStore').val() || ''; |
||||
|
|
||||
|
console.log('📅 [INIT] Période du mois:', { |
||||
|
firstDay: startDate, |
||||
|
lastDay: endDate, |
||||
|
store_id: store_id |
||||
|
}); |
||||
|
|
||||
|
// ✅ Appeler la fonction globale |
||||
|
loadStatisticsForFilteredPeriod(startDate, endDate, store_id); |
||||
|
} |
||||
|
|
||||
|
// ✅ INITIALISATION DATATABLE |
||||
|
var historyTable = $('#historyTable').DataTable({ |
||||
|
ajax: { |
||||
|
url: '<?= base_url('encaissements/fetch') ?>', |
||||
|
type: 'GET', |
||||
|
data: function(d) { |
||||
|
d.startDate = $('#filterStartDate').val(); |
||||
|
d.endDate = $('#filterEndDate').val(); |
||||
|
d.store_id = $('#filterStore').val(); |
||||
|
|
||||
|
console.log('📤 [DATATABLE] Filtres envoyés:', { |
||||
|
startDate: d.startDate, |
||||
|
endDate: d.endDate, |
||||
|
store_id: d.store_id |
||||
|
}); |
||||
|
}, |
||||
|
dataSrc: function(json) { |
||||
|
console.log('📥 [DATATABLE] Données reçues:', json.data.length, 'lignes'); |
||||
|
return json.data; |
||||
|
}, |
||||
|
error: function(xhr, error, code) { |
||||
|
console.error('❌ [DATATABLE] Erreur:', error); |
||||
|
showAlert('danger', 'Erreur lors du chargement des données'); |
||||
|
} |
||||
|
}, |
||||
|
columns: [ |
||||
|
{ data: 0 }, // ID |
||||
|
{ data: 1 }, // Type |
||||
|
{ data: 2 }, // Montant |
||||
|
{ data: 3 }, // Mode de paiement |
||||
|
{ data: 4 }, // Commentaire |
||||
|
{ data: 5 }, // Utilisateur |
||||
|
{ data: 6 }, // Magasin |
||||
|
{ data: 7 }, // Date |
||||
|
{ data: 8, orderable: false } // Actions |
||||
|
], |
||||
|
order: [[7, 'desc']], |
||||
|
pageLength: 10, |
||||
|
lengthMenu: [[5, 10, 25, 50, -1], [5, 10, 25, 50, "Tout"]] |
||||
|
}); |
||||
|
|
||||
|
console.log('✅ [INIT] DataTable initialisée'); |
||||
|
|
||||
|
// ✅ CHARGER LES STATISTIQUES DU MOIS AU DÉMARRAGE |
||||
|
console.log('🚀 [INIT] Appel de loadStatisticsForCurrentMonth()'); |
||||
|
loadStatisticsForCurrentMonth(); |
||||
|
|
||||
|
// ✅ BOUTON FILTRER - RECHARGE TABLEAU + STATISTIQUES |
||||
|
$('#btnFilter').on('click', function() { |
||||
|
console.log('🔍 [FILTER] Bouton Filtrer cliqué'); |
||||
|
|
||||
|
var startDate = $('#filterStartDate').val(); |
||||
|
var endDate = $('#filterEndDate').val(); |
||||
|
var store_id = $('#filterStore').val(); |
||||
|
|
||||
|
console.log('📋 [FILTER] Filtres appliqués:', { |
||||
|
startDate: startDate, |
||||
|
endDate: endDate, |
||||
|
store_id: store_id |
||||
|
}); |
||||
|
|
||||
|
// ✅ RECHARGER LE TABLEAU |
||||
|
historyTable.ajax.reload(); |
||||
|
|
||||
|
// ✅ RECHARGER LES STATISTIQUES SELON LA PÉRIODE FILTRÉE |
||||
|
loadStatisticsForFilteredPeriod(startDate, endDate, store_id); |
||||
|
}); |
||||
|
|
||||
|
// ✅ BOUTON RÉINITIALISER |
||||
|
$('#btnReset').on('click', function() { |
||||
|
console.log('🔄 [RESET] Réinitialisation des filtres'); |
||||
|
|
||||
|
// ✅ RÉINITIALISER À LA DATE DU JOUR |
||||
|
var today = new Date().toISOString().split('T')[0]; |
||||
|
$('#filterStartDate').val(today); |
||||
|
$('#filterEndDate').val(today); |
||||
|
$('#filterStore').val(''); |
||||
|
|
||||
|
console.log('📅 [RESET] Nouvelles dates:', { |
||||
|
startDate: today, |
||||
|
endDate: today |
||||
|
}); |
||||
|
|
||||
|
// ✅ RECHARGER LE TABLEAU |
||||
|
historyTable.ajax.reload(); |
||||
|
|
||||
|
// ✅ RECHARGER LES STATISTIQUES DU MOIS EN COURS |
||||
|
loadStatisticsForCurrentMonth(); |
||||
|
}); |
||||
|
|
||||
|
// ✅ CHANGEMENT DE MAGASIN - RECHARGER AUTOMATIQUEMENT |
||||
|
$('#filterStore').on('change', function() { |
||||
|
var selectedStore = $(this).val(); |
||||
|
console.log('🏪 [STORE] Changement de magasin:', selectedStore || 'Tous'); |
||||
|
|
||||
|
var startDate = $('#filterStartDate').val(); |
||||
|
var endDate = $('#filterEndDate').val(); |
||||
|
|
||||
|
// ✅ RECHARGER LE TABLEAU |
||||
|
historyTable.ajax.reload(); |
||||
|
|
||||
|
// ✅ RECHARGER LES STATISTIQUES |
||||
|
loadStatisticsForFilteredPeriod(startDate, endDate, selectedStore); |
||||
|
}); |
||||
|
|
||||
|
// ✅ FORMULAIRE D'AJOUT |
||||
|
$('#formEncaissement').on('submit', function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
console.log('➕ [CREATE] Soumission du formulaire d\'ajout'); |
||||
|
|
||||
|
$.ajax({ |
||||
|
url: '<?= base_url('encaissements/create') ?>', |
||||
|
type: 'POST', |
||||
|
data: $(this).serialize(), |
||||
|
dataType: 'json', |
||||
|
beforeSend: function() { |
||||
|
$('button[type="submit"]').prop('disabled', true) |
||||
|
.html('<i class="fa fa-spinner fa-spin"></i> Enregistrement...'); |
||||
|
}, |
||||
|
success: function(response) { |
||||
|
console.log('✅ [CREATE] Réponse:', response); |
||||
|
|
||||
|
if (response.success) { |
||||
|
showAlert('success', response.messages); |
||||
|
$('#formEncaissement')[0].reset(); |
||||
|
$('#autreTypeGroup').hide(); |
||||
|
historyTable.ajax.reload(null, false); |
||||
|
|
||||
|
// ✅ RECHARGER LES STATISTIQUES APRÈS AJOUT |
||||
|
var startDate = $('#filterStartDate').val(); |
||||
|
var endDate = $('#filterEndDate').val(); |
||||
|
var store_id = $('#filterStore').val(); |
||||
|
loadStatisticsForFilteredPeriod(startDate, endDate, store_id); |
||||
|
} else { |
||||
|
var errorMsg = typeof response.messages === 'object' |
||||
|
? Object.values(response.messages).join('<br>') |
||||
|
: response.messages; |
||||
|
showAlert('danger', errorMsg); |
||||
|
} |
||||
|
}, |
||||
|
error: function(xhr) { |
||||
|
console.error('❌ [CREATE] Erreur:', xhr); |
||||
|
var errorMsg = 'Erreur lors de l\'enregistrement'; |
||||
|
try { |
||||
|
var response = JSON.parse(xhr.responseText); |
||||
|
if (response.messages) { |
||||
|
errorMsg = typeof response.messages === 'object' |
||||
|
? Object.values(response.messages).join('<br>') |
||||
|
: response.messages; |
||||
|
} |
||||
|
} catch(e) { |
||||
|
errorMsg += ': ' + xhr.statusText; |
||||
|
} |
||||
|
showAlert('danger', errorMsg); |
||||
|
}, |
||||
|
complete: function() { |
||||
|
$('button[type="submit"]').prop('disabled', false) |
||||
|
.html('<i class="fa fa-save"></i> Enregistrer l\'Encaissement'); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// ✅ FORMULAIRE D'ÉDITION |
||||
|
$('#formEditEncaissement').on('submit', function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
var id = $('#editId').val(); |
||||
|
console.log('✏️ [UPDATE] Soumission du formulaire d\'édition, ID:', id); |
||||
|
|
||||
|
$.ajax({ |
||||
|
url: '<?= base_url('encaissements/update/') ?>' + id, |
||||
|
type: 'POST', |
||||
|
data: $(this).serialize(), |
||||
|
dataType: 'json', |
||||
|
success: function(response) { |
||||
|
console.log('✅ [UPDATE] Réponse:', response); |
||||
|
|
||||
|
if (response.success) { |
||||
|
$('#editModal').modal('hide'); |
||||
|
showAlert('success', response.messages); |
||||
|
historyTable.ajax.reload(null, false); |
||||
|
|
||||
|
// ✅ RECHARGER LES STATISTIQUES APRÈS MODIFICATION |
||||
|
var startDate = $('#filterStartDate').val(); |
||||
|
var endDate = $('#filterEndDate').val(); |
||||
|
var store_id = $('#filterStore').val(); |
||||
|
loadStatisticsForFilteredPeriod(startDate, endDate, store_id); |
||||
|
} else { |
||||
|
var errorMsg = typeof response.messages === 'object' |
||||
|
? Object.values(response.messages).join('<br>') |
||||
|
: response.messages; |
||||
|
showAlert('danger', errorMsg); |
||||
|
} |
||||
|
}, |
||||
|
error: function(xhr) { |
||||
|
console.error('❌ [UPDATE] Erreur:', xhr); |
||||
|
var errorMsg = 'Erreur lors de la modification'; |
||||
|
try { |
||||
|
var response = JSON.parse(xhr.responseText); |
||||
|
if (response.messages) { |
||||
|
errorMsg = response.messages; |
||||
|
} |
||||
|
} catch(e) { |
||||
|
errorMsg += ': ' + xhr.statusText; |
||||
|
} |
||||
|
showAlert('danger', errorMsg); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
console.log('✅ [INIT] Page complètement initialisée'); |
||||
|
}); |
||||
|
|
||||
|
// ============================================ |
||||
|
// AUTRES FONCTIONS (viewDetails, editEncaissement, deleteEncaissement, showAlert) |
||||
|
// ============================================ |
||||
|
function viewDetails(id) { |
||||
|
$.ajax({ |
||||
|
url: '<?= base_url('encaissements/details/') ?>' + id, |
||||
|
type: 'GET', |
||||
|
dataType: 'json', |
||||
|
success: function(response) { |
||||
|
if (response.success) { |
||||
|
var data = response.data; |
||||
|
$('#detailId').text(data.id); |
||||
|
$('#detailType').text(data.type_encaissement); |
||||
|
$('#detailMontant').text(new Intl.NumberFormat('fr-FR').format(data.montant) + ' Ar'); |
||||
|
$('#detailMode').text(data.mode_paiement); |
||||
|
$('#detailDate').text(new Date(data.created_at).toLocaleString('fr-FR')); |
||||
|
$('#detailUser').text(data.user_name); |
||||
|
$('#detailEmail').text(data.user_email); |
||||
|
$('#detailStore').text(data.store_name); |
||||
|
$('#detailCommentaire').text(data.commentaire || 'Aucun commentaire'); |
||||
|
|
||||
|
$('#detailsModal').modal('show'); |
||||
|
} else { |
||||
|
showAlert('danger', response.message || 'Erreur lors du chargement des détails'); |
||||
|
} |
||||
|
}, |
||||
|
error: function(xhr) { |
||||
|
showAlert('danger', 'Erreur lors du chargement des détails'); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function editEncaissement(id) { |
||||
|
$.ajax({ |
||||
|
url: '<?= base_url('encaissements/getEncaissement/') ?>' + id, |
||||
|
type: 'GET', |
||||
|
dataType: 'json', |
||||
|
success: function(response) { |
||||
|
if (response.success) { |
||||
|
var data = response.data; |
||||
|
$('#editId').val(data.id); |
||||
|
$('#editTypeEncaissement').val(data.type_encaissement); |
||||
|
$('#editAutreType').val(data.autre_type); |
||||
|
$('#editMontant').val(data.montant); |
||||
|
$('#editModePaiement').val(data.mode_paiement); |
||||
|
$('#editCommentaire').val(data.commentaire); |
||||
|
|
||||
|
var predefinedTypes = ['Plastification', 'Duplicata', 'Retour Décaissement', 'Vente de Pièce']; |
||||
|
if (data.type_encaissement === 'Autre' || !predefinedTypes.includes(data.type_encaissement)) { |
||||
|
$('#editTypeEncaissement').val('Autre'); |
||||
|
$('#editAutreTypeGroup').show(); |
||||
|
$('#editAutreType').val(data.type_encaissement); |
||||
|
} |
||||
|
|
||||
|
$('#editModal').modal('show'); |
||||
|
} else { |
||||
|
showAlert('danger', response.message || 'Erreur lors du chargement'); |
||||
|
} |
||||
|
}, |
||||
|
error: function(xhr) { |
||||
|
showAlert('danger', 'Erreur lors du chargement de l\'encaissement'); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function deleteEncaissement(id) { |
||||
|
Swal.fire({ |
||||
|
title: 'Confirmation', |
||||
|
text: "Voulez-vous vraiment supprimer cet encaissement ?", |
||||
|
icon: 'warning', |
||||
|
showCancelButton: true, |
||||
|
confirmButtonColor: '#d33', |
||||
|
cancelButtonColor: '#3085d6', |
||||
|
confirmButtonText: 'Oui, supprimer', |
||||
|
cancelButtonText: 'Annuler' |
||||
|
}).then((result) => { |
||||
|
if (result.isConfirmed) { |
||||
|
console.log('🗑️ [DELETE] Suppression de l\'encaissement ID:', id); |
||||
|
|
||||
|
$.ajax({ |
||||
|
url: '<?= base_url('encaissements/delete/') ?>' + id, |
||||
|
type: 'POST', |
||||
|
dataType: 'json', |
||||
|
success: function(response) { |
||||
|
console.log('✅ [DELETE] Réponse:', response); |
||||
|
|
||||
|
if (response.success) { |
||||
|
Swal.fire({ |
||||
|
icon: 'success', |
||||
|
title: 'Supprimé', |
||||
|
text: response.messages, |
||||
|
timer: 2000, |
||||
|
showConfirmButton: false |
||||
|
}); |
||||
|
|
||||
|
$('#historyTable').DataTable().ajax.reload(null, false); |
||||
|
|
||||
|
// ✅ RECHARGER LES STATISTIQUES APRÈS SUPPRESSION |
||||
|
var startDate = $('#filterStartDate').val(); |
||||
|
var endDate = $('#filterEndDate').val(); |
||||
|
var store_id = $('#filterStore').val(); |
||||
|
loadStatisticsForFilteredPeriod(startDate, endDate, store_id); |
||||
|
} else { |
||||
|
Swal.fire({ |
||||
|
icon: 'error', |
||||
|
title: 'Erreur', |
||||
|
text: response.messages || 'Erreur lors de la suppression' |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
error: function(xhr) { |
||||
|
console.error('❌ [DELETE] Erreur:', xhr); |
||||
|
Swal.fire({ |
||||
|
icon: 'error', |
||||
|
title: 'Erreur serveur', |
||||
|
text: 'Impossible de supprimer cet encaissement' |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function showAlert(type, message) { |
||||
|
var alertHtml = '<div class="alert alert-' + type + ' alert-dismissible alert-notification">' + |
||||
|
'<button type="button" class="close" data-dismiss="alert">×</button>' + |
||||
|
message + |
||||
|
'</div>'; |
||||
|
|
||||
|
$('.alert-notification').remove(); |
||||
|
|
||||
|
$('body').append(alertHtml); |
||||
|
|
||||
|
setTimeout(function() { |
||||
|
$('.alert-notification').fadeOut('slow', function() { |
||||
|
$(this).remove(); |
||||
|
}); |
||||
|
}, 5000); |
||||
|
} |
||||
|
</script> |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue