diff --git a/dashboard.php b/dashboard.php index df805b64..6181fbd5 100644 --- a/dashboard.php +++ b/dashboard.php @@ -192,52 +192,106 @@ if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $use } } -// TABLA DE PEDIDOS POR DÍA Y ASESORA (TIPO EXCEL) -$pedidos_mensual_asesora = []; +// TABLA DE PEDIDOS POR DÍA Y ASESORA (TIPO EXCEL) - PROVINCIA y CONTRAENTREGA +$pedidos_mensual_provincia_asesora = []; +$pedidos_mensual_contraentrega_asesora = []; $asesoras_mensual = []; +$asesoras_mensual_provincia = []; +$asesoras_mensual_contraentrega = []; + if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $user_role === 'Logistica' || $user_role === 'Asesor') { // 1. Obtener todas las asesoras $stmt_asesoras = $pdo->query("SELECT id, nombre_asesor FROM users WHERE role = 'Asesor' AND nombre_asesor IS NOT NULL AND nombre_asesor != ''"); $asesoras_mensual = $stmt_asesoras->fetchAll(PDO::FETCH_ASSOC); + + // Por defecto, las dos tablas usan la misma lista (luego se ordena por cada tipo) + $asesoras_mensual_provincia = $asesoras_mensual; + $asesoras_mensual_contraentrega = $asesoras_mensual; + $asesora_ids = array_column($asesoras_mensual, 'id'); - // 2. Obtener y procesar los datos de pedidos del mes if (!empty($asesora_ids)) { - $placeholders = implode(',', array_fill(0, count($asesora_ids), '?')); - $stmt_pedidos_mensual = $pdo->prepare(" + // PROVINCIA + $estados_provincia = ['ROTULADO 📦', 'EN TRANSITO 🚛', 'EN DESTINO 🏬', 'COMPLETADO ✅']; + $placeholders_estados_prov = implode(',', array_fill(0, count($estados_provincia), '?')); + $placeholders_asesores = implode(',', array_fill(0, count($asesora_ids), '?')); + + $stmt_prov = $pdo->prepare(" SELECT DAY(created_at) as dia, asesor_id, COUNT(id) as total_pedidos FROM pedidos - WHERE MONTH(created_at) = ? AND YEAR(created_at) = ? AND asesor_id IN (" . $placeholders . ") + WHERE estado IN ($placeholders_estados_prov) + AND MONTH(created_at) = ? AND YEAR(created_at) = ? + AND asesor_id IN ($placeholders_asesores) GROUP BY dia, asesor_id "); - $params = array_merge([$selected_month, $selected_year], $asesora_ids); - $stmt_pedidos_mensual->execute($params); - $pedidos_data = $stmt_pedidos_mensual->fetchAll(PDO::FETCH_ASSOC); + $params_prov = array_merge($estados_provincia, [$selected_month, $selected_year], $asesora_ids); + $stmt_prov->execute($params_prov); + $pedidos_prov_data = $stmt_prov->fetchAll(PDO::FETCH_ASSOC); - foreach ($pedidos_data as $row) { - $pedidos_mensual_asesora[$row['dia']][$row['asesor_id']] = $row['total_pedidos']; + foreach ($pedidos_prov_data as $row) { + $dia = (int)$row['dia']; + $asesor_id = (int)$row['asesor_id']; + $pedidos_mensual_provincia_asesora[$dia][$asesor_id] = (int)$row['total_pedidos']; } - // 3. Calcular totales por asesora para el mes - $totales_por_asesora_para_orden = array_fill_keys($asesora_ids, 0); - foreach ($pedidos_mensual_asesora as $dia_data) { + // Totales por asesora (Provincia) para ordenar columnas + $totales_prov_para_orden = array_fill_keys($asesora_ids, 0); + foreach ($pedidos_mensual_provincia_asesora as $dia_data) { foreach ($dia_data as $asesor_id => $cantidad) { - if (isset($totales_por_asesora_para_orden[$asesor_id])) { - $totales_por_asesora_para_orden[$asesor_id] += $cantidad; + if (isset($totales_prov_para_orden[$asesor_id])) { + $totales_prov_para_orden[$asesor_id] += (int)$cantidad; } } } - // 4. Ordenar el array de asesoras basado en el total de pedidos (de menor a mayor) - usort($asesoras_mensual, function ($a, $b) use ($totales_por_asesora_para_orden) { - $total_a = $totales_por_asesora_para_orden[$a['id']] ?? 0; - $total_b = $totales_por_asesora_para_orden[$b['id']] ?? 0; - return $total_a <=> $total_b; + usort($asesoras_mensual_provincia, function ($a, $b) use ($totales_prov_para_orden) { + $total_a = $totales_prov_para_orden[$a['id']] ?? 0; + $total_b = $totales_prov_para_orden[$b['id']] ?? 0; + return $total_a <=> $total_b; // de menor a mayor + }); + + // CONTRAENTREGA + $estados_contraentrega = ['RUTA_CONTRAENTREGA', 'RETORNADO', 'ENTREGA EXITOSA']; + $placeholders_estados_contra = implode(',', array_fill(0, count($estados_contraentrega), '?')); + + $stmt_contra = $pdo->prepare(" + SELECT DAY(created_at) as dia, asesor_id, COUNT(id) as total_pedidos + FROM pedidos + WHERE estado IN ($placeholders_estados_contra) + AND MONTH(created_at) = ? AND YEAR(created_at) = ? + AND asesor_id IN ($placeholders_asesores) + GROUP BY dia, asesor_id + "); + $params_contra = array_merge($estados_contraentrega, [$selected_month, $selected_year], $asesora_ids); + $stmt_contra->execute($params_contra); + $pedidos_contra_data = $stmt_contra->fetchAll(PDO::FETCH_ASSOC); + + foreach ($pedidos_contra_data as $row) { + $dia = (int)$row['dia']; + $asesor_id = (int)$row['asesor_id']; + $pedidos_mensual_contraentrega_asesora[$dia][$asesor_id] = (int)$row['total_pedidos']; + } + + // Totales por asesora (Contraentrega) para ordenar columnas + $totales_contra_para_orden = array_fill_keys($asesora_ids, 0); + foreach ($pedidos_mensual_contraentrega_asesora as $dia_data) { + foreach ($dia_data as $asesor_id => $cantidad) { + if (isset($totales_contra_para_orden[$asesor_id])) { + $totales_contra_para_orden[$asesor_id] += (int)$cantidad; + } + } + } + + usort($asesoras_mensual_contraentrega, function ($a, $b) use ($totales_contra_para_orden) { + $total_a = $totales_contra_para_orden[$a['id']] ?? 0; + $total_b = $totales_contra_para_orden[$b['id']] ?? 0; + return $total_a <=> $total_b; // de menor a mayor }); } } ?> +

