Autosave: 20260203-020543

This commit is contained in:
Flatlogic Bot 2026-02-03 02:05:44 +00:00
parent bdc6e40a34
commit 6995544f62
4 changed files with 259 additions and 68 deletions

View File

@ -215,4 +215,34 @@ h1, .h1 {
border-radius: 8px;
overflow: hidden; /* This is important to make border-radius work on tables */
border: 1px solid #dee2e6;
}
}
/* Submenu Styles */
.sidebar .submenu {
display: none;
list-style: none;
padding-left: 20px;
background-color: #2c3e50;
}
/* Show submenu when it has .show class or its parent .has-submenu is active */
.sidebar .submenu.show,
.sidebar .nav-item.has-submenu.active > .submenu {
display: block;
}
/* Keep the submenu open when the parent has the 'open' class (for JS toggle) */
.sidebar .nav-item.open > .submenu {
display: block;
}
.sidebar .submenu .nav-link {
padding-left: 35px;
color: #bdc3c7; /* Match main link color */
}
/* Style for the active link within the submenu */
.sidebar .submenu .nav-link.active {
color: #fff; /* White color for active sub-item */
font-weight: bold;
}

View File

@ -1,9 +1,30 @@
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var hasSubmenu = document.querySelectorAll(".has-submenu > a");
for (var i = 0; i < hasSubmenu.length; i++) {
hasSubmenu[i].addEventListener("click", function(e) {
e.preventDefault();
var parent = this.parentElement;
if (parent.classList.contains("open")) {
parent.classList.remove("open");
} else {
// Close other open submenus
var openSubmenus = document.querySelectorAll(".has-submenu.open");
for (var j = 0; j < openSubmenus.length; j++) {
openSubmenus[j].classList.remove("open");
}
parent.classList.add("open");
}
});
}
});
</script>
</body>
</html>
</html>

View File

