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.
362 lines
15 KiB
362 lines
15 KiB
<!-- Custom Styling -->
|
|
<style>
|
|
.card {
|
|
border-radius: 12px;
|
|
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.table {
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
th {
|
|
text-align: center;
|
|
background-color: #343a40 !important;
|
|
color: white;
|
|
}
|
|
|
|
tbody tr:hover {
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.btn-primary {
|
|
border-radius: 8px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.bg-primary {
|
|
background-color: #007bff !important;
|
|
}
|
|
|
|
.bg-success {
|
|
background-color: #28a745 !important;
|
|
}
|
|
|
|
.bg-warning {
|
|
background-color: #ffc107 !important;
|
|
}
|
|
</style>
|
|
|
|
<div class="content-wrapper">
|
|
<section class="content-header">
|
|
<h1>📦 Rapports des Stocks</h1>
|
|
<ol class="breadcrumb">
|
|
<li><a href="#"><i class="fa fa-home"></i> Accueil</a></li>
|
|
<li class="active" onclick="window.history.back()" style="cursor: pointer;">📊 Rapports</li>
|
|
</ol>
|
|
</section>
|
|
|
|
<section class="content">
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-primary text-white">
|
|
<h3 class="card-title m-0">📊 Gérer les Produits vendus et en stock</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Filter Bar -->
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label for="storeFilter" class="form-label fw-bold">🏪 Points de ventes</label>
|
|
<select id="storeFilter" class="form-control">
|
|
<option value="TOUS">TOUS</option>
|
|
<?php foreach ($stores as $value) { ?>
|
|
<option value="<?= $value['id'] ?>"><?= $value['name'] ?></option>
|
|
<?php } ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 d-flex align-items-end">
|
|
<br>
|
|
<button id="filterBtn" class="btn btn-primary w-100">🔍 Filtrer</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chart Section -->
|
|
<div class="row justify-content-center my-4">
|
|
<div class="col-md-12">
|
|
<canvas id="stockChart" style="min-height: 400px;"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Product Details -->
|
|
<div class="row mt-4">
|
|
<div class="col-md-12 col-lg-12">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-success text-white">
|
|
<h3 class="card-title m-0">🛒 Détails des produits vendus</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<table id="venteTable" class="table table-hover table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Numéro de série</th>
|
|
<th>Date de vente</th>
|
|
<th>Point de vente</th>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-12 col-lg-12">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h3 class="card-title m-0">📦 Détails des produits en stock</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<table id="stockTable" class="table table-hover table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Marques</th>
|
|
<th>Nombre des produits</th>
|
|
<th>Point de vente</th>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-12 col-lg-12">
|
|
<div class="card shadow-sm border-0 rounded-3">
|
|
<div
|
|
class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
|
<h3 class="card-title m-0">📦 Exportation des produits vendus</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3 align-items-center mb-4" style="margin: 5px 0 5px 5px;">
|
|
<div class="col-md-4">
|
|
<label for="startDate" class="form-label">Date de début</label>
|
|
<input type="date" id="startDate" class="form-control">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="endDate" class="form-label">Date de fin</label>
|
|
<input type="date" id="endDate" class="form-control">
|
|
</div>
|
|
<div class="col-md-4 d-flex align-items-end">
|
|
<br>
|
|
<button id="filteredB1" class="btn btn-primary w-100">Filtrer
|
|
🔍</button>
|
|
<button id="ExportBTN1" class="btn btn-success w-100">Exporter
|
|
📤</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table id="export1"
|
|
class="table table-hover table-striped table-bordered">
|
|
<thead class="table-primary">
|
|
<tr>
|
|
<th>Numéro de série</th>
|
|
<th>Quantité</th>
|
|
<th>Prix</th>
|
|
<th>Date de vente</th>
|
|
<th>Point de vente</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- DataTables will populate this -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div> <!-- End row -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
|
|
|
<script>
|
|
var manageTable;
|
|
var manageTable2;
|
|
var manageTable3;
|
|
$(document).ready(function () {
|
|
$("#reportNav").addClass('active');
|
|
|
|
// initialize the datatable
|
|
|
|
|
|
manageTable = $('#venteTable').DataTable({
|
|
'ajax': 'fetctData/' + 0,
|
|
'order': [],
|
|
'pageLength': 5, // Set default rows per page
|
|
'lengthMenu': [
|
|
[5, 10, 25, 50, -1],
|
|
[5, 10, 25, 50, "All"]
|
|
] // Allow users to select different page sizes
|
|
});
|
|
|
|
manageTable2 = $('#stockTable').DataTable({
|
|
'ajax': 'fetctDataStock/' + 0,
|
|
'order': [],
|
|
'pageLength': 5, // Set default rows per page
|
|
'lengthMenu': [
|
|
[5, 10, 25, 50, -1],
|
|
[5, 10, 25, 50, "All"]
|
|
] // Allow users to select different page sizes
|
|
});
|
|
|
|
manageTable3 = $('#export1').DataTable({
|
|
ajax: {
|
|
url: 'fetctDataStock2/' + 0,
|
|
dataSrc: 'data'
|
|
},
|
|
order: [],
|
|
pageLength: 5,
|
|
lengthMenu: [
|
|
[5, 10, 25, 50, -1],
|
|
[5, 10, 25, 50, "All"]
|
|
]
|
|
});
|
|
|
|
const filterBtn = document.getElementById('storeFilter');
|
|
|
|
filterBtn.addEventListener('change', function () {
|
|
let filterValue = filterBtn.value === "TOUS" ? "0" : filterBtn.value;
|
|
|
|
// Update the DataTable dynamically without reinitialization
|
|
manageTable.ajax.url('fetctData/' + filterValue).load();
|
|
manageTable2.ajax.url('fetctDataStock/' + filterValue).load();
|
|
});
|
|
|
|
let productsSold = <?= $ventes ?>;
|
|
let productsInStock = <?= $stock ?>;
|
|
|
|
function applyFilter(storeId) {
|
|
let filteredSoldProducts = productsSold;
|
|
let filteredStockProducts = productsInStock;
|
|
|
|
// If a specific store is selected, filter the data
|
|
if (storeId && storeId !== "TOUS") {
|
|
filteredSoldProducts = productsSold.filter(sale => sale.store_id === storeId);
|
|
filteredStockProducts = productsInStock.filter(product => product.store_id === storeId);
|
|
}
|
|
|
|
updateChart(filteredStockProducts, filteredSoldProducts);
|
|
}
|
|
|
|
// Trigger the filter on button click
|
|
$('#filterBtn').click(function () {
|
|
let storeId = $('#storeFilter').val();
|
|
applyFilter(storeId);
|
|
});
|
|
|
|
// Apply default filter on page load (TOUS)
|
|
applyFilter("TOUS");
|
|
});
|
|
|
|
$.fn.dataTable.ext.search.push(function (settings, data, dataIndex) {
|
|
if (settings.nTable.id !== 'export1') return true; // Apply only to your table
|
|
|
|
const startDate = $('#startDate').val();
|
|
const endDate = $('#endDate').val();
|
|
const saleDate = data[3]; // assuming column 4 is "Date de vente"
|
|
|
|
// If no start date, show everything (default)
|
|
if (!startDate) {
|
|
return true;
|
|
}
|
|
|
|
const sale = new Date(saleDate);
|
|
const start = new Date(startDate);
|
|
const end = endDate ? new Date(endDate) : null;
|
|
|
|
if (end) {
|
|
return sale >= start && sale <= end;
|
|
} else {
|
|
return sale >= start;
|
|
}
|
|
});
|
|
|
|
|
|
$('#filteredB1').on('click', function () {
|
|
manageTable3.draw(); // re-filter table
|
|
});
|
|
|
|
|
|
// Function to update the chart
|
|
function updateChart(filteredStockProducts, filteredSoldProducts) {
|
|
const stockData = {};
|
|
const soldData = {};
|
|
|
|
// Aggregate stock quantities
|
|
filteredStockProducts.forEach(product => {
|
|
stockData[product.name] = (stockData[product.name] || 0) + parseInt(product.qty);
|
|
});
|
|
|
|
// Aggregate sold quantities
|
|
filteredSoldProducts.forEach(sale => {
|
|
soldData[sale.Pname] = (soldData[sale.Pname] || 0) + 1;
|
|
});
|
|
|
|
// Extract labels and data
|
|
const labels = [...new Set([...Object.keys(stockData), ...Object.keys(soldData)])];
|
|
const stockValues = labels.map(label => stockData[label] || 0);
|
|
const soldValues = labels.map(label => soldData[label] || 0);
|
|
|
|
// Update Chart.js instance
|
|
stockChart.data.labels = labels;
|
|
stockChart.data.datasets[0].data = stockValues;
|
|
stockChart.data.datasets[1].data = soldValues;
|
|
stockChart.update();
|
|
}
|
|
|
|
// Initialize Line Chart
|
|
const ctx = document.getElementById("stockChart").getContext("2d");
|
|
const stockChart = new Chart(ctx, {
|
|
type: "line", // Changed to line chart
|
|
data: {
|
|
labels: [],
|
|
datasets: [{
|
|
label: "Quantité en stock",
|
|
data: [],
|
|
borderColor: "rgba(0, 0, 255, 0.5)",
|
|
backgroundColor: "rgba(0, 0, 255, 0.2)",
|
|
fill: true
|
|
},
|
|
{
|
|
label: "Quantité vendue",
|
|
data: [],
|
|
borderColor: "rgba(255, 0, 0, 0.5)",
|
|
backgroundColor: "rgba(255, 0, 0, 0.2)",
|
|
fill: true
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
document.getElementById('ExportBTN1').addEventListener('click', function () {
|
|
// Select your table
|
|
var table = document.getElementById('export1');
|
|
|
|
// Convert it to a workbook
|
|
var wb = XLSX.utils.table_to_book(table, {
|
|
sheet: "Feuille1"
|
|
});
|
|
|
|
// Export it
|
|
XLSX.writeFile(wb, 'export-produits.xlsx');
|
|
});
|
|
</script>
|