From 923ddb7b18cc638d1792ed14383b1e7a8d028e40 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 2 Jun 2026 01:48:20 +0000 Subject: [PATCH] Autosave: 20260602-014831 --- download_ruta_contraentrega.php | 277 +++++++++++++++++++++++++++++--- pedidos.php | 2 + pedidos_contraentrega.php | 6 + ruta_contraentrega.php | 77 +++++++-- save_pedido_contraentrega.php | 13 +- 5 files changed, 340 insertions(+), 35 deletions(-) diff --git a/download_ruta_contraentrega.php b/download_ruta_contraentrega.php index 7472ee0d..8d8a67c1 100644 --- a/download_ruta_contraentrega.php +++ b/download_ruta_contraentrega.php @@ -20,22 +20,80 @@ function splitProvinciaDistrito(?string $value): array $clean = preg_replace('/\s+/', ' ', $value); $parts = preg_split('/\s*[\/\-,|]\s*/', $clean, 2); - if (is_array($parts) && count($parts) === 2) { + if (is_array($parts) and count($parts) === 2) { return [trim($parts[0]), trim($parts[1])]; } return [$clean, '']; } +function parseTotalQuantity(mixed $value): int +{ + if (is_int($value)) { + return $value; + } + + if (is_float($value)) { + return (int)$value; + } + + $s = trim((string)$value); + if ($s === '') { + return 0; + } + + // Old/legacy format can be: "1+2+3" + if (strpos($s, '+') !== false) { + $sum = 0; + foreach (explode('+', $s) as $part) { + $part = trim($part); + if ($part === '') { + continue; + } + if (is_numeric($part)) { + $sum += (int)$part; + continue; + } + if (preg_match('/\d+/', $part, $m)) { + $sum += (int)$m[0]; + } + } + return $sum; + } + + if (is_numeric($s)) { + return (int)$s; + } + + if (preg_match('/\d+/', $s, $m)) { + return (int)$m[0]; + } + + return 0; +} + +function formatPriceSegment(float $value): string +{ + $rounded = round($value, 2); + if (abs($rounded - (int)$rounded) < 0.00001) { + return (string)(int)$rounded; + } + + // Keep up to 2 decimals, but remove trailing zeros + $s = number_format($rounded, 2, '.', ''); + $s = rtrim(rtrim($s, '0'), '.'); + return $s; +} + function extractProductNames(array $pedido): array { $names = []; $notas = (string)($pedido['notas'] ?? ''); - if (preg_match('/Detalle de productos:\s*(.+)$/mi', $notas, $match)) { + if (preg_match('/Detalle de productos:\\s*(.+)$/mi', $notas, $match)) { $items = explode(',', $match[1]); foreach ($items as $item) { - $name = preg_replace('/\s*\(x\d+\)\s*$/i', '', trim($item)); + $name = preg_replace('/\\s*\\(x\\d+\\)\\s*$/i', '', trim($item)); if ($name !== '') { $names[] = $name; } @@ -54,6 +112,115 @@ function extractProductNames(array $pedido): array return array_values(array_unique($names)); } +function extractProductDetailsWithQuantities(array $pedido): array +{ + $notas = (string)($pedido['notas'] ?? ''); + + // Prefer parsing from "Detalle de productos:" (it stores quantities as (xN)) + if (preg_match_all('/Detalle de productos:\\s*(.+)$/mi', $notas, $matches) && !empty($matches[1])) { + $lastLine = trim((string)end($matches[1])); + if ($lastLine !== '') { + $parts = preg_split('/\\s*,\\s*/u', $lastLine); + $out = []; + + if (is_array($parts)) { + foreach ($parts as $part) { + $part = trim((string)$part); + if ($part === '') { + continue; + } + + // Expected format: "Nombre de producto (x2)" + if (preg_match('/^(.*?)\\s*\\(x\\s*(\\d+)\\s*\\)\\s*$/iu', $part, $m)) { + $name = trim((string)$m[1]); + $qty = (int)$m[2]; + } else { + $name = $part; + $qty = 1; + } + + if ($name !== '') { + if ($qty <= 0) { + $qty = 1; + } + $out[] = ['name' => $name, 'qty' => $qty]; + } + } + } + + if (!empty($out)) { + return $out; + } + } + } + + // Fallback: use "producto" + "cantidad" (best-effort) + $productoStr = (string)($pedido['producto'] ?? ''); + $names = []; + foreach (preg_split('/\\s*,\\s*/u', $productoStr) as $n) { + $n = trim((string)$n); + if ($n !== '') { + $names[] = $n; + } + } + + if (empty($names)) { + return []; + } + + $cantidad_total = parseTotalQuantity($pedido['cantidad'] ?? 0); + + // Legacy possibility: "cantidad" can be like "1+2" + $cantidadField = $pedido['cantidad'] ?? ''; + $qtyParts = []; + if (is_string($cantidadField) && strpos(trim($cantidadField), '+') !== false) { + $qtyParts = array_map('trim', explode('+', $cantidadField)); + } + + $out = []; + $countNames = count($names); + foreach ($names as $i => $name) { + if ($countNames === 1) { + $qty = $cantidad_total > 0 ? $cantidad_total : 1; + } elseif (!empty($qtyParts) && isset($qtyParts[$i]) && is_numeric($qtyParts[$i])) { + $qty = (int)$qtyParts[$i]; + if ($qty <= 0) { + $qty = 1; + } + } else { + $qty = 1; + } + + $out[] = ['name' => $name, 'qty' => $qty]; + } + + return $out; +} + +function getEanMap(PDO $pdo, array $productNames): array +{ + $productNames = array_values(array_filter(array_map('trim', $productNames))); + if (empty($productNames)) { + return []; + } + + $uniqueNames = array_values(array_unique($productNames)); + $placeholders = implode(',', array_fill(0, count($uniqueNames), '?')); + $stmt = $pdo->prepare("SELECT nombre, ean FROM products WHERE nombre IN ($placeholders)"); + $stmt->execute($uniqueNames); + + $map = []; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $nombre = (string)($row['nombre'] ?? ''); + $ean = trim((string)($row['ean'] ?? '')); + if ($nombre !== '' && $ean !== '') { + $map[$nombre] = $ean; + } + } + + return $map; +} + function getEansForProducts(PDO $pdo, array $productNames): string { if (empty($productNames)) { @@ -68,7 +235,7 @@ function getEansForProducts(PDO $pdo, array $productNames): string while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $ean = trim((string)($row['ean'] ?? '')); if ($ean !== '') { - $map[$row['nombre']] = $ean; + $map[(string)($row['nombre'] ?? '')] = $ean; } } @@ -79,7 +246,7 @@ function getEansForProducts(PDO $pdo, array $productNames): string } } - return implode(' | ', array_values(array_unique($eans))); + return implode('|', array_values(array_unique($eans))); } try { @@ -88,10 +255,11 @@ try { $user_id = $_SESSION['user_id']; $user_role = $_SESSION['user_role'] ?? 'Asesor'; - $selected_month = $_GET['mes'] ?? ''; - $selected_year = $_GET['año'] ?? ''; $search_query = trim($_GET['q'] ?? ''); + // Exportar solo los pedidos con fecha de entrega "mañana" + $date_condition = "DATE(p.fecha_entrega) = DATE_ADD(CURDATE(), INTERVAL 1 DAY)"; + $sql = "SELECT p.* FROM pedidos p WHERE p.estado IN ('RUTA_CONTRAENTREGA', 'PENDIENTE', 'NO CONTESTO, VOLVER A LLAMAR', 'NO CONTESTO, DEVOLVER LLAMADA', 'CANCELADO', 'REPROGRAMADO', 'ENTREGA EXITOSA', 'RETORNADO')"; $params = []; @@ -107,15 +275,8 @@ try { $params[] = "%$search_query%"; } - if ($selected_month !== '') { - $sql .= " AND MONTH(p.created_at) = ?"; - $params[] = $selected_month; - } - - if ($selected_year !== '') { - $sql .= " AND YEAR(p.created_at) = ?"; - $params[] = $selected_year; - } + // Siempre: pedidos cuya fecha de entrega sea mañana + $sql .= " AND $date_condition"; $sql .= " ORDER BY p.created_at DESC"; $stmt = $pdo->prepare($sql); @@ -141,10 +302,68 @@ try { foreach ($pedidos as $pedido) { [$provincia, $distrito] = splitProvinciaDistrito($pedido['codigo_rastreo'] ?? ''); - $cantidad = (int)($pedido['cantidad'] ?? 0); + + $cantidad_total = parseTotalQuantity($pedido['cantidad'] ?? 0); $total = (float)($pedido['monto_total'] ?? 0); - $precio = $cantidad > 0 ? round($total / $cantidad, 2) : 0; - $ean = getEansForProducts($pdo, extractProductNames($pedido)); + $unit_price_rounded = $cantidad_total > 0 ? round($total / $cantidad_total, 2) : 0; + + $eanOut = ''; + $cantidadOut = $cantidad_total; + $precioOut = $unit_price_rounded; + + $details = extractProductDetailsWithQuantities($pedido); + + $eanSegments = []; + $qtySegments = []; + $priceSegments = []; + + if (!empty($details)) { + $names = []; + foreach ($details as $d) { + if (!empty($d['name'])) { + $names[] = (string)$d['name']; + } + } + + $eanMap = getEanMap($pdo, $names); + + foreach ($details as $d) { + $name = (string)($d['name'] ?? ''); + if ($name === '') { + continue; + } + + $qty = (int)($d['qty'] ?? 0); + if ($qty <= 0) { + $qty = 1; + } + + $ean = trim((string)($eanMap[$name] ?? '')); + if ($ean === '') { + continue; + } + + $eanSegments[] = $ean; + $qtySegments[] = $qty; + $priceSegments[] = formatPriceSegment((float)$unit_price_rounded); // unit price repeated + } + } + + // If we have segment data, format with pipes (no spaces) + if (count($eanSegments) > 1) { + $eanOut = implode('|', $eanSegments); + $cantidadOut = implode('|', $qtySegments); + $precioOut = implode('|', $priceSegments); + } elseif (count($eanSegments) === 1) { + $eanOut = $eanSegments[0]; + $cantidadOut = $qtySegments[0] ?? $cantidad_total; + $precioOut = $unit_price_rounded; + } else { + // Fallback (keep old behavior, but with "|" formatting) + $eanOut = getEansForProducts($pdo, extractProductNames($pedido)); + $cantidadOut = $cantidad_total; + $precioOut = $cantidad_total > 0 ? round($total / $cantidad_total, 2) : 0; + } $rows[] = [ (string)($pedido['nombre_completo'] ?? ''), @@ -156,13 +375,27 @@ try { (string)($pedido['direccion_exacta'] ?? ''), (string)($pedido['referencia_domicilio'] ?? ''), (string)($pedido['coordenadas'] ?? ''), - (string)$ean, - $cantidad, - $precio, + (string)$eanOut, + $cantidadOut, + $precioOut, $total ]; } + // Right-align certain columns (Codigo EAN, Cantidad, Precio, Total) in the exported Excel + $rightAlignedCols = [9, 10, 11, 12]; + foreach ($rows as $rIdx => &$row) { + foreach ($rightAlignedCols as $cIdx) { + if (!array_key_exists($cIdx, $row)) { + continue; + } + if (is_string($row[$cIdx]) && strpos($row[$cIdx], '') !== false) { + continue; + } + $row[$cIdx] = '' . (string)$row[$cIdx] . ''; + } + } + unset($row); $filename = 'ruta_contraentrega_' . date('Y-m-d_H-i') . '.xlsx'; SimpleXLSXGen::fromArray($rows, 'Ruta Contraentrega')->downloadAs($filename); exit; diff --git a/pedidos.php b/pedidos.php index 4afffd78..c219cc69 100644 --- a/pedidos.php +++ b/pedidos.php @@ -165,6 +165,8 @@ include 'layout_header.php'; Generar Todos los Rótulos Descargar Todos los Rotulados + + Descargar Shalom Terrestre Descargar Shalom Aéreo diff --git a/pedidos_contraentrega.php b/pedidos_contraentrega.php index dd94b8dd..c64c5ead 100644 --- a/pedidos_contraentrega.php +++ b/pedidos_contraentrega.php @@ -30,6 +30,7 @@ $pedido = [ 'fecha_recojo' => '', 'asesor_id' => $user_id, // Default to current user 'notas' => '', + 'nota_adicional' => '', ]; $provinciasPorDepartamentoContraentrega = contraentregaProvinciasPorDepartamento(); @@ -347,6 +348,11 @@ include 'layout_header.php'; +
+ + +
+ Cancelar diff --git a/ruta_contraentrega.php b/ruta_contraentrega.php index f084221e..937db409 100644 --- a/ruta_contraentrega.php +++ b/ruta_contraentrega.php @@ -102,6 +102,35 @@ $selected_month = $_GET['mes'] ?? ''; $selected_year = $_GET['año'] ?? ''; $search_query = $_GET['q'] ?? ''; +$period = $_GET['period'] ?? ''; +$date_condition = ''; +$label_period = ''; + +if (!empty($period)) { + switch ($period) { + case 'today': + $date_condition = "DATE(p.created_at) = CURDATE()"; + $label_period = "Hoy"; + break; + case 'yesterday': + $date_condition = "DATE(p.created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)"; + $label_period = "Ayer"; + break; + case '7': + $date_condition = "DATE(p.created_at) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)"; + $label_period = "Últimos 7 días"; + break; + case '15': + $date_condition = "DATE(p.created_at) >= DATE_SUB(CURDATE(), INTERVAL 15 DAY)"; + $label_period = "Últimos 15 días"; + break; + case 'month': + $date_condition = "MONTH(p.created_at) = MONTH(CURDATE()) AND YEAR(p.created_at) = YEAR(CURDATE())"; + $label_period = "Este Mes"; + break; + } +} + $sql = "SELECT p.*, u.nombre_asesor as asesor_nombre FROM pedidos p LEFT JOIN users u ON p.asesor_id = u.id WHERE p.estado IN ('RUTA_CONTRAENTREGA', 'PENDIENTE', 'NO CONTESTO, VOLVER A LLAMAR', 'NO CONTESTO, DEVOLVER LLAMADA', 'CANCELADO', 'REPROGRAMADO', 'ENTREGA EXITOSA', 'RETORNADO')"; $params = []; @@ -117,13 +146,17 @@ if (!empty($search_query)) { $params[] = "%$search_query%"; } -if (!empty($selected_month)) { - $sql .= " AND MONTH(p.created_at) = ?"; - $params[] = $selected_month; -} -if (!empty($selected_year)) { - $sql .= " AND YEAR(p.created_at) = ?"; - $params[] = $selected_year; +if (!empty($date_condition)) { + $sql .= " AND $date_condition"; +} else { + if (!empty($selected_month)) { + $sql .= " AND MONTH(p.created_at) = ?"; + $params[] = $selected_month; + } + if (!empty($selected_year)) { + $sql .= " AND YEAR(p.created_at) = ?"; + $params[] = $selected_year; + } } $sql .= " ORDER BY p.created_at DESC"; @@ -237,7 +270,20 @@ include 'layout_header.php'; -
+ +
+
+ Fecha: + Hoy + Ayer + 7 Días + 15 Días + Este Mes +
+
+
Limpiar @@ -248,15 +294,13 @@ include 'layout_header.php'; $search_query, - 'mes' => $selected_month, - 'año' => $selected_year, ], static function ($value) { return $value !== '' && $value !== null; }); $excelUrl = 'download_ruta_contraentrega.php' . (!empty($excelParams) ? '?' . http_build_query($excelParams) : ''); ?> - Descargar Excel + Descargar Excel (Mañana)
@@ -283,6 +327,7 @@ include 'layout_header.php'; Distrito Coordenadas Producto + Dedicatoria / Obs. Cant. Total @@ -354,6 +399,16 @@ include 'layout_header.php'; + + + + diff --git a/save_pedido_contraentrega.php b/save_pedido_contraentrega.php index 2adb250c..eec945a3 100644 --- a/save_pedido_contraentrega.php +++ b/save_pedido_contraentrega.php @@ -100,6 +100,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $numero_operacion = trim($_POST['numero_operacion'] ?? ''); $banco = trim($_POST['banco'] ?? ''); $notas = trim($_POST['notas'] ?? ''); + $nota_adicional = trim($_POST['nota_adicional'] ?? ''); + + // Campo combinado del formulario: (Dedicatoria o grabado) + Observación del cliente + // Guardamos en ambas columnas para mantener compatibilidad con el resto del sistema. + $descargo = $nota_adicional; // Normalizar Paquete: el selector "Seleccionar" llega como cadena vacía, // pero la columna ENUM permite NULL y no permite ''. @@ -175,6 +180,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'banco' => $banco, 'monto_debe' => $monto_debe, 'notas' => $notas, + 'nota_adicional' => $nota_adicional, + 'descargo' => $descargo, 'seguimiento' => $seguimiento, 'fecha_entrega' => $fecha_entrega, 'tipo_paquete' => $tipo_paquete, @@ -204,6 +211,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { "banco = :banco", "monto_debe = :monto_debe", "notas = :notas", + "nota_adicional = :nota_adicional", + "descargo = :descargo", "seguimiento = :seguimiento", "fecha_entrega = :fecha_entrega", "estado = :estado", @@ -227,8 +236,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $params['asesor_id'] = $_SESSION['user_id']; $params['estado'] = $_POST['estado'] ?? 'RUTA_CONTRAENTREGA'; - $columns_sql = "dni_cliente, nombre_completo, celular, agencia, sede_envio, direccion_exacta, referencia_domicilio, coordenadas, codigo_rastreo, codigo_tracking, producto, cantidad, monto_total, monto_adelantado, numero_operacion, banco, monto_debe, asesor_id, notas, estado, seguimiento, fecha_entrega, tipo_paquete"; - $values_sql = ":dni_cliente, :nombre_completo, :celular, :agencia, :sede_envio, :direccion_exacta, :referencia_domicilio, :coordenadas, :codigo_rastreo, :codigo_tracking, :producto, :cantidad, :monto_total, :monto_adelantado, :numero_operacion, :banco, :monto_debe, :asesor_id, :notas, :estado, :seguimiento, :fecha_entrega, :tipo_paquete"; + $columns_sql = "dni_cliente, nombre_completo, celular, agencia, sede_envio, direccion_exacta, referencia_domicilio, coordenadas, codigo_rastreo, codigo_tracking, producto, cantidad, monto_total, monto_adelantado, numero_operacion, banco, monto_debe, asesor_id, notas, nota_adicional, descargo, estado, seguimiento, fecha_entrega, tipo_paquete"; + $values_sql = ":dni_cliente, :nombre_completo, :celular, :agencia, :sede_envio, :direccion_exacta, :referencia_domicilio, :coordenadas, :codigo_rastreo, :codigo_tracking, :producto, :cantidad, :monto_total, :monto_adelantado, :numero_operacion, :banco, :monto_debe, :asesor_id, :notas, :nota_adicional, :descargo, :estado, :seguimiento, :fecha_entrega, :tipo_paquete"; $sql = "INSERT INTO pedidos ($columns_sql) VALUES ($values_sql)"; $stmt = $pdo->prepare($sql);