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