Autosave: 20260519-183100

This commit is contained in:
Flatlogic Bot 2026-05-19 18:31:00 +00:00
parent 2454573a89
commit 4bc4e0b695

View File

@ -118,15 +118,36 @@ $stmtEstados = $db->query("SELECT estado, COUNT(*) as total FROM pedidos p WHERE
$estadosData = $stmtEstados->fetchAll(PDO::FETCH_ASSOC);
// 7. Top Productos (Ajustado al periodo) - TOTAL
$stmtTopProd = $db->query("SELECT producto, SUM(cantidad) as ventas FROM pedidos p WHERE $date_condition AND estado NOT IN ('RETORNADO', 'GESTIONES') GROUP BY producto ORDER BY ventas DESC");
$stmtTopProd = $db->query("SELECT
CASE WHEN producto LIKE '%,%' THEN CONCAT('COMBO ', cantidad, ': ', producto) ELSE producto END as producto,
SUM(cantidad) as ventas,
SUM(monto_total) as monto
FROM pedidos p
WHERE $date_condition AND estado NOT IN ('RETORNADO', 'GESTIONES')
GROUP BY producto
ORDER BY monto DESC");
$topProductos = $stmtTopProd->fetchAll(PDO::FETCH_ASSOC);
// 7b. Top Productos Contraentrega (Ajustado al periodo)
$stmtTopProdCE = $db->query("SELECT producto, SUM(cantidad) as ventas FROM pedidos p WHERE $date_condition AND estado IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA') GROUP BY producto ORDER BY ventas DESC");
$stmtTopProdCE = $db->query("SELECT
CASE WHEN producto LIKE '%,%' THEN CONCAT('COMBO ', cantidad, ': ', producto) ELSE producto END as producto,
SUM(cantidad) as ventas,
SUM(monto_total) as monto
FROM pedidos p
WHERE $date_condition AND estado IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA')
GROUP BY producto
ORDER BY monto DESC");
$topProductosCE = $stmtTopProdCE->fetchAll(PDO::FETCH_ASSOC);
// 7c. Top Productos Provincia (Ajustado al periodo)
$stmtTopProdProv = $db->query("SELECT producto, SUM(cantidad) as ventas FROM pedidos p WHERE $date_condition AND estado NOT IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA', 'RETORNADO', 'GESTIONES') GROUP BY producto ORDER BY ventas DESC");
$stmtTopProdProv = $db->query("SELECT
CASE WHEN producto LIKE '%,%' THEN CONCAT('COMBO ', cantidad, ': ', producto) ELSE producto END as producto,
SUM(cantidad) as ventas,
SUM(monto_total) as monto
FROM pedidos p
WHERE $date_condition AND estado NOT IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA', 'RETORNADO', 'GESTIONES')
GROUP BY producto
ORDER BY monto DESC");
$topProductosProv = $stmtTopProdProv->fetchAll(PDO::FETCH_ASSOC);
// 8. Ventas por Asesor (Ajustado al periodo)
@ -188,41 +209,53 @@ $detalleCanalRaw = $stmtDetalleCanal->fetchAll(PDO::FETCH_ASSOC);
// 13. Top Productos Detallado (Para la tabla) - TOTAL
$stmtTopDetalle = $db->query("
SELECT p.producto, SUM(p.cantidad) as total_cantidad, SUM(p.monto_total) as monto_total, AVG(pr.costo) as costo_promedio
SELECT
CASE WHEN p.producto LIKE '%,%' THEN CONCAT('COMBO ', p.cantidad, ': ', p.producto) ELSE p.producto END as producto,
SUM(p.cantidad) as total_cantidad,
SUM(p.monto_total) as monto_total,
AVG(pr.costo) as costo_promedio
FROM pedidos p
LEFT JOIN products pr ON p.producto = pr.nombre
WHERE $date_condition AND p.estado NOT IN ('RETORNADO', 'GESTIONES')
GROUP BY p.producto
ORDER BY total_cantidad DESC
GROUP BY producto
ORDER BY monto_total DESC
");
$topProductosDetalle = $stmtTopDetalle->fetchAll(PDO::FETCH_ASSOC);
// 13b. Top Productos Detallado Contraentrega
$stmtTopDetalleCE = $db->query("
SELECT p.producto, SUM(p.cantidad) as total_cantidad, SUM(p.monto_total) as monto_total, AVG(pr.costo) as costo_promedio
SELECT
CASE WHEN p.producto LIKE '%,%' THEN CONCAT('COMBO ', p.cantidad, ': ', p.producto) ELSE p.producto END as producto,
SUM(p.cantidad) as total_cantidad,
SUM(p.monto_total) as monto_total,
AVG(pr.costo) as costo_promedio
FROM pedidos p
LEFT JOIN products pr ON p.producto = pr.nombre
WHERE $date_condition AND p.estado IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA')
GROUP BY p.producto
ORDER BY total_cantidad DESC
GROUP BY producto
ORDER BY monto_total DESC
");
$topProductosDetalleCE = $stmtTopDetalleCE->fetchAll(PDO::FETCH_ASSOC);
// 13c. Top Productos Detallado Provincia
$stmtTopDetalleProv = $db->query("
SELECT p.producto, SUM(p.cantidad) as total_cantidad, SUM(p.monto_total) as monto_total, AVG(pr.costo) as costo_promedio
SELECT
CASE WHEN p.producto LIKE '%,%' THEN CONCAT('COMBO ', p.cantidad, ': ', p.producto) ELSE p.producto END as producto,
SUM(p.cantidad) as total_cantidad,
SUM(p.monto_total) as monto_total,
AVG(pr.costo) as costo_promedio
FROM pedidos p
LEFT JOIN products pr ON p.producto = pr.nombre
WHERE $date_condition AND p.estado NOT IN ('RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA', 'RETORNADO', 'GESTIONES')
GROUP BY p.producto
ORDER BY total_cantidad DESC
GROUP BY producto
ORDER BY monto_total DESC
");
$topProductosDetalleProv = $stmtTopDetalleProv->fetchAll(PDO::FETCH_ASSOC);
// 14. Rendimiento de Productos en Contraentrega (Para Gráfica de Barras Apiladas)
$stmtProdRendimientoCE = $db->query("
SELECT
producto,
CASE WHEN producto LIKE '%,%' THEN CONCAT('COMBO ', cantidad, ': ', producto) ELSE producto END as 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,
@ -238,7 +271,7 @@ $prodRendimientoCE = $stmtProdRendimientoCE->fetchAll(PDO::FETCH_ASSOC);
// 15. Rendimiento de Productos en Provincia
$stmtProdRendimientoProv = $db->query("
SELECT
producto,
CASE WHEN producto LIKE '%,%' THEN CONCAT('COMBO ', cantidad, ': ', producto) ELSE producto END as 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,
@ -253,13 +286,16 @@ $prodRendimientoProv = $stmtProdRendimientoProv->fetchAll(PDO::FETCH_ASSOC);
// 16. Top Upsells (Combinaciones de productos distintos)
$stmtTopUpsell = $db->query("
SELECT producto, COUNT(*) as total_pedidos, SUM(monto_total) as recaudo
SELECT
CONCAT('COMBO ', cantidad, ': ', producto) as producto,
COUNT(*) as total_pedidos,
SUM(monto_total) as recaudo
FROM pedidos p
WHERE $date_condition
AND producto LIKE '%,%'
AND estado NOT IN ('RETORNADO', 'GESTIONES')
GROUP BY producto
ORDER BY total_pedidos DESC
GROUP BY producto, cantidad
ORDER BY recaudo DESC
");
$topUpsells = $stmtTopUpsell->fetchAll(PDO::FETCH_ASSOC);
@ -650,67 +686,6 @@ include 'layout_header.php';
</div>
</div>
<!-- SECCIÓN TOP UPSELL -->
<div class="row">
<div class="col-12 mb-4">
<div class="card shadow h-100 border-left-success">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0 text-success"><i class="fas fa-plus-circle me-2"></i> Top Upsell: Combinaciones de Productos Distintos</h5>
<span class="badge bg-success text-white">Estrategia de Venta Cruzada</span>
</div>
<div class="card-body">
<?php if (empty($topUpsells)): ?>
<div class="text-center py-5">
<i class="fas fa-shopping-basket fa-3x text-gray-300 mb-3"></i>
<p class="text-muted">No se encontraron pedidos con combinaciones de productos distintos en este periodo.</p>
</div>
<?php else: ?>
<div class="row">
<div class="col-lg-7 mb-4">
<div style="position: relative; height: 400px;">
<canvas id="upsellChart"></canvas>
</div>
</div>
<div class="col-lg-5">
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
<table class="table table-hover table-sm align-middle">
<thead class="table-light sticky-top">
<tr>
<th>Combinación de Productos</th>
<th class="text-center">Pedidos</th>
<th class="text-end">Recaudo</th>
</tr>
</thead>
<tbody>
<?php foreach ($topUpsells as $upsell): ?>
<tr>
<td class="small fw-bold text-dark">
<?php
$parts = explode(', ', $upsell['producto']);
foreach($parts as $index => $part) {
echo '<span class="badge bg-light text-dark border mb-1">' . htmlspecialchars($part) . '</span>' . ($index < count($parts)-1 ? ' <i class="fas fa-plus text-muted small mx-1"></i> ' : '');
}
?>
</td>
<td class="text-center">
<span class="badge rounded-pill bg-success px-3"><?php echo $upsell['total_pedidos']; ?></span>
</td>
<td class="text-end fw-bold text-success">
S/ <?php echo number_format($upsell['recaudo'], 2); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Top Productos Contraentrega -->
<div class="col-md-6 mb-4">
@ -797,6 +772,73 @@ include 'layout_header.php';
</div>
</div>
<!-- SECCIÓN TOP UPSELL -->
<div class="row">
<div class="col-12 mb-4">
<div class="card shadow h-100 border-left-success">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0 text-success"><i class="fas fa-plus-circle me-2"></i> Top Upsell: Combinaciones de Productos Distintos</h5>
<span class="badge bg-success text-white">Estrategia de Venta Cruzada</span>
</div>
<div class="card-body">
<?php if (empty($topUpsells)): ?>
<div class="text-center py-5">
<i class="fas fa-shopping-basket fa-3x text-gray-300 mb-3"></i>
<p class="text-muted">No se encontraron pedidos con combinaciones de productos distintos en este periodo.</p>
</div>
<?php else: ?>
<div class="row">
<div class="col-lg-7 mb-4">
<div style="position: relative; height: 400px;">
<canvas id="upsellChart"></canvas>
</div>
</div>
<div class="col-lg-5">
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
<table class="table table-hover table-sm align-middle">
<thead class="table-light sticky-top">
<tr>
<th>Combinación de Productos</th>
<th class="text-center">Pedidos</th>
<th class="text-end">Recaudo</th>
</tr>
</thead>
<tbody>
<?php foreach ($topUpsells as $upsell): ?>
<tr>
<td class="small fw-bold text-dark">
<?php
$displayName = $upsell['producto'];
if (strpos($displayName, 'COMBO ') === 0 && strpos($displayName, ': ') !== false) {
$parts_combo = explode(': ', $displayName, 2);
echo '<span class="badge bg-primary text-white mb-1">' . htmlspecialchars($parts_combo[0]) . '</span> ';
$displayName = $parts_combo[1];
}
$parts = explode(', ', $displayName);
foreach($parts as $index => $part) {
echo '<span class="badge bg-light text-dark border mb-1">' . htmlspecialchars($part) . '</span>' . ($index < count($parts)-1 ? ' <i class="fas fa-plus text-muted small mx-1"></i> ' : '');
}
?>
</td>
<td class="text-center">
<span class="badge rounded-pill bg-success px-3"><?php echo $upsell['total_pedidos']; ?></span>
</td>
<td class="text-end fw-bold text-success">
S/ <?php echo number_format($upsell['recaudo'], 2); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Rendimiento Detallado por Producto en Provincia -->
<div class="col-md-6 mb-4">
@ -1055,8 +1097,8 @@ include 'layout_header.php';
data: {
labels: <?php echo json_encode(array_column($topProductos, 'producto')); ?>,
datasets: [{
label: 'Unidades',
data: <?php echo json_encode(array_column($topProductos, 'ventas')); ?>,
label: 'Monto Total (S/)',
data: <?php echo json_encode(array_column($topProductos, 'monto')); ?>,
backgroundColor: '#0d6efd',
borderRadius: 5,
barPercentage: 0.7
@ -1073,7 +1115,8 @@ include 'layout_header.php';
align: 'top',
color: '#000',
offset: 5,
font: { size: 13, weight: 'bold' }
font: { size: 13, weight: 'bold' },
formatter: (value) => 'S/ ' + new Intl.NumberFormat('es-PE').format(value)
}
},
scales: {
@ -1083,7 +1126,7 @@ include 'layout_header.php';
drawBorder: false,
color: 'rgba(0,0,0,0.05)'
},
title: { display: true, text: 'Unidades Vendidas', font: { weight: 'bold' } }
title: { display: true, text: 'Monto Total (Soles)', font: { weight: 'bold' } }
},
x: {
grid: { display: false },
@ -1104,8 +1147,8 @@ include 'layout_header.php';
data: {
labels: <?php echo json_encode(array_column($topProductosCE, 'producto')); ?>,
datasets: [{
label: 'Unidades',
data: <?php echo json_encode(array_column($topProductosCE, 'ventas')); ?>,
label: 'Monto Total (S/)',
data: <?php echo json_encode(array_column($topProductosCE, 'monto')); ?>,
backgroundColor: '#ffc107',
borderRadius: 5,
barPercentage: 0.7
@ -1121,13 +1164,15 @@ include 'layout_header.php';
anchor: 'end',
align: 'top',
color: '#000',
font: { size: 11, weight: 'bold' }
font: { size: 11, weight: 'bold' },
formatter: (value) => 'S/ ' + new Intl.NumberFormat('es-PE').format(value)
}
},
scales: {
y: {
beginAtZero: true,
grid: { color: 'rgba(0,0,0,0.05)' }
grid: { color: 'rgba(0,0,0,0.05)' },
title: { display: true, text: 'Monto Total (Soles)' }
},
x: {
grid: { display: false },
@ -1148,8 +1193,8 @@ include 'layout_header.php';
data: {
labels: <?php echo json_encode(array_column($topProductosProv, 'producto')); ?>,
datasets: [{
label: 'Unidades',
data: <?php echo json_encode(array_column($topProductosProv, 'ventas')); ?>,
label: 'Monto Total (S/)',
data: <?php echo json_encode(array_column($topProductosProv, 'monto')); ?>,
backgroundColor: '#0dcaf0',
borderRadius: 5,
barPercentage: 0.7
@ -1165,13 +1210,15 @@ include 'layout_header.php';
anchor: 'end',
align: 'top',
color: '#000',
font: { size: 11, weight: 'bold' }
font: { size: 11, weight: 'bold' },
formatter: (value) => 'S/ ' + new Intl.NumberFormat('es-PE').format(value)
}
},
scales: {
y: {
beginAtZero: true,
grid: { color: 'rgba(0,0,0,0.05)' }
grid: { color: 'rgba(0,0,0,0.05)' },
title: { display: true, text: 'Monto Total (Soles)' }
},
x: {
grid: { display: false },
@ -1348,8 +1395,8 @@ include 'layout_header.php';
data: {
labels: <?php echo json_encode(array_column($topUpsells, 'producto')); ?>,
datasets: [{
label: 'Número de Pedidos',
data: <?php echo json_encode(array_column($topUpsells, 'total_pedidos')); ?>,
label: 'Recaudo Total (S/)',
data: <?php echo json_encode(array_column($topUpsells, 'recaudo')); ?>,
backgroundColor: '#198754',
borderRadius: 5,
}]
@ -1364,11 +1411,12 @@ include 'layout_header.php';
anchor: 'end',
align: 'right',
color: '#000',
font: { weight: 'bold' }
font: { weight: 'bold' },
formatter: (value) => 'S/ ' + new Intl.NumberFormat('es-PE').format(value)
}
},
scales: {
x: { beginAtZero: true, title: { display: true, text: 'Cantidad de Pedidos' } },
x: { beginAtZero: true, title: { display: true, text: 'Recaudo (Soles)' } },
y: { ticks: { font: { size: 10 } } }
}
}