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
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">×</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">×</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 :",
|
|
sLengthMenu: "Afficher _MENU_ éléments",
|
|
sInfo: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
|
sInfoEmpty: "Affichage de l'élement 0 à 0 sur 0 élément",
|
|
sInfoFiltered: "(filtré de _MAX_ éléments au total)",
|
|
sLoadingRecords: "Chargement en cours...",
|
|
sZeroRecords: "Aucun élément à afficher",
|
|
sEmptyTable: "Aucune donnée disponible dans le tableau",
|
|
oPaginate: {
|
|
sFirst: "Premier",
|
|
sPrevious: "Précédent",
|
|
sNext: "Suivant",
|
|
sLast: "Dernier"
|
|
},
|
|
oAria: {
|
|
sSortAscending: ": activer pour trier la colonne par ordre croissant",
|
|
sSortDescending: ": activer pour trier la colonne par ordre dé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>
|