Autosave: 20260517-004332

This commit is contained in:
Flatlogic Bot 2026-05-17 00:43:33 +00:00
parent ca33865e0a
commit 5336ec4122
15 changed files with 460 additions and 68 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

View File

@ -12,95 +12,423 @@ if (!isset($_SESSION['user_id']) || !in_array($_SESSION['user_role'], ['Administ
$db = db();
// 1. Estadísticas de hoy
$hoy = date('Y-m-d');
$stmtHoy = $db->prepare("SELECT COUNT(*) as total_pedidos, SUM(monto_total) as total_dinero FROM pedidos WHERE DATE(created_at) = ? AND estado != 'RETORNADO'");
$stmtHoy->execute([$hoy]);
$statsHoy = $stmtHoy->fetch(PDO::FETCH_ASSOC);
// --- LÓGICA DE FILTROS ---
$period = $_GET['period'] ?? '7';
$start_date = $_GET['start_date'] ?? '';
$end_date = $_GET['end_date'] ?? '';
// 2. Pedidos Pendientes (ROTULADO 📦)
$date_condition = "";
$label_period = "";
if ($period === 'custom' && !empty($start_date) && !empty($end_date)) {
$date_condition = "DATE(created_at) BETWEEN '$start_date' AND '$end_date'";
$label_period = "Desde " . date('d/m/Y', strtotime($start_date)) . " hasta " . date('d/m/Y', strtotime($end_date));
} else {
switch ($period) {
case 'today':
$date_condition = "DATE(created_at) = CURDATE()";
$label_period = "Hoy";
break;
case '7':
$date_condition = "DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
$label_period = "Últimos 7 días";
break;
case '30':
$date_condition = "DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)";
$label_period = "Últimos 30 días";
break;
case 'month':
$date_condition = "MONTH(created_at) = MONTH(CURDATE()) AND YEAR(created_at) = YEAR(CURDATE())";
$label_period = "Este Mes";
break;
case 'year':
$date_condition = "DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)";
$label_period = "Último Año";
break;
default:
$date_condition = "DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
$label_period = "Últimos 7 días";
$period = '7';
}
}
// 1. Estadísticas del periodo seleccionado
$stmtStats = $db->query("SELECT COUNT(*) as total_pedidos, SUM(monto_total) as total_dinero FROM pedidos WHERE $date_condition AND estado != 'RETORNADO'");
$statsPeriodo = $stmtStats->fetch(PDO::FETCH_ASSOC);
// 2. Pedidos Pendientes (Global, no depende del filtro de fecha usualmente, pero lo mantendremos así)
$stmtPendientes = $db->query("SELECT COUNT(*) FROM pedidos WHERE estado = 'ROTULADO 📦'");
$pendientes = $stmtPendientes->fetchColumn();
// 3. Recaudo Esperado (En tránsito, En destino, Ruta contraentrega)
// 3. Recaudo Esperado (Global)
$stmtRecaudo = $db->query("SELECT SUM(monto_debe) FROM pedidos WHERE estado IN ('EN TRANSITO 🚛', 'EN DESTINO 🏬', 'RUTA_CONTRAENTREGA') AND (estado_pago != 'Pagado' OR estado_pago IS NULL)");
$recaudoEsperado = $stmtRecaudo->fetchColumn() ?: 0;
// 4. Stock Crítico (menos de 5 unidades en total entre todas las sedes)
// 4. Stock Crítico (Global)
$stmtStock = $db->query("SELECT COUNT(*) FROM (SELECT product_id, SUM(quantity) as total_stock FROM stock_sedes GROUP BY product_id HAVING total_stock <= 5) as critico");
$stockCritico = $stmtStock->fetchColumn() ?: 0;
// 5. Datos para Gráfico de Ventas (Últimos 7 días)
$ventas7dias = [];
for ($i = 6; $i >= 0; $i--) {
$fecha = date('Y-m-d', strtotime("-$i days"));
$stmt = $db->prepare("SELECT COUNT(*) as cant, SUM(monto_total) as monto FROM pedidos WHERE DATE(created_at) = ? AND estado != 'RETORNADO'");
$stmt->execute([$fecha]);
$res = $stmt->fetch(PDO::FETCH_ASSOC);
$ventas7dias[] = [
'fecha' => date('d/m', strtotime($fecha)),
'cantidad' => $res['cant'] ?: 0,
'monto' => $res['monto'] ?: 0
];
// 5. Datos para Gráfico de Ventas (Ajustado al periodo)
$ventasTendencia = [];
if ($period === 'today') {
// Si es hoy, mostrar por horas
$stmt = $db->query("SELECT HOUR(created_at) as hora, COUNT(*) as cant, SUM(monto_total) as monto FROM pedidos WHERE $date_condition AND estado != 'RETORNADO' GROUP BY HOUR(created_at) ORDER BY hora ASC");
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($res as $r) {
$ventasTendencia[] = ['fecha' => $r['hora'] . ':00', 'cantidad' => $r['cant'], 'monto' => $r['monto']];
}
} elseif ($period === 'year') {
// Si es un año, mostrar por meses
$stmt = $db->query("SELECT DATE_FORMAT(created_at, '%Y-%m') as mes, COUNT(*) as cant, SUM(monto_total) as monto FROM pedidos WHERE $date_condition AND estado != 'RETORNADO' GROUP BY mes ORDER BY mes ASC");
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($res as $r) {
$ventasTendencia[] = ['fecha' => date('M Y', strtotime($r['mes'] . '-01')), 'cantidad' => $r['cant'], 'monto' => $r['monto']];
}
} else {
// Mostrar por días
$stmt = $db->query("SELECT DATE(created_at) as fecha, COUNT(*) as cant, SUM(monto_total) as monto FROM pedidos WHERE $date_condition AND estado != 'RETORNADO' GROUP BY DATE(created_at) ORDER BY fecha ASC");
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($res as $r) {
$ventasTendencia[] = ['fecha' => date('d/m', strtotime($r['fecha'])), 'cantidad' => $r['cant'], 'monto' => $r['monto']];
}
}
// 6. Estados de Pedidos
$stmtEstados = $db->query("SELECT estado, COUNT(*) as total FROM pedidos GROUP BY estado");
// 6. Estados de Pedidos (Ajustado al periodo)
$stmtEstados = $db->query("SELECT estado, COUNT(*) as total FROM pedidos WHERE $date_condition GROUP BY estado");
$estadosData = $stmtEstados->fetchAll(PDO::FETCH_ASSOC);
// 7. Top Productos
$stmtTopProd = $db->query("SELECT producto, COUNT(*) as ventas FROM pedidos GROUP BY producto ORDER BY ventas DESC LIMIT 5");
// 7. Top Productos (Ajustado al periodo)
$stmtTopProd = $db->query("SELECT producto, SUM(cantidad) as ventas FROM pedidos WHERE $date_condition AND estado != 'RETORNADO' GROUP BY producto ORDER BY ventas DESC LIMIT 5");
$topProductos = $stmtTopProd->fetchAll(PDO::FETCH_ASSOC);
// 8. Ventas por Asesor
$stmtAsesores = $db->query("SELECT u.nombre_asesor, COUNT(p.id) as total_pedidos, COALESCE(SUM(p.monto_total), 0) as total_monto
// 8. Ventas por Asesor (Ajustado al periodo)
$stmtAsesores = $db->query("SELECT u.nombre_asesor,
COUNT(p.id) as total_pedidos,
COALESCE(SUM(p.monto_total), 0) as total_monto,
COUNT(CASE WHEN p.estado = 'COMPLETADO ✅' THEN 1 END) as completados
FROM users u
LEFT JOIN pedidos p ON u.id = p.asesor_id AND p.estado != 'RETORNADO'
WHERE u.role = 'Asesor'
LEFT JOIN pedidos p ON u.id = p.asesor_id AND $date_condition AND p.estado != 'RETORNADO'
WHERE u.role IN ('Asesor', 'admin', 'Administrador')
GROUP BY u.id
HAVING total_pedidos > 0
ORDER BY total_monto DESC");
$ventasAsesores = $stmtAsesores->fetchAll(PDO::FETCH_ASSOC);
// 9. Comparativa Mensual (Se mantiene global para contexto)
$mesActual = date('Y-m');
$mesPasado = date('Y-m', strtotime('first day of last month'));
$stmtMesActual = $db->prepare("SELECT SUM(monto_total) as monto FROM pedidos WHERE DATE_FORMAT(created_at, '%Y-%m') = ? AND estado != 'RETORNADO'");
$stmtMesActual->execute([$mesActual]);
$montoMesActual = $stmtMesActual->fetchColumn() ?: 0;
$stmtMesPasado = $db->prepare("SELECT SUM(monto_total) as monto FROM pedidos WHERE DATE_FORMAT(created_at, '%Y-%m') = ? AND estado != 'RETORNADO'");
$stmtMesPasado->execute([$mesPasado]);
$montoMesPasado = $stmtMesPasado->fetchColumn() ?: 0;
$crecimientoMensual = $montoMesPasado > 0 ? (($montoMesActual - $montoMesPasado) / $montoMesPasado) * 100 : 0;
// 10. Eficiencia Logística (Ajustado al periodo)
$stmtTiempo = $db->query("SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, fecha_completado)) / 24 as promedio_dias
FROM pedidos
WHERE $date_condition AND estado = 'COMPLETADO ✅' AND fecha_completado IS NOT NULL");
$tiempoPromedio = $stmtTiempo->fetchColumn() ?: 0;
$stmtRetorno = $db->query("SELECT (COUNT(CASE WHEN estado = 'RETORNADO' THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0)) as tasa_retorno
FROM pedidos WHERE $date_condition");
$tasaRetorno = $stmtRetorno->fetchColumn() ?: 0;
// 11. Utilidad Total Estimada (Ajustado al periodo)
$stmtUtilidad = $db->query("
SELECT SUM(p.monto_total - (COALESCE(pr.costo_unitario, 0) * p.cantidad)) as utilidad_total
FROM pedidos p
LEFT JOIN products pr ON p.producto = pr.nombre
WHERE p.$date_condition AND p.estado != 'RETORNADO'
");
$utilidadTotal = $stmtUtilidad->fetchColumn() ?: 0;
// 12. Datos Detallados por Canal (Provincia vs Contraentrega)
$stmtDetalleCanal = $db->query("SELECT
CASE WHEN agencia = 'CONTRAENTREGA' THEN 'Contraentrega' ELSE 'Provincia' END as canal,
estado,
COUNT(*) as total,
SUM(monto_total) as monto
FROM pedidos
WHERE $date_condition
GROUP BY canal, estado");
$detalleCanalRaw = $stmtDetalleCanal->fetchAll(PDO::FETCH_ASSOC);
// 13. Top Productos Detallado (Para la tabla)
$stmtTopDetalle = $db->query("
SELECT p.producto, SUM(p.cantidad) as total_cantidad, SUM(p.monto_total) as monto_total, AVG(pr.costo_unitario) as costo_promedio
FROM pedidos p
LEFT JOIN products pr ON p.producto = pr.nombre
WHERE p.$date_condition AND p.estado != 'RETORNADO'
GROUP BY p.producto
ORDER BY total_cantidad DESC
LIMIT 5
");
$topProductosDetalle = $stmtTopDetalle->fetchAll(PDO::FETCH_ASSOC);
$canalesResumen = [
'Provincia' => ['pedidos' => 0, 'monto' => 0, 'estados' => []],
'Contraentrega' => ['pedidos' => 0, 'monto' => 0, 'estados' => []]
];
foreach ($detalleCanalRaw as $row) {
$canal = $row['canal'];
$estado = $row['estado'];
if ($estado != 'RETORNADO') {
$canalesResumen[$canal]['pedidos'] += $row['total'];
$canalesResumen[$canal]['monto'] += $row['monto'];
}
if (!isset($canalesResumen[$canal]['estados'][$estado])) {
$canalesResumen[$canal]['estados'][$estado] = 0;
}
$canalesResumen[$canal]['estados'][$estado] += $row['total'];
}
$pageTitle = "Dashboard Principal";
include 'layout_header.php';
?>
<div class="container-fluid mt-4">
<!-- Filtros -->
<div class="card shadow mb-4">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-6">
<h1 class="h3 mb-0 text-gray-800">Dashboard de Control</h1>
<p class="text-muted mb-0">Mostrando datos de: <strong><?php echo $label_period; ?></strong></p>
</div>
<div class="col-md-6 text-md-end mt-3 mt-md-0">
<form action="" method="GET" class="d-inline-block">
<div class="btn-group me-2" role="group">
<a href="?period=today" class="btn btn-outline-primary btn-sm <?php echo $period == 'today' ? 'active' : ''; ?>">Hoy</a>
<a href="?period=7" class="btn btn-outline-primary btn-sm <?php echo $period == '7' ? 'active' : ''; ?>">7 Días</a>
<a href="?period=30" class="btn btn-outline-primary btn-sm <?php echo $period == '30' ? 'active' : ''; ?>">30 Días</a>
<a href="?period=month" class="btn btn-outline-primary btn-sm <?php echo $period == 'month' ? 'active' : ''; ?>">Este Mes</a>
<a href="?period=year" class="btn btn-outline-primary btn-sm <?php echo $period == 'year' ? 'active' : ''; ?>">1 Año</a>
</div>
</form>
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#customDateCollapse">
<i class="fas fa-calendar-alt"></i> Personalizado
</button>
</div>
</div>
<div class="collapse mt-3" id="customDateCollapse">
<form action="" method="GET" class="row g-2 justify-content-end">
<input type="hidden" name="period" value="custom">
<div class="col-auto">
<label class="visually-hidden">Desde</label>
<input type="date" name="start_date" class="form-control form-control-sm" value="<?php echo $start_date; ?>" required>
</div>
<div class="col-auto">
<label class="visually-hidden">Hasta</label>
<input type="date" name="end_date" class="form-control form-control-sm" value="<?php echo $end_date; ?>" required>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary btn-sm">Filtrar</button>
</div>
</form>
</div>
</div>
</div>
<!-- Tarjetas de Resumen -->
<div class="row mb-4">
<div class="col-md-3">
<div class="col-xl-3 col-md-6 mb-3">
<div class="card bg-primary text-white shadow h-100">
<div class="card-body">
<h6 class="card-title">Ventas Hoy</h6>
<h3>S/ <?php echo number_format($statsHoy['total_dinero'] ?: 0, 2); ?></h3>
<p class="mb-0"><?php echo $statsHoy['total_pedidos']; ?> pedidos</p>
<h6 class="card-title">Ventas Periodo</h6>
<h3>S/ <?php echo number_format($statsPeriodo['total_dinero'] ?: 0, 2); ?></h3>
<p class="mb-0"><?php echo $statsPeriodo['total_pedidos']; ?> pedidos</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="col-xl-3 col-md-6 mb-3">
<div class="card bg-success text-white shadow h-100">
<div class="card-body">
<h6 class="card-title">Utilidad Estimada</h6>
<h3>S/ <?php echo number_format($utilidadTotal, 2); ?></h3>
<p class="mb-0">Margen bruto del periodo</p>
</div>
</div>
</div>
<div class="col-xl-2 col-md-4 mb-3">
<div class="card bg-warning text-dark shadow h-100">
<div class="card-body">
<h6 class="card-title">Pedidos Pendientes</h6>
<h6 class="card-title">Pendientes</h6>
<h3><?php echo $pendientes; ?></h3>
<p class="mb-0">Por procesar</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white shadow h-100">
<div class="col-xl-2 col-md-4 mb-3">
<div class="card bg-info text-white shadow h-100">
<div class="card-body">
<h6 class="card-title">Recaudo Esperado</h6>
<h3>S/ <?php echo number_format($recaudoEsperado, 2); ?></h3>
<p class="mb-0">En ruta / Contraentrega</p>
<p class="mb-0">En ruta (Global)</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="col-xl-2 col-md-4 mb-3">
<div class="card bg-danger text-white shadow h-100">
<div class="card-body">
<h6 class="card-title">Stock Crítico</h6>
<h3><?php echo $stockCritico; ?></h3>
<p class="mb-0">Productos < 5 unidades</p>
<p class="mb-0">Bajo stock</p>
</div>
</div>
</div>
</div>
<!-- Tarjetas de Eficiencia y Crecimiento -->
<div class="row mb-4">
<div class="col-md-4 mb-3">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Ventas Mes Actual vs Pasado</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">S/ <?php echo number_format($montoMesActual, 2); ?></div>
<div class="mt-2 mb-0 text-muted text-xs">
<span class="<?php echo $crecimientoMensual >= 0 ? 'text-success' : 'text-danger'; ?> mr-2">
<i class="fa <?php echo $crecimientoMensual >= 0 ? 'fa-arrow-up' : 'fa-arrow-down'; ?>"></i> <?php echo number_format(abs($crecimientoMensual), 1); ?>%
</span>
<span>Desde el mes pasado</span>
</div>
</div>
<div class="col-auto">
<i class="fas fa-calendar fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Tiempo Promedio de Entrega</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($tiempoPromedio, 1); ?> Días</div>
<div class="mt-2 mb-0 text-muted text-xs">
<span>En el periodo seleccionado</span>
</div>
</div>
<div class="col-auto">
<i class="fas fa-shipping-fast fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card border-left-danger shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">Tasa de Retorno (Devoluciones)</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($tasaRetorno, 1); ?>%</div>
<div class="mt-2 mb-0 text-muted text-xs">
<span>En el periodo seleccionado</span>
</div>
</div>
<div class="col-auto">
<i class="fas fa-undo fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Comparativa de Canales: Provincia vs Contraentrega -->
<div class="row mb-4">
<!-- Sección Provincia -->
<div class="col-md-6 mb-3">
<div class="card shadow h-100">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="fas fa-map-marked-alt me-2"></i> Provincia (Agencias)</h5>
<span class="badge bg-light text-primary">Logística Externa</span>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-6 border-end">
<small class="text-muted d-block">Pedidos</small>
<h4 class="mb-0"><?php echo $canalesResumen['Provincia']['pedidos']; ?></h4>
</div>
<div class="col-6">
<small class="text-muted d-block">Monto Total</small>
<h4 class="mb-0 text-primary">S/ <?php echo number_format($canalesResumen['Provincia']['monto'], 2); ?></h4>
</div>
</div>
<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'];
foreach ($estadosProv as $est):
$count = $canalesResumen['Provincia']['estados'][$est] ?? 0;
$pct = $canalesResumen['Provincia']['pedidos'] > 0 ? ($count / array_sum($canalesResumen['Provincia']['estados'])) * 100 : 0;
?>
<div class="list-group-item d-flex justify-content-between align-items-center px-0">
<span><?php echo $est; ?></span>
<div class="d-flex align-items-center">
<span class="badge bg-secondary rounded-pill me-2"><?php echo $count; ?></span>
<small class="text-muted" style="width: 40px;"><?php echo round($pct); ?>%</small>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<!-- Sección Contraentrega -->
<div class="col-md-6 mb-3">
<div class="card shadow h-100">
<div class="card-header bg-warning text-dark d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="fas fa-motorcycle me-2"></i> Contraentrega (Local)</h5>
<span class="badge bg-dark text-white">Ruta Propia</span>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-6 border-end">
<small class="text-muted d-block">Pedidos</small>
<h4 class="mb-0"><?php echo $canalesResumen['Contraentrega']['pedidos']; ?></h4>
</div>
<div class="col-6">
<small class="text-muted d-block">Monto Total</small>
<h4 class="mb-0 text-warning">S/ <?php echo number_format($canalesResumen['Contraentrega']['monto'], 2); ?></h4>
</div>
</div>
<h6 class="border-bottom pb-2 mb-3">Estados Ruta Contraentrega</h6>
<div class="list-group list-group-flush">
<?php
$estadosCE = ['RUTA_CONTRAENTREGA', 'ENTREGA EXITOSA', 'RETORNADO'];
foreach ($estadosCE as $est):
$count = $canalesResumen['Contraentrega']['estados'][$est] ?? 0;
$totalCE = array_sum($canalesResumen['Contraentrega']['estados']);
$pct = $totalCE > 0 ? ($count / $totalCE) * 100 : 0;
$color = 'secondary';
if ($est == 'RUTA_CONTRAENTREGA') $color = 'primary';
if ($est == 'ENTREGA EXITOSA') $color = 'success';
if ($est == 'RETORNADO') $color = 'danger';
?>
<div class="list-group-item d-flex justify-content-between align-items-center px-0">
<strong><?php echo $est; ?></strong>
<div class="d-flex align-items-center">
<span class="badge bg-<?php echo $color; ?> rounded-pill me-2"><?php echo $count; ?></span>
<small class="text-muted" style="width: 40px;"><?php echo round($pct); ?>%</small>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="mt-3">
<a href="ruta_contraentrega.php" class="btn btn-sm btn-outline-warning w-100">Ver Detalle de Ruta</a>
</div>
</div>
</div>
</div>
@ -111,7 +439,7 @@ include 'layout_header.php';
<div class="col-md-8 mb-4">
<div class="card shadow">
<div class="card-header bg-white">
<h5 class="mb-0">Tendencia de Ventas (Últimos 7 días)</h5>
<h5 class="mb-0">Tendencia de Ventas (<?php echo $label_period; ?>)</h5>
</div>
<div class="card-body">
<canvas id="ventasChart" height="100"></canvas>
@ -135,11 +463,53 @@ include 'layout_header.php';
<!-- Top Productos -->
<div class="col-md-6 mb-4">
<div class="card shadow">
<div class="card-header bg-white">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">Top 5 Productos más Vendidos</h5>
<span class="badge bg-primary">Por Unidades</span>
</div>
<div class="card-body">
<canvas id="productosChart"></canvas>
<canvas id="productosChart" class="mb-4"></canvas>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Producto</th>
<th class="text-center">Cant.</th>
<th class="text-end">Monto Total</th>
<th class="text-end">Rent. %</th>
</tr>
</thead>
<tbody>
<?php foreach ($topProductosDetalle as $prod):
$costoTotal = $prod['costo_promedio'] * $prod['total_cantidad'];
$utilidad = $prod['monto_total'] - $costoTotal;
$rentabilidad = ($prod['monto_total'] > 0) ? ($utilidad / $prod['monto_total']) * 100 : 0;
$rentClass = $rentabilidad > 30 ? 'text-success' : ($rentabilidad > 15 ? 'text-warning' : 'text-danger');
?>
<tr>
<td class="fw-bold text-dark small"><?php echo htmlspecialchars($prod['producto']); ?></td>
<td class="text-center">
<span class="badge bg-light text-primary border border-primary px-2">
<?php echo number_format($prod['total_cantidad']); ?>
</span>
</td>
<td class="text-end fw-bold text-success small">
S/ <?php echo number_format($prod['monto_total'], 2); ?>
</td>
<td class="text-end fw-bold <?php echo $rentClass; ?> small">
<?php echo number_format($rentabilidad, 1); ?>%
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($topProductosDetalle)): ?>
<tr>
<td colspan="4" class="text-center text-muted py-3">No hay datos en este periodo</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
@ -147,29 +517,10 @@ include 'layout_header.php';
<div class="col-md-6 mb-4">
<div class="card shadow">
<div class="card-header bg-white">
<h5 class="mb-0">Rendimiento por Asesor</h5>
<h5 class="mb-0">Rendimiento por Asesor (Monto Total)</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Asesor</th>
<th>Pedidos</th>
<th>Total Ventas</th>
</tr>
</thead>
<tbody>
<?php foreach ($ventasAsesores as $asesor): ?>
<tr>
<td><?php echo htmlspecialchars($asesor['nombre_asesor']); ?></td>
<td><?php echo $asesor['total_pedidos']; ?></td>
<td>S/ <?php echo number_format($asesor['total_monto'], 2); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<canvas id="asesoresChart"></canvas>
</div>
</div>
</div>
@ -183,17 +534,17 @@ include 'layout_header.php';
new Chart(ctxVentas, {
type: 'line',
data: {
labels: <?php echo json_encode(array_column($ventas7dias, 'fecha')); ?>,
labels: <?php echo json_encode(array_column($ventasTendencia, 'fecha')); ?>,
datasets: [{
label: 'Monto (S/)',
data: <?php echo json_encode(array_column($ventas7dias, 'monto')); ?>,
data: <?php echo json_encode(array_column($ventasTendencia, 'monto')); ?>,
borderColor: '#0d6efd',
backgroundColor: 'rgba(13, 110, 253, 0.1)',
fill: true,
yAxisID: 'y',
}, {
label: 'Cantidad Pedidos',
data: <?php echo json_encode(array_column($ventas7dias, 'cantidad')); ?>,
data: <?php echo json_encode(array_column($ventasTendencia, 'cantidad')); ?>,
borderColor: '#198754',
borderDash: [5, 5],
yAxisID: 'y1',
@ -227,7 +578,7 @@ include 'layout_header.php';
labels: <?php echo json_encode(array_column($estadosData, 'estado')); ?>,
datasets: [{
data: <?php echo json_encode(array_column($estadosData, 'total')); ?>,
backgroundColor: ['#ffc107', '#0dcaf0', '#198754', '#dc3545', '#6c757d', '#0d6efd']
backgroundColor: ['#ffc107', '#0dcaf0', '#198754', '#dc3545', '#6c757d', '#0d6efd', '#20c997', '#6610f2']
}]
}
});
@ -237,7 +588,7 @@ include 'layout_header.php';
new Chart(ctxProductos, {
type: 'bar',
data: {
labels: <?php echo json_encode(array_column($topProductos, 'producto')); ?>,
labels: <?php echo json_encode(array_map(function($p) { return strlen($p['producto']) > 20 ? substr($p['producto'], 0, 20) . '...' : $p['producto']; }, $topProductos)); ?>,
datasets: [{
label: 'Unidades Vendidas',
data: <?php echo json_encode(array_column($topProductos, 'ventas')); ?>,
@ -249,6 +600,47 @@ include 'layout_header.php';
responsive: true
}
});
// Gráfico de Asesores
const ctxAsesores = document.getElementById('asesoresChart').getContext('2d');
new Chart(ctxAsesores, {
type: 'bar',
data: {
labels: <?php echo json_encode(array_column($ventasAsesores, 'nombre_asesor')); ?>,
datasets: [{
label: 'Monto Total (S/)',
data: <?php echo json_encode(array_column($ventasAsesores, 'total_monto')); ?>,
backgroundColor: '#198754',
yAxisID: 'y',
}, {
label: 'Efectividad (%)',
data: <?php echo json_encode(array_map(function($a) {
return $a['total_pedidos'] > 0 ? round(($a['completados'] / $a['total_pedidos']) * 100, 1) : 0;
}, $ventasAsesores)); ?>,
type: 'line',
borderColor: '#ffc107',
backgroundColor: '#ffc107',
yAxisID: 'y1',
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
position: 'left',
title: { display: true, text: 'Soles' }
},
y1: {
beginAtZero: true,
position: 'right',
max: 100,
grid: { drawOnChartArea: false },
title: { display: true, text: 'Efectividad %' }
}
}
}
});
</script>
<?php include 'layout_footer.php'; ?>