Autosave: 20260518-171536
This commit is contained in:
parent
4048403247
commit
515395635b
BIN
assets/uploads/marketing_images/6a0b459229e98.png
Normal file
BIN
assets/uploads/marketing_images/6a0b459229e98.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/uploads/marketing_images/6a0b4592b821f.png
Normal file
BIN
assets/uploads/marketing_images/6a0b4592b821f.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/uploads/vouchers/6a0b420ae0091-2438.png
Normal file
BIN
assets/uploads/vouchers/6a0b420ae0091-2438.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 315 KiB |
BIN
assets/uploads/vouchers/6a0b448cdb0b7-4848.png
Normal file
BIN
assets/uploads/vouchers/6a0b448cdb0b7-4848.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 349 KiB |
BIN
assets/uploads/vouchers/6a0b4509e9eba-9494.png
Normal file
BIN
assets/uploads/vouchers/6a0b4509e9eba-9494.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 619 KiB |
BIN
assets/uploads/vouchers/6a0b4591572e5-468.png
Normal file
BIN
assets/uploads/vouchers/6a0b4591572e5-468.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 374 KiB |
@ -195,6 +195,40 @@ $stmtTopDetalleCE = $db->query("
|
||||
");
|
||||
$topProductosDetalleCE = $stmtTopDetalleCE->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 14. Rendimiento de Productos en Contraentrega (Para Gráfica de Barras Apiladas)
|
||||
$stmtProdRendimientoCE = $db->query("
|
||||
SELECT
|
||||
producto,
|
||||
COUNT(CASE WHEN estado = 'ENTREGA EXITOSA' THEN 1 END) as exitosas,
|
||||
COUNT(CASE WHEN estado = 'RETORNADO' THEN 1 END) as retornados,
|
||||
COUNT(CASE WHEN estado = 'RUTA_CONTRAENTREGA' THEN 1 END) as en_ruta,
|
||||
COUNT(*) as total
|
||||
FROM pedidos p
|
||||
WHERE $date_condition
|
||||
AND (estado IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA') OR (estado = 'RETORNADO' AND agencia = 'CONTRAENTREGA'))
|
||||
GROUP BY producto
|
||||
ORDER BY total DESC
|
||||
LIMIT 10
|
||||
");
|
||||
$prodRendimientoCE = $stmtProdRendimientoCE->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 15. Rendimiento de Productos en Provincia
|
||||
$stmtProdRendimientoProv = $db->query("
|
||||
SELECT
|
||||
producto,
|
||||
COUNT(CASE WHEN estado = 'COMPLETADO ✅' THEN 1 END) as completados,
|
||||
COUNT(CASE WHEN estado = 'GESTIONES' THEN 1 END) as gestiones,
|
||||
COUNT(CASE WHEN estado IN ('ROTULADO 📦', 'EN TRANSITO 🚛', 'EN DESTINO 🏬') THEN 1 END) as en_proceso,
|
||||
COUNT(*) as total
|
||||
FROM pedidos p
|
||||
WHERE $date_condition
|
||||
AND NOT (estado IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA') OR (estado = 'RETORNADO' AND agencia = 'CONTRAENTREGA'))
|
||||
GROUP BY producto
|
||||
ORDER BY total DESC
|
||||
LIMIT 10
|
||||
");
|
||||
$prodRendimientoProv = $stmtProdRendimientoProv->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$canalesResumen = [
|
||||
'Provincia' => ['pedidos' => 0, 'monto' => 0, 'estados' => []],
|
||||
'Contraentrega' => ['pedidos' => 0, 'monto' => 0, 'estados' => []]
|
||||
@ -384,7 +418,7 @@ include 'layout_header.php';
|
||||
<span class="badge bg-primary text-white">Distribución Interna</span>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column align-items-center justify-content-center" style="min-height: 300px;">
|
||||
<div style="width: 100%; max-width: 280px;">
|
||||
<div style="width: 100%; max-width: 450px;">
|
||||
<canvas id="provinciaPieChart"></canvas>
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
@ -403,7 +437,7 @@ include 'layout_header.php';
|
||||
<span class="badge bg-warning text-dark">Distribución Interna</span>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column align-items-center justify-content-center" style="min-height: 300px;">
|
||||
<div style="width: 100%; max-width: 280px;">
|
||||
<div style="width: 100%; max-width: 450px;">
|
||||
<canvas id="contraentregaPieChart"></canvas>
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
@ -438,7 +472,7 @@ include 'layout_header.php';
|
||||
<h6 class="border-bottom pb-2 mb-3">Distribución de Estados</h6>
|
||||
<div class="list-group list-group-flush">
|
||||
<?php
|
||||
$estadosProv = ['ROTULADO 📦', 'EN TRANSITO 🚛', 'EN DESTINO 🏬', 'COMPLETADO ✅', 'RETORNADO'];
|
||||
$estadosProv = ['ROTULADO 📦', 'EN TRANSITO 🚛', 'EN DESTINO 🏬', 'COMPLETADO ✅', 'GESTIONES'];
|
||||
foreach ($estadosProv as $est):
|
||||
$count = $canalesResumen['Provincia']['estados'][$est] ?? 0;
|
||||
$pct = $canalesResumen['Provincia']['pedidos'] > 0 ? ($count / array_sum($canalesResumen['Provincia']['estados'])) * 100 : 0;
|
||||
@ -507,7 +541,7 @@ include 'layout_header.php';
|
||||
|
||||
<div class="row">
|
||||
<!-- Gráfico de Ventas -->
|
||||
<div class="col-md-8 mb-4">
|
||||
<div class="col-md-7 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="mb-0">Tendencia de Ventas (<?php echo $label_period; ?>)</h5>
|
||||
@ -518,7 +552,7 @@ include 'layout_header.php';
|
||||
</div>
|
||||
</div>
|
||||
<!-- Gráfico de Estados -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="col-md-5 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="mb-0">Distribución de Estados</h5>
|
||||
@ -640,6 +674,44 @@ include 'layout_header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Rendimiento Detallado por Producto en Provincia -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow border-left-primary h-100">
|
||||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 text-primary"><i class="fas fa-chart-bar me-2"></i> Rendimiento Producto (Provincia)</h5>
|
||||
<span class="badge bg-primary text-white">Top 10</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div style="height: 350px;">
|
||||
<canvas id="rendimientoProdProvChart"></canvas>
|
||||
</div>
|
||||
<div class="mt-3 small text-muted text-center">
|
||||
<i class="fas fa-info-circle me-1"></i> Completados vs Gestiones en Provincia.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rendimiento Detallado por Producto en Contraentrega -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow border-left-warning h-100">
|
||||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 text-warning"><i class="fas fa-chart-bar me-2"></i> Rendimiento Producto (Contraentrega)</h5>
|
||||
<span class="badge bg-warning text-dark">Top 10</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div style="height: 350px;">
|
||||
<canvas id="rendimientoProdCEChart"></canvas>
|
||||
</div>
|
||||
<div class="mt-3 small text-muted text-center">
|
||||
<i class="fas fa-info-circle me-1"></i> Éxito vs Retorno en Contraentrega.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Ventas por Asesor -->
|
||||
<div class="col-md-12 mb-4">
|
||||
@ -656,22 +728,32 @@ include 'layout_header.php';
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
|
||||
<script>
|
||||
// Registrar el plugin de etiquetas de datos
|
||||
Chart.register(ChartDataLabels);
|
||||
|
||||
// Gráfico de Provincia (Doughnut)
|
||||
const ctxProvincia = document.getElementById('provinciaPieChart').getContext('2d');
|
||||
new Chart(ctxProvincia, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['ROTULADO 📦', 'EN TRANSITO 🚛', 'EN DESTINO 🏬', 'COMPLETADO ✅', 'RETORNADO'],
|
||||
labels: [
|
||||
'ROTULADO 📦 (<?php echo $canalesResumen['Provincia']['estados']['ROTULADO 📦'] ?? 0; ?>)',
|
||||
'EN TRANSITO 🚛 (<?php echo $canalesResumen['Provincia']['estados']['EN TRANSITO 🚛'] ?? 0; ?>)',
|
||||
'EN DESTINO 🏬 (<?php echo $canalesResumen['Provincia']['estados']['EN DESTINO 🏬'] ?? 0; ?>)',
|
||||
'COMPLETADO ✅ (<?php echo $canalesResumen['Provincia']['estados']['COMPLETADO ✅'] ?? 0; ?>)',
|
||||
'GESTIONES (<?php echo $canalesResumen['Provincia']['estados']['GESTIONES'] ?? 0; ?>)'
|
||||
],
|
||||
datasets: [{
|
||||
data: [
|
||||
<?php echo $canalesResumen['Provincia']['estados']['ROTULADO 📦'] ?? 0; ?>,
|
||||
<?php echo $canalesResumen['Provincia']['estados']['EN TRANSITO 🚛'] ?? 0; ?>,
|
||||
<?php echo $canalesResumen['Provincia']['estados']['EN DESTINO 🏬'] ?? 0; ?>,
|
||||
<?php echo $canalesResumen['Provincia']['estados']['COMPLETADO ✅'] ?? 0; ?>,
|
||||
<?php echo $canalesResumen['Provincia']['estados']['RETORNADO'] ?? 0; ?>
|
||||
<?php echo $canalesResumen['Provincia']['estados']['GESTIONES'] ?? 0; ?>
|
||||
],
|
||||
backgroundColor: ['#ffc107', '#0dcaf0', '#6610f2', '#198754', '#dc3545'],
|
||||
backgroundColor: ['#ffc107', '#0dcaf0', '#6610f2', '#198754', '#6c757d'],
|
||||
hoverOffset: 10,
|
||||
borderWidth: 0,
|
||||
cutout: '70%'
|
||||
@ -681,7 +763,27 @@ include 'layout_header.php';
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'right',
|
||||
labels: {
|
||||
boxWidth: 15,
|
||||
padding: 20,
|
||||
color: '#000',
|
||||
font: { size: 14, weight: 'bold' }
|
||||
}
|
||||
},
|
||||
datalabels: {
|
||||
color: '#fff',
|
||||
font: { weight: 'bold', size: 16 },
|
||||
formatter: (value, ctx) => {
|
||||
let sum = 0;
|
||||
let dataArr = ctx.chart.data.datasets[0].data;
|
||||
dataArr.map(data => { sum += data; });
|
||||
let percentage = (value * 100 / sum).toFixed(0) + "%";
|
||||
return value > 0 ? percentage : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -691,7 +793,11 @@ include 'layout_header.php';
|
||||
new Chart(ctxContraentrega, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA', 'RETORNADO'],
|
||||
labels: [
|
||||
'RUTA CE (<?php echo $canalesResumen['Contraentrega']['estados']['RUTA_CONTRAENTREGA'] ?? 0; ?>)',
|
||||
'EXITOSA ✅ (<?php echo $canalesResumen['Contraentrega']['estados']['ENTREGA EXITOSA'] ?? 0; ?>)',
|
||||
'RETORNADO (<?php echo $canalesResumen['Contraentrega']['estados']['RETORNADO'] ?? 0; ?>)'
|
||||
],
|
||||
datasets: [{
|
||||
data: [
|
||||
<?php echo $canalesResumen['Contraentrega']['estados']['RUTA_CONTRAENTREGA'] ?? 0; ?>,
|
||||
@ -708,7 +814,27 @@ include 'layout_header.php';
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'right',
|
||||
labels: {
|
||||
boxWidth: 15,
|
||||
padding: 20,
|
||||
color: '#000',
|
||||
font: { size: 14, weight: 'bold' }
|
||||
}
|
||||
},
|
||||
datalabels: {
|
||||
color: '#fff',
|
||||
font: { weight: 'bold', size: 16 },
|
||||
formatter: (value, ctx) => {
|
||||
let sum = 0;
|
||||
let dataArr = ctx.chart.data.datasets[0].data;
|
||||
dataArr.map(data => { sum += data; });
|
||||
let percentage = (value * 100 / sum).toFixed(0) + "%";
|
||||
return value > 0 ? percentage : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -759,11 +885,43 @@ include 'layout_header.php';
|
||||
new Chart(ctxEstados, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: <?php echo json_encode(array_column($estadosData, 'estado')); ?>,
|
||||
labels: <?php
|
||||
$labelsConValores = array_map(function($item) {
|
||||
return $item['estado'] . ' (' . $item['total'] . ')';
|
||||
}, $estadosData);
|
||||
echo json_encode($labelsConValores);
|
||||
?>,
|
||||
datasets: [{
|
||||
data: <?php echo json_encode(array_column($estadosData, 'total')); ?>,
|
||||
backgroundColor: ['#ffc107', '#0dcaf0', '#198754', '#dc3545', '#6c757d', '#0d6efd', '#20c997', '#6610f2']
|
||||
backgroundColor: ['#ffc107', '#0dcaf0', '#198754', '#dc3545', '#6c757d', '#0d6efd', '#20c997', '#6610f2'],
|
||||
borderWidth: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'right',
|
||||
labels: {
|
||||
boxWidth: 15,
|
||||
padding: 15,
|
||||
color: '#000',
|
||||
font: { size: 12, weight: 'bold' }
|
||||
}
|
||||
},
|
||||
datalabels: {
|
||||
color: '#fff',
|
||||
font: { weight: 'bold', size: 14 },
|
||||
formatter: (value, ctx) => {
|
||||
let sum = 0;
|
||||
let dataArr = ctx.chart.data.datasets[0].data;
|
||||
dataArr.map(data => { sum += data; });
|
||||
let percentage = (value * 100 / sum).toFixed(0) + "%";
|
||||
return value > 0 ? percentage : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -803,6 +961,124 @@ include 'layout_header.php';
|
||||
}
|
||||
});
|
||||
|
||||
// Gráfico de Rendimiento por Producto en Contraentrega (Barras Apiladas - Vertical)
|
||||
const ctxRendimientoCE = document.getElementById('rendimientoProdCEChart').getContext('2d');
|
||||
new Chart(ctxRendimientoCE, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: <?php echo json_encode(array_map(function($p) {
|
||||
return strlen($p['producto']) > 15 ? substr($p['producto'], 0, 15) . '...' : $p['producto'];
|
||||
}, $prodRendimientoCE)); ?>,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Exitosa ✅',
|
||||
data: <?php echo json_encode(array_column($prodRendimientoCE, 'exitosas')); ?>,
|
||||
backgroundColor: '#198754',
|
||||
},
|
||||
{
|
||||
label: 'Retornado ❌',
|
||||
data: <?php echo json_encode(array_column($prodRendimientoCE, 'retornados')); ?>,
|
||||
backgroundColor: '#dc3545',
|
||||
},
|
||||
{
|
||||
label: 'En Ruta 🚛',
|
||||
data: <?php echo json_encode(array_column($prodRendimientoCE, 'en_ruta')); ?>,
|
||||
backgroundColor: '#0d6efd',
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'x',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
ticks: {
|
||||
maxRotation: 45,
|
||||
minRotation: 45,
|
||||
color: '#000',
|
||||
font: { size: 10, weight: 'bold' }
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
title: { display: true, text: 'Cantidad' }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
labels: { boxWidth: 12, font: { size: 11 } }
|
||||
},
|
||||
datalabels: {
|
||||
color: '#fff',
|
||||
font: { weight: 'bold', size: 10 },
|
||||
formatter: (value) => value > 0 ? value : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Gráfico de Rendimiento por Producto en Provincia (Barras Apiladas - Vertical)
|
||||
const ctxRendimientoProv = document.getElementById('rendimientoProdProvChart').getContext('2d');
|
||||
new Chart(ctxRendimientoProv, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: <?php echo json_encode(array_map(function($p) {
|
||||
return strlen($p['producto']) > 15 ? substr($p['producto'], 0, 15) . '...' : $p['producto'];
|
||||
}, $prodRendimientoProv)); ?>,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Completado ✅',
|
||||
data: <?php echo json_encode(array_column($prodRendimientoProv, 'completados')); ?>,
|
||||
backgroundColor: '#198754',
|
||||
},
|
||||
{
|
||||
label: 'Gestiones ⚠️',
|
||||
data: <?php echo json_encode(array_column($prodRendimientoProv, 'gestiones')); ?>,
|
||||
backgroundColor: '#6c757d',
|
||||
},
|
||||
{
|
||||
label: 'En Proceso 🚛',
|
||||
data: <?php echo json_encode(array_column($prodRendimientoProv, 'en_proceso')); ?>,
|
||||
backgroundColor: '#0dcaf0',
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'x',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
ticks: {
|
||||
maxRotation: 45,
|
||||
minRotation: 45,
|
||||
color: '#000',
|
||||
font: { size: 10, weight: 'bold' }
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
title: { display: true, text: 'Cantidad' }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
labels: { boxWidth: 12, font: { size: 11 } }
|
||||
},
|
||||
datalabels: {
|
||||
color: '#fff',
|
||||
font: { weight: 'bold', size: 10 },
|
||||
formatter: (value) => value > 0 ? value : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Gráfico de Asesores
|
||||
const ctxAsesores = document.getElementById('asesoresChart').getContext('2d');
|
||||
new Chart(ctxAsesores, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user