@ -80,23 +80,30 @@ $navItems = [
'text' => 'Gestionar Productos',
'roles' => ['Administrador', 'admin']
],
'finanzas' => [
'url' => 'finanzas.php',
'finanzas_group' => [
'icon' => 'fa-dollar-sign',
'text' => 'Finanzas',
'roles' => ['Administrador', 'admin']
],
'recaudo_esperado' => [
'url' => 'recaudo_esperado.php',
'icon' => 'fa-chart-line',
'text' => 'Recaudo Esperado',
'roles' => ['Administrador', 'admin']
],
'rentabilidad' => [
'url' => 'rentabilidad.php',
'icon' => 'fa-chart-pie',
'text' => 'Rentabilidad de Producto',
'roles' => ['Administrador', 'admin']
'roles' => ['Administrador', 'admin'],
'submenu' => [
'finanzas' => [
'url' => 'finanzas.php',
'icon' => 'fa-dollar-sign',
'text' => 'Finanzas',
'roles' => ['Administrador', 'admin']
],
'recaudo_esperado' => [
'url' => 'recaudo_esperado.php',
'icon' => 'fa-chart-line',
'text' => 'Recaudo Esperado',
'roles' => ['Administrador', 'admin']
],
'rentabilidad' => [
'url' => 'rentabilidad.php',
'icon' => 'fa-chart-pie',
'text' => 'Rentabilidad de Producto',
'roles' => ['Administrador', 'admin']
],
]
],
'info_producto' => [
'url' => 'info_producto.php',
@ -150,13 +157,45 @@ $navItems = [
<div class="sidebar">
<a href="pedidos.php" class="navbar-brand"><h3>FLOOWER CRM</h3></a>
<ul class="nav flex-column">
<?php foreach ($navItems as $key => $item): ?>
<?php if (in_array($userRole, $item['roles'])): ?>
<li class="nav-item">
<a href="<?php echo $item['url']; ?>" class="nav-link"><i class="fas <?php echo $item['icon']; ?>"></i> <?php echo $item['text']; ?></a>
</li>
<?php endif; ?>
<?php endforeach; ?>
<?php
$currentPage = basename($_SERVER['PHP_SELF']);
foreach ($navItems as $key => $item):
if (in_array($userRole, $item['roles'])):
if (isset($item['submenu'])):
$isSubmenuActive = false;
foreach ($item['submenu'] as $sub_item) {
if ($currentPage == $sub_item['url']) {
$isSubmenuActive = true;
break;
}
}
?>
<li class="nav-item has-submenu <?php echo $isSubmenuActive ? 'active' : ''; ?>">
<a href="#" class="nav-link"><i class="fas <?php echo $item['icon']; ?>"></i> <?php echo $item['text']; ?></a>
<ul class="submenu <?php echo $isSubmenuActive ? 'show' : ''; ?>">
<?php foreach ($item['submenu'] as $sub_item):
if (in_array($userRole, $sub_item['roles'])):
$isActive = $currentPage == $sub_item['url'] ? 'active' : '';
?>
<li class="nav-item">
<a href="<?php echo $sub_item['url']; ?>" class="nav-link <?php echo $isActive; ?>"><i class="fas <?php echo $sub_item['icon']; ?>"></i> <?php echo $sub_item['text']; ?></a>
</li>
<?php
endif;
endforeach; ?>
</ul>
</li>
<?php else:
$isActive = $currentPage == $item['url'] ? 'active' : '';
?>
<li class="nav-item">
<a href="<?php echo $item['url']; ?>" class="nav-link <?php echo $isActive; ?>"><i class="fas <?php echo $item['icon']; ?>"></i> <?php echo $item['text']; ?></a>
</li>
<?php endif;
endif;
endforeach;
?>
<li class="nav-item mt-auto">
<a href="logout.php" class="nav-link"><i class="fas fa-sign-out-alt"></i> Cerrar Sesión</a>
</li>

View File

@ -28,7 +28,109 @@ $where_clause = '';
$params = [];
if ($fecha_inicio_obj && $fecha_fin_obj) {
$where_clause = ' WHERE p.created_at BETWEEN ? AND ?';
$where_conditions = ["p.estado = 'Completado'", "p.estado_pago = 'Pendiente'"];
$params = [];
if ($fecha_inicio_obj && $fecha_fin_obj) {
$where_conditions[] = 'p.created_at BETWEEN ? AND ?';
$params[] = $fecha_inicio_obj->format('Y-m-d 00:00:00');
$params[] = $fecha_fin_obj->format('Y-m-d 23:59:59');
}
$where_clause = ' WHERE ' . implode(' AND ', $where_conditions);
// --- RECAUDO ESPERADO POR ASESORA --
$asesoras_recuado = [];
try {
$sql_asesoras = "
SELECT
u.nombre_asesor AS asesora_nombre,
SUM(p.monto_total) AS monto_total,
COUNT(p.id) AS cantidad_pedidos
FROM users u
JOIN pedidos p ON u.id = p.asesor_id
{$where_clause}
GROUP BY u.id, u.nombre_asesor
ORDER BY monto_total DESC
";
$stmt_asesoras = $pdo->prepare($sql_asesoras);
$stmt_asesoras->execute($params);
$asesoras_recuado = $stmt_asesoras->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Error en RECAUDO POR ASESORA: " . $e->getMessage());
}
// --- RESUMEN GENERAL Y POR ASESOR (VERSIÓN CORREGIDA) ---
$productos_resumen = [];
$resumen_por_asesor = [];
$pedidos = []; // Inicializar para que exista siempre
try {
// 1. Obtener precios de productos
$precios_productos = $pdo->query("SELECT nombre, price FROM products")->fetchAll(PDO::FETCH_KEY_PAIR);
// 2. Obtener todos los pedidos relevantes
$sql_pedidos = "
SELECT
p.producto,
p.cantidad,
COALESCE(u.nombre_asesor, u.username, 'Sin Asesor') AS asesor_nombre
FROM pedidos p
LEFT JOIN users u ON p.asesor_id = u.id
{$where_clause}
";
$stmt_pedidos = $pdo->prepare($sql_pedidos);
$stmt_pedidos->execute($params);
$pedidos = $stmt_pedidos->fetchAll(PDO::FETCH_ASSOC); // Traer todos los pedidos a un array
// 3. Procesar pedidos en PHP
foreach ($pedidos as $pedido) { // Iterar sobre el array
$asesor = $pedido['asesor_nombre'];
$nombres = explode(',', $pedido['producto']);
$cantidades = explode(',', $pedido['cantidad']);
foreach ($nombres as $index => $nombre) {
$nombre = trim($nombre);
if (empty($nombre)) continue;
$cantidad = isset($cantidades[$index]) ? (int)trim($cantidades[$index]) : 0;
$precio = isset($precios_productos[$nombre]) ? (float)$precios_productos[$nombre] : 0;
$monto = $cantidad * $precio;
// Acumular para Resumen General
if (!isset($productos_resumen[$nombre])) {
$productos_resumen[$nombre] = ['cantidad' => 0, 'monto' => 0, 'num_pedidos' => 0];
}
$productos_resumen[$nombre]['cantidad'] += $cantidad;
$productos_resumen[$nombre]['monto'] += $monto;
$productos_resumen[$nombre]['num_pedidos']++;
// Acumular para Desglose por Asesor
if (!isset($resumen_por_asesor[$asesor][$nombre])) {
$resumen_por_asesor[$asesor][$nombre] = ['cantidad' => 0, 'monto' => 0, 'num_pedidos' => 0];
}
$resumen_por_asesor[$asesor][$nombre]['cantidad'] += $cantidad;
$resumen_por_asesor[$asesor][$nombre]['monto'] += $monto;
$resumen_por_asesor[$asesor][$nombre]['num_pedidos']++;
}
}
// 4. Ordenar resultados
uasort($productos_resumen, function($a, $b) { return $b['cantidad'] <=> $a['cantidad']; });
foreach ($resumen_por_asesor as &$productos) {
uasort($productos, function($a, $b) { return $b['cantidad'] <=> $a['cantidad']; });
}
unset($productos);
} catch (PDOException $e) {
error_log("Error en RESUMEN GENERAL DE PRODUCTOS: " . $e->getMessage());
$productos_resumen = [];
$resumen_por_asesor = [];
$pedidos = []; // Asegurar que se reinicia en caso de error
}
$params[] = $fecha_inicio_obj->format('Y-m-d 00:00:00');
$params[] = $fecha_fin_obj->format('Y-m-d 23:59:59');
}
@ -57,81 +159,80 @@ try {
error_log("Error en RECAUDO POR ASESORA: " . $e->getMessage());
}
// --- RESUMEN GENERAL Y POR ASESOR ---
// --- RESUMEN GENERAL Y POR ASESOR (VERSIÓN CORREGIDA) ---
$productos_resumen = [];
$resumen_por_asesor = [];
$pedidos = []; // Inicializar para que exista siempre
try {
// Consulta para obtener el resumen de productos vendidos
$sql_resumen = "
// 1. Obtener precios de productos
$precios_productos = $pdo->query("SELECT nombre, price FROM products")->fetchAll(PDO::FETCH_KEY_PAIR);
// 2. Obtener todos los pedidos relevantes
$sql_pedidos = "
SELECT
p.producto,
p.cantidad,
p.monto_total,
COALESCE(u.nombre_asesor, u.username, 'Sin Asesor') AS asesor_nombre
FROM pedidos p
LEFT JOIN users u ON p.asesor_id = u.id
{$where_clause}
";
$stmt_resumen = $pdo->prepare($sql_resumen);
$stmt_resumen->execute($params);
$pedidos_resumen = $stmt_resumen->fetchAll(PDO::FETCH_ASSOC);
$stmt_pedidos = $pdo->prepare($sql_pedidos);
$stmt_pedidos->execute($params);
$pedidos = $stmt_pedidos->fetchAll(PDO::FETCH_ASSOC); // Traer todos los pedidos a un array
$temp_resumen = [];
$temp_asesor = [];
foreach ($pedidos_resumen as $pedido) {
// 3. Procesar pedidos en PHP
foreach ($pedidos as $pedido) { // Iterar sobre el array
$asesor = $pedido['asesor_nombre'];
$productos_pedido = explode(',', $pedido['producto']);
$cantidades_pedido = explode(',', $pedido['cantidad']);
$nombres = explode(',', $pedido['producto']);
$cantidades = explode(',', $pedido['cantidad']);
// Solo si el pedido tiene un único producto, asignamos el monto total.
$monto_a_distribuir = (count($productos_pedido) == 1) ? $pedido['monto_total'] : 0;
foreach ($nombres as $index => $nombre) {
$nombre = trim($nombre);
if (empty($nombre)) continue;
foreach ($productos_pedido as $index => $nombre_prod) {
$nombre_prod = trim($nombre_prod);
if (empty($nombre_prod)) continue;
$cantidad = isset($cantidades[$index]) ? (int)trim($cantidades[$index]) : 0;
$precio = isset($precios_productos[$nombre]) ? (float)$precios_productos[$nombre] : 0;
$monto = $cantidad * $precio;
$cantidad = isset($cantidades_pedido[$index]) ? (int)trim($cantidades_pedido[$index]) : 0;
// Resumen general
if (!isset($temp_resumen[$nombre_prod])) {
$temp_resumen[$nombre_prod] = ['cantidad' => 0, 'monto' => 0, 'num_pedidos' => 0];
// Acumular para Resumen General
if (!isset($productos_resumen[$nombre])) {
$productos_resumen[$nombre] = ['cantidad' => 0, 'monto' => 0, 'num_pedidos' => 0];
}
$temp_resumen[$nombre_prod]['cantidad'] += $cantidad;
$temp_resumen[$nombre_prod]['monto'] += $monto_a_distribuir;
$temp_resumen[$nombre_prod]['num_pedidos']++;
$productos_resumen[$nombre]['cantidad'] += $cantidad;
$productos_resumen[$nombre]['monto'] += $monto;
$productos_resumen[$nombre]['num_pedidos']++;
// Desglose por asesor
if (!isset($temp_asesor[$asesor][$nombre_prod])) {
$temp_asesor[$asesor][$nombre_prod] = ['cantidad' => 0, 'monto' => 0, 'num_pedidos' => 0];
// Acumular para Desglose por Asesor
if (!isset($resumen_por_asesor[$asesor][$nombre])) {
$resumen_por_asesor[$asesor][$nombre] = ['cantidad' => 0, 'monto' => 0, 'num_pedidos' => 0];
}
$temp_asesor[$asesor][$nombre_prod]['cantidad'] += $cantidad;
$temp_asesor[$asesor][$nombre_prod]['monto'] += $monto_a_distribuir;
$temp_asesor[$asesor][$nombre_prod]['num_pedidos']++;
$resumen_por_asesor[$asesor][$nombre]['cantidad'] += $cantidad;
$resumen_por_asesor[$asesor][$nombre]['monto'] += $monto;
$resumen_por_asesor[$asesor][$nombre]['num_pedidos']++;
}
}
// Ordenar resultados
uasort($temp_resumen, function($a, $b) { return $b['cantidad'] <=> $a['cantidad']; });
foreach ($temp_asesor as &$prods) {
uasort($prods, function($a, $b) { return $b['cantidad'] <=> $a['cantidad']; });
// 4. Ordenar resultados
uasort($productos_resumen, function($a, $b) { return $b['cantidad'] <=> $a['cantidad']; });
foreach ($resumen_por_asesor as &$productos) {
uasort($productos, function($a, $b) { return $b['cantidad'] <=> $a['cantidad']; });
}
unset($prods);
$productos_resumen = $temp_resumen;
$resumen_por_asesor = $temp_asesor;
unset($productos);
} catch (PDOException $e) {
error_log("Error en RESUMEN GENERAL DE PRODUCTOS: " . $e->getMessage());
// Dejar las variables vacías para que la UI muestre "No hay datos"
$productos_resumen = [];
$resumen_por_asesor = [];
$pedidos = []; // Asegurar que se reinicia en caso de error
}
include 'layout_header.php';
?>
<div class="container mt-4">