Autosave: 20260203-020543
This commit is contained in:
parent
bdc6e40a34
commit
6995544f62
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user