You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

433 lines
19 KiB

<div class="content-wrapper">
<section class="content-header">
<h1>
<i class="fa fa-history text-blue"></i> Gérer l'
<small>Historique</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Accueil</a></li>
<li class="active">Historique</li>
</ol>
</section>
<section class="content">
<div class="row">
<div class="col-md-12 col-xs-12">
<div id="messages"></div>
<div class="box box-primary shadow-sm">
<div class="box-header with-border">
<h3 class="box-title"><i class="fa fa-list"></i> <?= $page_title ?></h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#filterModal">
<i class="fa fa-filter"></i> Filtrer
</button>
<button type="button" class="btn btn-sm btn-success" onclick="exportHistorique()">
<i class="fa fa-download"></i> Exporter
</button>
<!-- <button type="button" class="btn btn-info" onclick="showStats()">
<i class="fa fa-bar-chart"></i> Statistiques
</button> -->
</div>
</div>
<div class="box-body">
<div class="row" style="margin-bottom: 15px;">
<div class="col-md-4">
<div class="form-group">
<label><i class="fa fa-home"></i> Sélectionner un magasin</label>
<select class="form-control input-sm" id="store_top_filter" name="store_id">
<option value="all">Tous les magasins</option>
<?php foreach ($stores as $store): ?>
<option value="<?= $store['name'] ?>"><?= $store['name'] ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="row" style="margin-bottom:10px;">
<div class="col-md-12">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default btn-sm filter-quick" data-filter="today"><i class="fa fa-calendar-day"></i> Aujourd'hui</button>
<button type="button" class="btn btn-default btn-sm filter-quick" data-filter="week"><i class="fa fa-calendar-week"></i> Cette semaine</button>
<button type="button" class="btn btn-default btn-sm filter-quick" data-filter="month"><i class="fa fa-calendar"></i> Ce mois</button>
<button type="button" class="btn btn-default btn-sm filter-quick active" data-filter="all"><i class="fa fa-infinity"></i> Tout</button>
</div>
<div class="btn-group pull-right" role="group">
<button type="button" class="btn btn-default btn-sm action-filter active" data-action="all"><i class="fa fa-list"></i> Toutes</button>
<button type="button" class="btn btn-success btn-sm action-filter" data-action="CREATE"><i class="fa fa-plus-circle"></i> Création</button>
<button type="button" class="btn btn-warning btn-sm action-filter" data-action="UPDATE"><i class="fa fa-edit"></i> Modification</button>
<!-- <button type="button" class="btn btn-warning action-filter" data-action="ASSIGN_STORE">Assignation</button> -->
<button type="button" class="btn btn-primary btn-sm action-filter" data-action="ENTRER"><i class="fa fa-arrow-down"></i> Entrée</button>
<button type="button" class="btn btn-danger btn-sm action-filter" data-action="SORTIE"><i class="fa fa-arrow-up"></i> Sortie</button>
</div>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table id="historiqueTable" class="table table-bordered table-striped table-hover nowrap" style="width:100%;">
<thead class="bg-light-blue">
<tr>
<th>Date</th>
<th>Produit</th>
<th>SKU</th>
<th>Magasin</th>
<th>Action</th>
<th>Description</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<!-- Loader -->
<div id="loading" style="display:none;text-align:center;margin:20px;">
<i class="fa fa-spinner fa-spin fa-2x text-blue"></i>
<p>Chargement des données...</p>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="modal fade" id="filterModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Filtres avancés</h4>
</div>
<div class="modal-body">
<form id="filterForm">
<input type="hidden" id="movement_type" name="movement_type" value="">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Date de début</label>
<input type="date" class="form-control" id="date_from" name="date_from">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Date de fin</label>
<input type="date" class="form-control" id="date_to" name="date_to">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Magasin</label>
<select class="form-control" id="store_filter" name="store_id">
<option value="all">Tous les magasins</option>
<option value="0">TOUS</option>
<?php foreach ($stores as $store): ?>
<option value="<?= $store['id'] ?>"><?= $store['name'] ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Action</label>
<select class="form-control" id="action_filter" name="action">
<option value="all">Toutes les actions</option>
<option value="CREATE">Création</option>
<option value="UPDATE">Modification</option>
<option value="DELETE">Suppression</option>
<option value="ASSIGN_STORE">Assignation</option>
<option value="ENTRER">Entrée</option>
<option value="SORTIE">Sortie</option>
</select>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-default" onclick="resetFilters()">Réinitialiser</button>
<button type="button" class="btn btn-primary" onclick="applyFilters()">Appliquer</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="statsModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Statistiques de l'historique</h4>
</div>
<div class="modal-body" id="statsContent">
</div>
</div>
</div>
</div>
<style>
.btn-group .btn.active {
font-weight: bold;
border: 1px solid #444;
}
.table td, .table th {
vertical-align: middle !important;
}
.bg-light-blue {
background: #3c8dbc;
color: white;
}
.box.shadow-sm {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}
</style>
<script>
// Déclaration de la variable de table en dehors du document ready pour un accès global
var historiqueTable;
$(document).ready(function() {
// On s'assure que le fichier de langue est chargé et on étend les options par défaut de DataTables.
// Cette configuration est copiée de votre deuxième script pour un affichage de pagination uniforme.
$.extend(true, $.fn.dataTable.defaults, {
language: {
sProcessing: "Traitement en cours...",
sSearch: "Rechercher&nbsp;:",
sLengthMenu: "Afficher _MENU_ &eacute;l&eacute;ments",
sInfo: "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
sInfoEmpty: "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ment",
sInfoFiltered: "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",
sLoadingRecords: "Chargement en cours...",
sZeroRecords: "Aucun &eacute;l&eacute;ment &agrave; afficher",
sEmptyTable: "Aucune donn&eacute;e disponible dans le tableau",
oPaginate: {
sFirst: "Premier",
sPrevious: "Pr&eacute;c&eacute;dent",
sNext: "Suivant",
sLast: "Dernier"
},
oAria: {
sSortAscending: ": activer pour trier la colonne par ordre croissant",
sSortDescending: ": activer pour trier la colonne par ordre d&eacute;croissant"
}
}
});
// Initialisation du DataTable
historiqueTable = $('#historiqueTable').DataTable({
"processing": true,
"serverSide": true,
"ajax": {
"url": "<?= base_url('historique/fetchHistoriqueData') ?>",
"type": "GET",
"data": function(d) {
d.store_name = $('#store_top_filter').val();
d.date_from = $('#date_from').val();
d.date_to = $('#date_to').val();
d.action = $('#action_filter').val();
d.movement_type = $('#movement_type').val();
},
"beforeSend": function() { $("#loading").show(); },
"complete": function() { $("#loading").hide(); }
},
"columns": [
{ "data": 0, "width": "15%" },
{ "data": 1, "width": "20%" },
{ "data": 2, "width": "10%" },
{ "data": 3, "width": "12%" },
{
"data": 4,
"width": "10%",
"render": function(data) {
let badgeClass = "badge bg-gray";
if (data === "CREATE") badgeClass = "badge bg-green";
else if (data === "UPDATE") badgeClass = "badge bg-yellow";
else if (data === "ENTRER") badgeClass = "badge bg-blue";
else if (data === "SORTIE") badgeClass = "badge bg-red";
return '<span class="'+badgeClass+'">'+data+'</span>';
}
},
{ "data": 5, "width": "33%" }
],
"order": [[0, "desc"]],
"pageLength": 25,
"lengthMenu": [
[5, 10, 20, 50, -1],
['5', '10', '20', '50', 'Tous']
],
"dom": 'Blfrtip',
"searching": false,
"buttons": [
'copy', 'csv', 'excel', 'pdf', 'print'
]
});
// Événement pour le nouveau champ de sélection de magasin
$('#store_top_filter').change(function() {
// Met à jour la valeur du filtre avancé pour la synchronisation
$('#store_filter').val('all');
historiqueTable.ajax.reload();
});
// S'assurer que le filtre avancé par magasin recharge le tableau
$('#store_filter').change(function() {
historiqueTable.ajax.reload();
});
// Gestion des filtres rapides (période)
$('.filter-quick').click(function() {
$('.filter-quick').removeClass('active');
$(this).addClass('active');
var filter = $(this).data('filter');
var today = new Date().toISOString().split('T')[0];
switch(filter) {
case 'today':
$('#date_from').val(today);
$('#date_to').val(today);
break;
case 'week':
var weekAgo = new Date();
weekAgo.setDate(weekAgo.getDate() - 7);
$('#date_from').val(weekAgo.toISOString().split('T')[0]);
$('#date_to').val(today);
break;
case 'month':
var monthAgo = new Date();
monthAgo.setMonth(monthAgo.getMonth() - 1);
$('#date_from').val(monthAgo.toISOString().split('T')[0]);
$('#date_to').val(today);
break;
case 'all':
$('#date_from').val('');
$('#date_to').val('');
break;
}
historiqueTable.ajax.reload();
});
// Gestion des filtres d'action rapides
$('.action-filter').click(function() {
$('.action-filter').removeClass('active');
$(this).addClass('active');
var action = $(this).data('action');
if (action === 'ENTRER' || action === 'SORTIE') {
$('#action_filter').val(action);
$('#movement_type').val(action.toLowerCase());
} else if (action === 'all') {
$('#action_filter').val('all');
$('#movement_type').val('');
} else {
$('#action_filter').val(action);
$('#movement_type').val('');
}
historiqueTable.ajax.reload();
});
});
function applyFilters() {
$('#filterModal').modal('hide');
// Mettre à jour le sélecteur rapide en fonction du filtre avancé
var selectedStoreId = $('#store_filter').val();
if (selectedStoreId === 'all') {
$('#store_top_filter').val('all');
} else {
// Trouver le nom du magasin correspondant à l'ID
var storeName = $('#store_filter option[value="' + selectedStoreId + '"]').text();
$('#store_top_filter').val(storeName);
}
historiqueTable.ajax.reload();
}
function resetFilters() {
$('#filterForm')[0].reset();
$('#store_filter').val('all');
$('#store_top_filter').val('all'); // Réinitialise aussi le champ supérieur
$('#action_filter').val('all');
$('#movement_type').val('');
$('.filter-quick, .action-filter').removeClass('active');
$('.filter-quick[data-filter="all"]').addClass('active');
$('.action-filter[data-action="all"]').addClass('active');
historiqueTable.ajax.reload();
}
function exportHistorique() {
var params = new URLSearchParams();
params.append('date_from', $('#date_from').val());
params.append('date_to', $('#date_to').val());
params.append('store_name', $('#store_top_filter').val()); // Utilise le nouveau champ
params.append('action', $('#action_filter').val());
params.append('movement_type', $('#movement_type').val());
window.location.href = '<?= base_url('historique/export') ?>?' + params.toString();
}
function showStats() {
$.ajax({
url: '<?= base_url('historique/getStats') ?>',
type: 'GET',
dataType: 'json',
success: function(data) {
var html = '<div class="row">';
html += '<div class="col-md-4">';
html += '<div class="info-box bg-aqua">';
html += '<span class="info-box-icon"><i class="fa fa-history"></i></span>';
html += '<div class="info-box-content">';
html += '<span class="info-box-text">Total événements</span>';
html += '<span class="info-box-number">' + data.total_mouvements + '</span>';
html += '</div></div></div>';
html += '<div class="col-md-8">';
html += '<h4>Répartition par action</h4>';
html += '<div class="row">';
var total = data.total_mouvements;
var actions = {
'CREATE': data.mouvements_create,
'UPDATE': data.mouvements_update,
'DELETE': data.mouvements_delete,
'ASSIGN_STORE': data.mouvements_assign_store,
'ENTRER': data.mouvements_entrer,
'SORTIE': data.mouvements_sortie,
};
for (var action in actions) {
var value = actions[action] || 0;
var percent = (total > 0) ? (value / total * 100).toFixed(2) : 0;
html += '<div class="col-md-6">';
html += '<small>' + action + '</small>';
html += '<div class="progress">';
html += '<div class="progress-bar" style="width: ' + percent + '%">';
html += value + ' (' + percent + '%)';
html += '</div></div></div>';
}
html += '</div></div></div>';
$('#statsContent').html(html);
$('#statsModal').modal('show');
},
error: function(xhr, status, error) {
// Remplacer l'alerte par un message plus élégant
$('#statsContent').html('<div class="alert alert-danger" role="alert">Erreur lors du chargement des statistiques.</div>');
$('#statsModal').modal('show');
}
});
}
</script>