Dashboard

Bienvenido, !

@@ -474,13 +528,13 @@ if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $use - +
-
Reporte Mensual de Pedidos por Asesora ()
+
Reporte Mensual de Pedidos por Asesora - Provincia ()
- +
No hay asesoras configuradas para mostrar en este reporte.
@@ -488,7 +542,7 @@ if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $use Día - + Total Día @@ -497,7 +551,7 @@ if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $use - ' . ($cantidad > 0 ? $cantidad : '-') . ''; @@ -522,7 +576,7 @@ if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $use Total Mes - + @@ -533,6 +587,7 @@ if ($user_role === 'Administrador' || $user_role === 'Control Logistico' || $use
+ @@ -549,93 +604,224 @@ $selected_month] . ' ' . $selected_year; ?>)
- +
-
Resumen Detallado por Asesora ()
+
Resumen Provincia por Asesora ()
- + +
- 'Ruta Contraentrega', 'RETORNADO' => 'Retornado', 'ENTREGA EXITOSA' => 'Entrega Exitosa']; - foreach ($estados as $estado): - $display_name = $estado_display_map[$estado] ?? $estado; - echo ""; - endforeach; ?> + + + - "; - else: - // Initialize totals - $totals = array_fill_keys($estados, 0); - $grand_total = 0; + + + + "; - echo ""; + foreach ($asesor_data as $data): + echo ""; + echo ""; - // Calcular los totales para cada grupo - $total_efectividad = 0; - $total_general_grupo = 0; - foreach ($estados_efectividad as $e) { - $total_efectividad += $data['pedidos'][$e]; - } - foreach ($estados as $estado) { - if (!in_array($estado, $estados_efectividad)) { - $total_general_grupo += $data['pedidos'][$estado]; + $total_provincia = 0; + foreach ($estados_provincia as $e) { + $total_provincia += $data['pedidos'][$e]; } - } - foreach ($estados as $estado): - $cantidad = $data['pedidos'][$estado]; - $totals[$estado] += $cantidad; - echo ""; + + echo ""; + endforeach; + + echo ""; + $grand_total_provincia += $total_provincia; + echo ""; endforeach; - echo ""; - $grand_total += $data['total']; - echo ""; - endforeach; - endif; - ?> - - - - - - " . $total_estado . ""; - endforeach; - echo ""; ?> + + + + + + + + + + + + + +
Asesora" . htmlspecialchars($display_name) . " Total
No hay datos para el período seleccionado.
No hay datos para el período seleccionado.
" . htmlspecialchars($data['nombre']) . "
" . htmlspecialchars($data['nombre']) . ""; - echo $cantidad; + foreach ($estados_provincia as $estado): + $cantidad = (int)($data['pedidos'][$estado] ?? 0); + $totals_provincia[$estado] += $cantidad; + echo ""; + echo $cantidad; - // Calcular porcentaje basado en el grupo - if (in_array($estado, $estados_efectividad)) { - if ($total_efectividad > 0) { - $porcentaje = round(($cantidad / $total_efectividad) * 100, 1); + if ($total_provincia > 0) { + $porcentaje = round(($cantidad / $total_provincia) * 100, 1); echo " ({" . $porcentaje . "}%)"; } - } else { - if ($total_general_grupo > 0) { - $porcentaje = round(($cantidad / $total_general_grupo) * 100, 1); - echo " ({" . $porcentaje . "}%)"; - } - } - - echo "" . $total_provincia . "
" . $data['total'] . "
Total General" . $grand_total . "
Total Provincia
+
+
+
+ + + + +
+
+
Reporte Mensual de Pedidos por Asesora - Contraentrega ()
+ +
+
+ +
No hay asesoras configuradas para mostrar en este reporte.
+ +
+ + + + + + + + + + + + + + + ' . ($cantidad > 0 ? $cantidad : '-') . ''; + endforeach; ?> + + + + + + + + + + + + + +
DíaTotal Día
Total Mes
+
+ +
+
+ + + + +
+
+
Resumen Contraentrega por Asesora ()
+
+
+
+ 'Ruta Contraentrega', + 'RETORNADO' => 'Retornado', + 'ENTREGA EXITOSA' => 'Entrega Exitosa' + ]; + ?> + + + + + + + + - + + + + + + "; + echo ""; + + $total_contraentrega = 0; + foreach ($estados_contraentrega as $e) { + $total_contraentrega += $data['pedidos'][$e]; + } + + foreach ($estados_contraentrega as $estado): + $cantidad = (int)($data['pedidos'][$estado] ?? 0); + $totals_contraentrega[$estado] += $cantidad; + echo ""; + endforeach; + + echo ""; + $grand_total_contraentrega += $total_contraentrega; + echo ""; + endforeach; + ?> + + + + + + + + + + + +
AsesoraTotal
No hay datos para el período seleccionado.
" . htmlspecialchars($data['nombre']) . ""; + echo $cantidad; + + if ($total_contraentrega > 0) { + $porcentaje = round(($cantidad / $total_contraentrega) * 100, 1); + echo " ({" . $porcentaje . "}%)"; + } + + echo "" . $total_contraentrega . "
Total Contraentrega
@@ -682,4 +868,21 @@ document.addEventListener("DOMContentLoaded", function() { }); + + \ No newline at end of file diff --git a/db/migrations/076_add_empaquetado_to_tipo_paquete_enum.sql b/db/migrations/076_add_empaquetado_to_tipo_paquete_enum.sql new file mode 100644 index 00000000..3d12fa20 --- /dev/null +++ b/db/migrations/076_add_empaquetado_to_tipo_paquete_enum.sql @@ -0,0 +1,2 @@ +-- Add EMPAQUETADO as valid option to tipo_paquete ENUM in pedidos table +ALTER TABLE pedidos MODIFY COLUMN tipo_paquete ENUM('RUTA', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'PENDIENTE A RETORNO', 'COMPLETADO', 'EMPAQUETADO', 'RETORNADO', 'ANULADO') DEFAULT NULL; diff --git a/download_ruta_contraentrega.php b/download_ruta_contraentrega.php index 8d8a67c1..e7e64152 100644 --- a/download_ruta_contraentrega.php +++ b/download_ruta_contraentrega.php @@ -297,12 +297,19 @@ try { 'Codigo EAN', 'Cantidad', 'Precio', - 'Total' + 'Total', + 'DE DEDICATORIA / OBS.' ]; foreach ($pedidos as $pedido) { [$provincia, $distrito] = splitProvinciaDistrito($pedido['codigo_rastreo'] ?? ''); + $nota_adicional = trim((string)($pedido['nota_adicional'] ?? '')); + if ($nota_adicional === '') { + $nota_adicional = trim((string)($pedido['descargo'] ?? '')); + } + $nota_adicional_display = $nota_adicional !== '' ? $nota_adicional : ''; + $cantidad_total = parseTotalQuantity($pedido['cantidad'] ?? 0); $total = (float)($pedido['monto_total'] ?? 0); $unit_price_rounded = $cantidad_total > 0 ? round($total / $cantidad_total, 2) : 0; @@ -378,7 +385,8 @@ try { (string)$eanOut, $cantidadOut, $precioOut, - $total + $total, + $nota_adicional_display ]; } diff --git a/pedidos_contraentrega.php b/pedidos_contraentrega.php index c64c5ead..db3744cc 100644 --- a/pedidos_contraentrega.php +++ b/pedidos_contraentrega.php @@ -248,7 +248,7 @@ include 'layout_header.php';
- +
@@ -320,7 +320,7 @@ include 'layout_header.php'; " . htmlspecialchars($paquete) . ""; diff --git a/ruta_contraentrega.php b/ruta_contraentrega.php index 937db409..ea415c1b 100644 --- a/ruta_contraentrega.php +++ b/ruta_contraentrega.php @@ -64,6 +64,7 @@ function getPaqueteStyle($paquete) { if ($paquete === 'NO CONTESTA, VOLVER A LLAMAR') return 'background-color: #fd7e14; color: white;'; // Orange if ($paquete === 'PENDIENTE A RETORNO') return 'background-color: #dc3545; color: white;'; // Red if ($paquete === 'COMPLETADO') return 'background-color: #198754; color: white;'; // Success green + if ($paquete === "EMPAQUETADO") return "background-color: #20c997; color: white;"; // Empaquetado if ($paquete === 'RETORNADO') return 'background-color: #dc3545; color: white;'; // Same red as PENDIENTE A RETORNO if ($paquete === 'ANULADO') return 'background-color: #212529; color: white;'; // Dark return 'background-color: #6c757d; color: white;'; // Secondary grey @@ -505,6 +506,7 @@ function getPaqueteStyleJS(paquete) { if (paquete === 'PENDIENTE A RETORNO') return 'background-color: #dc3545; color: white;'; if (paquete === 'RETORNADO') return 'background-color: #dc3545; color: white;'; if (paquete === 'COMPLETADO') return 'background-color: #198754; color: white;'; + if (paquete === "EMPAQUETADO") return "background-color: #20c997; color: white;"; return 'background-color: #6c757d; color: white;'; } @@ -580,6 +582,7 @@ document.addEventListener('DOMContentLoaded', function() { {val: 'ANULADO', text: 'ANULADO'}, {val: 'PENDIENTE A RETORNO', text: 'PENDIENTE A RETORNO'}, {val: 'RETORNADO', text: 'RETORNADO'}, + {val: "EMPAQUETADO", text: "EMPAQUETADO"}, {val: 'COMPLETADO', text: 'COMPLETADO'} ]; diff --git a/save_pedido_contraentrega.php b/save_pedido_contraentrega.php index eec945a3..0ef8dd68 100644 --- a/save_pedido_contraentrega.php +++ b/save_pedido_contraentrega.php @@ -69,7 +69,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $referencia_domicilio = trim($_POST['referencia_domicilio'] ?? ''); $coordenadas = trim($_POST['coordenadas'] ?? ''); $seguimiento = trim($_POST['seguimiento'] ?? ''); - $fecha_entrega = !empty($_POST['fecha_entrega']) ? trim($_POST['fecha_entrega']) : null; + $fecha_entrega_raw = trim((string)($_POST['fecha_entrega'] ?? '')); + $fecha_entrega = null; + if ($fecha_entrega_raw !== '' && $fecha_entrega_raw !== '0000-00-00') { + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha_entrega_raw)) { + redirectPedidoContraentregaWithError('La Fecha de Entrega no tiene un formato válido (YYYY-MM-DD).', $id, $embed_mode); + } + $fecha_entrega = $fecha_entrega_raw; + } // --- Manejo de multiples productos --- $productos_post = $_POST['productos'] ?? []; @@ -112,7 +119,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($tipo_paquete === '') { $tipo_paquete = null; } - $tipos_paquete_validos = ['RUTA', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'PENDIENTE A RETORNO', 'COMPLETADO', 'RETORNADO', 'ANULADO']; + $tipos_paquete_validos = ['RUTA', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'PENDIENTE A RETORNO', 'COMPLETADO', 'EMPAQUETADO', 'RETORNADO', 'ANULADO']; if ($tipo_paquete !== null && !in_array($tipo_paquete, $tipos_paquete_validos, true)) { redirectPedidoContraentregaWithError('El paquete seleccionado no es válido. Seleccione una opción de la lista.', $id, $embed_mode); } @@ -133,8 +140,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { redirectPedidoContraentregaWithError('Seleccione un distrito válido para la provincia elegida.', $id, $embed_mode); } - if (empty($nombre_completo) || empty($celular) || empty($sede_envio) || empty($provincia) || empty($distrito) || empty($producto) || $cantidad === false || $monto_total === false || empty($coordenadas)) { - redirectPedidoContraentregaWithError('Por favor, complete todos los campos obligatorios: departamento, provincia, distrito y coordenadas.', $id, $embed_mode); + if (empty($nombre_completo) || empty($celular) || empty($sede_envio) || empty($provincia) || empty($distrito) || empty($producto) || $cantidad === false || $monto_total === false || empty($fecha_entrega) || empty($coordenadas)) { + redirectPedidoContraentregaWithError('Por favor, complete todos los campos obligatorios: fecha de entrega, departamento, provincia, distrito y coordenadas.', $id, $embed_mode); } $coordenadasNormalizadas = normalizeContraentregaCoordinates($coordenadas); diff --git a/update_paquete.php b/update_paquete.php index 57233855..2c39c833 100644 --- a/update_paquete.php +++ b/update_paquete.php @@ -26,7 +26,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $stmt = $pdo->prepare("UPDATE pedidos SET tipo_paquete = NULL WHERE id = ?"); $result = $stmt->execute([$pedido_id]); } else { - $valid_types = ['RUTA', 'ANULADO', 'PENDIENTE A RETORNO', 'RETORNADO', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'COMPLETADO']; + $valid_types = ['RUTA', 'ANULADO', 'PENDIENTE A RETORNO', 'RETORNADO', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'COMPLETADO', 'EMPAQUETADO']; if (!in_array($tipo_paquete, $valid_types)) { echo json_encode(['success' => false, 'message' => 'Tipo inválido']); exit;