diff --git a/call_center_pro.php b/call_center_pro.php index b1e48c49..a010cdec 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -168,6 +168,8 @@ if (!isset($availableStores[$storeKey])) { $storeKey = 'flower'; } $storeConfig = $availableStores[$storeKey]; +$storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey)); +$usesIncrementalSync = !empty($storeConfig['incremental_sync']); $errorMessage = null; $noticeMessage = null; @@ -177,8 +179,7 @@ $visibleOrders = []; $modalsHtml = []; $totalRows = 0; $agregadosCount = 0; -$tuaniLastProcessedRow = null; -$loadLimit = $storeKey === 'otra_tienda' ? 0 : 100; +$lastProcessedRow = null; $startRow = (int) ($storeConfig['startRow'] ?? 5552); $catalogoProductos = []; $stats = [ @@ -205,10 +206,10 @@ try { $catalogoProductos = []; } - if ($storeKey === 'otra_tienda') { + if ($usesIncrementalSync) { $syncInfo = drive_test_sync_orders_incremental($pdo, $storeKey, 0); $totalRows = (int) ($syncInfo['drive_rows_total'] ?? 0); - $tuaniLastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0); + $lastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0); $startRow = (int) ($syncInfo['next_start_row'] ?? $startRow); $orders = drive_test_fetch_orders_from_db($pdo, $storeKey); @@ -220,7 +221,7 @@ try { } } } else { - $preview = drive_test_fetch_orders($loadLimit, $startRow, $storeKey); + $preview = drive_test_fetch_orders(100, $startRow, $storeKey); $totalRows = (int) ($preview['total_rows'] ?? 0); $orders = $preview['orders'] ?? []; @@ -398,6 +399,18 @@ try { return $aPendienteLogistica ? -1 : 1; } + $aAssigned = cc_test_parse_datetime($a['assigned_at'] ?? null); + $bAssigned = cc_test_parse_datetime($b['assigned_at'] ?? null); + if ($aAssigned && $bAssigned && $aAssigned != $bAssigned) { + return $bAssigned <=> $aAssigned; + } + if ($aAssigned && !$bAssigned) { + return -1; + } + if (!$aAssigned && $bAssigned) { + return 1; + } + $aDue = cc_test_parse_datetime($a["proxima_llamada_at"] ?? null); $bDue = cc_test_parse_datetime($b["proxima_llamada_at"] ?? null); if ($aDue && $bDue && $aDue != $bDue) { @@ -455,7 +468,7 @@ require_once 'layout_header.php';
Drive detectado: filas Agregados: - TUANI: distribución desde fila · último procesado Extracción: desde fila + : distribución desde fila · último procesado Extracción: desde fila Auto actualización: cada 10 min Ver vista previa Drive
diff --git a/download_ruta_contraentrega.php b/download_ruta_contraentrega.php index 44aec406..75b77bc7 100644 --- a/download_ruta_contraentrega.php +++ b/download_ruta_contraentrega.php @@ -274,9 +274,15 @@ try { $user_id = $_SESSION['user_id']; $user_role = $_SESSION['user_role'] ?? 'Asesor'; +$is_admin = in_array($user_role, ['Administrador', 'admin'], true); $search_query = trim($_GET['q'] ?? ''); + if (!$is_admin) { + http_response_code(403); + exit('Acceso denegado.'); + } + // Exportar solo los pedidos con fecha de entrega "mañana" $date_condition = "DATE(p.fecha_entrega) = DATE_ADD(CURDATE(), INTERVAL 1 DAY)"; diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index e5c79877..66faab62 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -169,6 +169,8 @@ if (!isset($availableStores[$storeKey])) { $storeKey = 'flower'; } $storeConfig = $availableStores[$storeKey]; +$storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey)); +$usesIncrementalSync = !empty($storeConfig['incremental_sync']); $errorMessage = null; $noticeMessage = null; @@ -179,8 +181,7 @@ $visibleOrders = []; $modalsHtml = []; $totalRows = 0; $agregadosCount = 0; -$tuaniLastProcessedRow = null; -$loadLimit = $storeKey === 'otra_tienda' ? 0 : 100; +$lastProcessedRow = null; $cupoObjetivoPorAsesora = 10; $advisorOpenCounts = []; @@ -233,10 +234,10 @@ try { } } - if ($storeKey === 'otra_tienda') { + if ($usesIncrementalSync) { $syncInfo = drive_test_sync_orders_incremental($pdo, $storeKey, 0); $totalRows = (int) ($syncInfo['drive_rows_total'] ?? 0); - $tuaniLastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0); + $lastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0); $startRow = (int) ($syncInfo['next_start_row'] ?? $startRow); $orders = drive_test_fetch_orders_from_db($pdo, $storeKey); @@ -248,7 +249,7 @@ try { } } } else { - $preview = drive_test_fetch_orders($loadLimit, $startRow, $storeKey); + $preview = drive_test_fetch_orders(100, $startRow, $storeKey); $totalRows = (int) ($preview['total_rows'] ?? 0); $orders = $preview['orders'] ?? []; @@ -311,6 +312,108 @@ try { })); } + if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'bulk_assign_assessor') { + if (empty($assessors)) { + throw new RuntimeException('No hay asesoras configuradas en el sistema.'); + } + + $targetValue = trim((string) ($_POST['target_assessor'] ?? '')); + $targetKey = trim(mb_strtoupper($targetValue)); + $targetKey = preg_replace('/\s+/', ' ', $targetKey) ?? $targetKey; + $selectedAssessor = $targetKey !== '' ? ($assessors[$targetKey] ?? null) : null; + if (!$selectedAssessor) { + throw new RuntimeException('Selecciona la asesora para la asignación rápida.'); + } + + $targetUserId = (int) ($selectedAssessor['id'] ?? 0); + if ($targetUserId <= 0) { + throw new RuntimeException('La asesora seleccionada no es válida.'); + } + + $availableSourceKeys = array_fill_keys(array_column($orders, 'source_key'), true); + $sourceKeys = []; + foreach ((array) ($_POST['source_keys'] ?? []) as $sourceKeyRaw) { + $sourceKey = trim((string) $sourceKeyRaw); + if ($sourceKey === '' || !preg_match('/^[a-f0-9]{40}$/', $sourceKey)) { + continue; + } + + if (!isset($availableSourceKeys[$sourceKey])) { + continue; + } + + $sourceKeys[$sourceKey] = $sourceKey; + } + + if (empty($sourceKeys)) { + throw new RuntimeException('Selecciona al menos un pedido válido para la asignación rápida.'); + } + + $placeholders = implode(',', array_fill(0, count($sourceKeys), '?')); + $stmtCurrent = $pdo->prepare("SELECT source_key, user_id FROM callcenter_test_tracking WHERE source_key IN ($placeholders)"); + $stmtCurrent->execute(array_values($sourceKeys)); + + $currentAssignments = []; + while ($row = $stmtCurrent->fetch(PDO::FETCH_ASSOC)) { + $currentAssignments[(string) ($row['source_key'] ?? '')] = isset($row['user_id']) && (int) $row['user_id'] > 0 + ? (int) $row['user_id'] + : null; + } + + $assignedCount = 0; + $sameAssessorCount = 0; + $lockedCount = 0; + + $pdo->beginTransaction(); + try { + foreach ($sourceKeys as $sourceKey) { + $currentUserId = $currentAssignments[$sourceKey] ?? null; + + if ($currentUserId !== null && (int) $currentUserId !== $targetUserId) { + $lockedCount++; + continue; + } + + if ($currentUserId !== null && (int) $currentUserId === $targetUserId) { + $sameAssessorCount++; + continue; + } + + cc_test_upsert_assignee($pdo, $sourceKey, $targetUserId); + $assignedCount++; + } + + $pdo->commit(); + } catch (Throwable $exception) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + + throw $exception; + } + + $messageParts = []; + if ($assignedCount > 0) { + $messageParts[] = 'Asignación rápida lista: ' . $assignedCount . ' pedido' . ($assignedCount === 1 ? '' : 's') . ' asignado' . ($assignedCount === 1 ? '' : 's') . ' a ' . $selectedAssessor['label'] . '.'; + } + if ($sameAssessorCount > 0) { + $messageParts[] = $sameAssessorCount . ' ya estaba' . ($sameAssessorCount === 1 ? '' : 'n') . ' con esa misma asesora.'; + } + if ($lockedCount > 0) { + $messageParts[] = $lockedCount . ' ya estaba' . ($lockedCount === 1 ? '' : 'n') . ' asignado' . ($lockedCount === 1 ? '' : 's') . ' a otra asesora y se dejó igual.'; + } + + $noticeMessage = !empty($messageParts) + ? implode(' ', $messageParts) + : 'No hubo cambios en la asignación rápida.'; + + $tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key')); + $orders = drive_test_merge_tracking($orders, $tracking); + $orders = array_values(array_filter($orders, static function (array $order): bool { + return empty($order['eliminado']); + })); + } + if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'auto_assign_cupo') { $openStates = cc_test_open_states(); @@ -671,6 +774,70 @@ try { require_once 'layout_header.php'; ?> + +
@@ -692,7 +859,7 @@ require_once 'layout_header.php';
Drive detectado: filas Agregados: - TUANI: distribución desde fila · último procesado Extracción: desde fila + : distribución desde fila · último procesado Extracción: desde fila Auto actualización: cada 10 min Ver vista previa Drive
@@ -882,22 +1049,45 @@ require_once 'layout_header.php'; pedidos en esta bandeja
+
+
+ +
+
+ 0 seleccionados + + +
+
Usa el botón de la izquierda de cada pedido y asigna varios de una sola vez.
+
+ + +
+
+
Si un pedido ya está tomado por otra asesora, el sistema lo deja igual y te avisa.
+
- +
+ + - - @@ -950,6 +1140,123 @@ require_once 'layout_header.php'; ob_start(); ?> + + - @@ -2238,6 +2445,142 @@ if (airDroidCopyButton) { }); } +const bulkAssignForm = document.getElementById('bulkAssignForm'); +const bulkAssignSourceKeys = document.getElementById('bulkAssignSourceKeys'); +const bulkSelectedCount = document.getElementById('bulkSelectedCount'); +const bulkAssignSubmit = document.getElementById('bulkAssignSubmit'); +const toggleSelectAllOrders = document.getElementById('toggleSelectAllOrders'); +const clearSelectedOrders = document.getElementById('clearSelectedOrders'); +const bulkOrderCheckboxes = Array.from(document.querySelectorAll('.js-bulk-order-checkbox')); +const quickSelectButtons = Array.from(document.querySelectorAll('.js-quick-select-btn')); +const bulkAssessorSelect = bulkAssignForm ? bulkAssignForm.querySelector('select[name="target_assessor"]') : null; + +function getSelectedBulkOrderCheckboxes() { + return bulkOrderCheckboxes.filter(function (checkbox) { + return checkbox.checked; + }); +} + +function setQuickSelectButtonState(button, selected) { + if (!button) { + return; + } + + button.classList.toggle('btn-primary', selected); + button.classList.toggle('btn-outline-primary', !selected); + button.setAttribute('aria-pressed', selected ? 'true' : 'false'); + button.setAttribute('title', selected ? 'Quitar de la selección rápida' : 'Seleccionar este pedido para asignación rápida'); + button.innerHTML = selected ? '' : ''; +} + +function syncBulkAssignSelectionUi() { + const selectedCheckboxes = getSelectedBulkOrderCheckboxes(); + + if (bulkSelectedCount) { + bulkSelectedCount.textContent = String(selectedCheckboxes.length); + } + + if (bulkAssignSourceKeys) { + bulkAssignSourceKeys.innerHTML = ''; + selectedCheckboxes.forEach(function (checkbox) { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'source_keys[]'; + input.value = checkbox.value; + bulkAssignSourceKeys.appendChild(input); + }); + } + + bulkOrderCheckboxes.forEach(function (checkbox) { + const row = checkbox.closest('tr'); + const button = checkbox.parentElement ? checkbox.parentElement.querySelector('.js-quick-select-btn') : null; + if (row) { + row.classList.toggle('is-selected', checkbox.checked); + } + setQuickSelectButtonState(button, checkbox.checked); + }); + + const anySelected = selectedCheckboxes.length > 0; + const canSubmit = anySelected && !!bulkAssessorSelect && !bulkAssessorSelect.disabled && bulkAssessorSelect.value !== ''; + if (bulkAssignSubmit) { + bulkAssignSubmit.disabled = !canSubmit; + } + if (clearSelectedOrders) { + clearSelectedOrders.disabled = !anySelected; + } + if (toggleSelectAllOrders) { + const allSelected = bulkOrderCheckboxes.length > 0 && selectedCheckboxes.length === bulkOrderCheckboxes.length; + toggleSelectAllOrders.textContent = allSelected ? 'Desmarcar visibles' : 'Marcar visibles'; + toggleSelectAllOrders.disabled = bulkOrderCheckboxes.length === 0; + } +} + +quickSelectButtons.forEach(function (button) { + button.addEventListener('click', function () { + const sourceKey = this.dataset.sourceKey || ''; + const checkbox = document.querySelector('.js-bulk-order-checkbox[data-source-key="' + sourceKey + '"]'); + if (!checkbox) { + return; + } + + checkbox.checked = !checkbox.checked; + syncBulkAssignSelectionUi(); + }); +}); + +bulkOrderCheckboxes.forEach(function (checkbox) { + checkbox.addEventListener('change', syncBulkAssignSelectionUi); +}); + +if (bulkAssessorSelect) { + bulkAssessorSelect.addEventListener('change', syncBulkAssignSelectionUi); +} + +if (toggleSelectAllOrders) { + toggleSelectAllOrders.addEventListener('click', function () { + const shouldSelectAll = bulkOrderCheckboxes.some(function (checkbox) { + return !checkbox.checked; + }); + + bulkOrderCheckboxes.forEach(function (checkbox) { + checkbox.checked = shouldSelectAll; + }); + + syncBulkAssignSelectionUi(); + }); +} + +if (clearSelectedOrders) { + clearSelectedOrders.addEventListener('click', function () { + bulkOrderCheckboxes.forEach(function (checkbox) { + checkbox.checked = false; + }); + + syncBulkAssignSelectionUi(); + }); +} + +if (bulkAssignForm) { + bulkAssignForm.addEventListener('submit', function (event) { + const selectedCheckboxes = getSelectedBulkOrderCheckboxes(); + const assessorSelect = this.querySelector('select[name="target_assessor"]'); + + if (selectedCheckboxes.length === 0) { + event.preventDefault(); + alert('Primero selecciona al menos un pedido.'); + return; + } + + if (!assessorSelect || !assessorSelect.value) { + event.preventDefault(); + alert('Elige la asesora para hacer la asignación rápida.'); + return; + } + }); + + syncBulkAssignSelectionUi(); +} + window.setInterval(function () { window.location.reload(); }, 600000); diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index 920f8ff0..76b98629 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -30,6 +30,7 @@ function cc_test_ensure_tracking_table(PDO $pdo): void `estado` VARCHAR(40) NOT NULL DEFAULT 'POR LLAMAR', `nota_seguimiento` TEXT NULL, `user_id` INT NULL, + `assigned_at` DATETIME NULL, `direccion` TEXT NULL, `referencia` TEXT NULL, `agencia` VARCHAR(80) NULL, @@ -78,6 +79,7 @@ function cc_test_ensure_tracking_table(PDO $pdo): void KEY `idx_callcenter_test_tracking_proxima` (`proxima_llamada_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); + cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'assigned_at', 'DATETIME NULL AFTER `user_id`'); cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'direccion', 'TEXT NULL AFTER `user_id`'); cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'referencia', 'TEXT NULL AFTER `direccion`'); cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'agencia', 'VARCHAR(80) NULL AFTER `referencia`'); @@ -119,6 +121,11 @@ function cc_test_ensure_tracking_table(PDO $pdo): void cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'pedido_rotulado_subido_por', 'INT NULL AFTER `pedido_rotulado_subido_at`'); cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'ultima_gestion_at', 'DATETIME NULL AFTER `ruta_contraentrega_subido_por`'); + $stmtAssignedBackfill = $pdo->query("SELECT id FROM callcenter_test_tracking WHERE assigned_at IS NULL AND user_id IS NOT NULL LIMIT 1"); + if ($stmtAssignedBackfill && $stmtAssignedBackfill->fetchColumn()) { + $pdo->exec("UPDATE callcenter_test_tracking SET assigned_at = created_at, updated_at = updated_at WHERE assigned_at IS NULL AND user_id IS NOT NULL"); + } + $checked = true; } @@ -215,18 +222,27 @@ function cc_test_upsert_assignee(PDO $pdo, string $sourceKey, ?int $userId): voi { cc_test_ensure_tracking_table($pdo); - $stmt = $pdo->prepare("INSERT INTO callcenter_test_tracking (source_key, user_id, ultima_gestion_at) - VALUES (:source_key, :user_id, CURRENT_TIMESTAMP) + $assignedAt = $userId === null ? null : (new DateTimeImmutable('now'))->format('Y-m-d H:i:s'); + + $stmt = $pdo->prepare("INSERT INTO callcenter_test_tracking (source_key, user_id, assigned_at, ultima_gestion_at) + VALUES (:source_key, :user_id, :assigned_at, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), + assigned_at = CASE + WHEN VALUES(user_id) IS NULL THEN NULL + WHEN assigned_at IS NULL OR user_id IS NULL OR user_id <> VALUES(user_id) THEN VALUES(assigned_at) + ELSE assigned_at + END, ultima_gestion_at = VALUES(ultima_gestion_at), updated_at = CURRENT_TIMESTAMP"); $stmt->bindValue(':source_key', $sourceKey, PDO::PARAM_STR); if ($userId === null) { $stmt->bindValue(':user_id', null, PDO::PARAM_NULL); + $stmt->bindValue(':assigned_at', null, PDO::PARAM_NULL); } else { $stmt->bindValue(':user_id', $userId, PDO::PARAM_INT); + $stmt->bindValue(':assigned_at', $assignedAt, PDO::PARAM_STR); } $stmt->execute(); diff --git a/includes/drive_test_orders.php b/includes/drive_test_orders.php index c6982bfc..348d9a8b 100644 --- a/includes/drive_test_orders.php +++ b/includes/drive_test_orders.php @@ -18,8 +18,10 @@ function drive_test_available_stores(): array 'label' => 'Flower', 'spreadsheet_id' => '1SSmQuR9quxeQbMKNMDkRe8-n1gU7WuEfsFaJ3WKFO-c', 'sheet_gid' => null, - 'startRow' => 5552, - 'enforce_db_start_row' => false, + 'startRow' => 7796, // Pedido #37431 + 'min_codigo_number' => 37431, + 'enforce_db_start_row' => true, + 'incremental_sync' => true, ], // Segunda tienda (nuevo link que pasaste). 'otra_tienda' => [ @@ -28,6 +30,7 @@ function drive_test_available_stores(): array 'sheet_gid' => 1523126328, 'startRow' => 6804, 'enforce_db_start_row' => true, + 'incremental_sync' => true, ], ]; } @@ -94,6 +97,16 @@ function drive_test_get_cell(array $row, array $indexes, array $aliases, string return $default; } +function drive_test_extract_codigo_number(?string $codigo): ?int +{ + $digits = preg_replace('/\D+/', '', trim((string) $codigo)); + if ($digits === '') { + return null; + } + + return (int) $digits; +} + function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $storeKey = 'flower'): array { $storeConfig = drive_test_get_store_config($storeKey); @@ -157,6 +170,7 @@ function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $ $previewRows = array_reverse($previewRows, true); $orders = []; + $minCodigoNumber = isset($storeConfig['min_codigo_number']) ? (int) ($storeConfig['min_codigo_number'] ?? 0) : 0; foreach ($previewRows as $rowIndex => $row) { $codigo = trim((string) ($row[0] ?? '')); @@ -177,6 +191,11 @@ function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $ $referencia = drive_test_get_cell($row, $headerIndexes, ['REFERENCIA']); $distrito = drive_test_get_cell($row, $headerIndexes, ['DISTRITO']); $sourceRow = (int) $rowIndex + 2; + $codigoNumber = drive_test_extract_codigo_number($codigo); + + if ($minCodigoNumber > 0 && ($codigoNumber === null || $codigoNumber < $minCodigoNumber)) { + continue; + } // Importante: para 'flower' mantenemos EXACTAMENTE el algoritmo anterior // para no romper el historial/tracking ya existente. @@ -229,7 +248,7 @@ function drive_test_fetch_tracking(PDO $pdo, array $sourceKeys): array cc_test_ensure_tracking_table($pdo); $placeholders = implode(',', array_fill(0, count($sourceKeys), '?')); - $stmt = $pdo->prepare("SELECT source_key, estado, nota_seguimiento, user_id, direccion, referencia, agencia, sede_agencia, sede, ciudad, distrito, dni, numero_cuenta_sede_id, numero_cuenta_dni, observaciones, coordenadas, producto, cantidad, precio, monto_adelantado, confirmacion_producto, confirmacion_cantidad, confirmacion_precio, confirmacion_producto_extra, confirmacion_cantidad_extra, confirmacion_precio_extra, proxima_llamada_at, fecha_entrega_programada, numero_cuenta_enviado_at, promo_final_evidencia_path, promo_final_evidencia_subido_at, promo_final_evidencia_subido_por, cancelado_evidencia_path, cancelado_evidencia_subido_at, cancelado_evidencia_subido_por, eliminado_at, eliminado_por, ruta_contraentrega_pedido_id, ruta_contraentrega_subido_at, ruta_contraentrega_subido_por, pedido_rotulado_pedido_id, pedido_rotulado_subido_at, pedido_rotulado_subido_por, ultima_gestion_at, updated_at FROM callcenter_test_tracking WHERE source_key IN ($placeholders)"); + $stmt = $pdo->prepare("SELECT source_key, estado, nota_seguimiento, user_id, assigned_at, direccion, referencia, agencia, sede_agencia, sede, ciudad, distrito, dni, numero_cuenta_sede_id, numero_cuenta_dni, observaciones, coordenadas, producto, cantidad, precio, monto_adelantado, confirmacion_producto, confirmacion_cantidad, confirmacion_precio, confirmacion_producto_extra, confirmacion_cantidad_extra, confirmacion_precio_extra, proxima_llamada_at, fecha_entrega_programada, numero_cuenta_enviado_at, promo_final_evidencia_path, promo_final_evidencia_subido_at, promo_final_evidencia_subido_por, cancelado_evidencia_path, cancelado_evidencia_subido_at, cancelado_evidencia_subido_por, eliminado_at, eliminado_por, ruta_contraentrega_pedido_id, ruta_contraentrega_subido_at, ruta_contraentrega_subido_por, pedido_rotulado_pedido_id, pedido_rotulado_subido_at, pedido_rotulado_subido_por, ultima_gestion_at, updated_at FROM callcenter_test_tracking WHERE source_key IN ($placeholders)"); $stmt->execute($sourceKeys); $tracking = []; @@ -251,6 +270,7 @@ function drive_test_merge_tracking(array $orders, array $tracking): array $userId = array_key_exists('user_id', (array) $current) ? $current['user_id'] : null; $order['user_id'] = $userId !== null ? ((int) $userId > 0 ? (int) $userId : null) : null; $order['seguimiento_actualizado'] = $current['updated_at'] ?? null; + $order['assigned_at'] = $current['assigned_at'] ?? null; $order['proxima_llamada_at'] = $current['proxima_llamada_at'] ?? null; $order['fecha_entrega_programada'] = $current['fecha_entrega_programada'] ?? null; $order['numero_cuenta_enviado_at'] = $current['numero_cuenta_enviado_at'] ?? null; @@ -628,12 +648,21 @@ function drive_test_fetch_orders_from_db(PDO $pdo, string $storeKey, ?bool $only $stmt->execute($params); $orders = []; + $minCodigoNumber = isset($storeConfig['min_codigo_number']) ? (int) ($storeConfig['min_codigo_number'] ?? 0) : 0; foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $isAgregado = (int) ($row['is_agregado'] ?? 0); + $codigo = (string) ($row['codigo'] ?? ''); + $codigoNumber = drive_test_extract_codigo_number($codigo); + + if ($minCodigoNumber > 0 && $isAgregado !== 1 && ($codigoNumber === null || $codigoNumber < $minCodigoNumber)) { + continue; + } + $orders[] = [ 'id' => (int) ($row['id'] ?? 0), - 'is_agregado' => (int) ($row['is_agregado'] ?? 0), + 'is_agregado' => $isAgregado, 'source_key' => (string) ($row['source_key'] ?? ''), - 'codigo' => (string) ($row['codigo'] ?? ''), + 'codigo' => $codigo, 'import_id' => (string) ($row['import_id'] ?? ''), 'source_row' => isset($row['source_row']) && (int) $row['source_row'] > 0 ? (int) $row['source_row'] : null, 'drive_imported_at' => (string) ($row['drive_imported_at'] ?? ''), diff --git a/ruta_contraentrega.php b/ruta_contraentrega.php index 68e56896..65522d09 100644 --- a/ruta_contraentrega.php +++ b/ruta_contraentrega.php @@ -88,6 +88,7 @@ ensureTipoPaqueteEnumDefinition($pdo); $user_id = $_SESSION['user_id']; $user_role = $_SESSION['user_role'] ?? 'Asesor'; +$is_admin = in_array($user_role, ['Administrador', 'admin'], true); // Fetch years for the filter $years_query = "SELECT DISTINCT YEAR(created_at) as year FROM pedidos"; @@ -337,17 +338,19 @@ include 'layout_header.php'; AGREGAR PEDIDOS CONTRAENTREGA - $search_query, - ], static function ($value) { - return $value !== '' && $value !== null; - }); - $excelUrl = 'download_ruta_contraentrega.php' . (!empty($excelParams) ? '?' . http_build_query($excelParams) : ''); - ?> - - Descargar Excel (Mañana) - + + $search_query, + ], static function ($value) { + return $value !== '' && $value !== null; + }); + $excelUrl = 'download_ruta_contraentrega.php' . (!empty($excelParams) ? '?' . http_build_query($excelParams) : ''); + ?> + + Descargar Excel (Mañana) + +
Sel.Asignación / acciones N° Pedido Cliente Ubicación editable Pedido GestiónAcciones
+ No hay pedidos en esta bandeja por ahora.
+
+ + +
+
+ + 0 ? (int) $orderUserId : null) : null; + $unassigned = $orderUserIdNorm === null; + $assignedAssessorKey = null; + if (!$unassigned) { + foreach ($assessors as $assessorKey => $assessor) { + if ((int) ($assessor['id'] ?? 0) === (int) $orderUserIdNorm) { + $assignedAssessorKey = (string) $assessorKey; + break; + } + } + } + + $assignFormClass = 'border rounded-3 p-1 text-start shadow-sm cc-callcenter-assign-form'; + if ($unassigned) { + $assignFormClass .= ' bg-light'; + } elseif ($assignedAssessorKey === 'KARINA') { + $assignFormClass .= ' bg-primary-subtle border-primary'; + } elseif ($assignedAssessorKey === 'ESTEFANYA') { + $assignFormClass .= ' bg-success-subtle border-success'; + } elseif ($assignedAssessorKey === 'CARMEN') { + $assignFormClass .= ' bg-warning-subtle border-warning'; + } else { + $assignFormClass .= ' bg-secondary-subtle border-secondary'; + } + + if ($unassigned) { + $selectedAssessorKeyForSelect = !empty($recommendedAssessorKey) ? (string) $recommendedAssessorKey : ''; + } else { + $selectedAssessorKeyForSelect = $assignedAssessorKey !== null ? (string) $assignedAssessorKey : ''; + } + + $buttonLabel = $unassigned + ? 'Asignar pedido' + : ($assignedAssessorKey !== null ? 'Guardar asignación' : 'Desasignar'); + ?> + +
+ + + +
+
Asignar a asesora
+ + Sin asignar + + KARINA + + ESTEFANYA + + CARMEN + + Asignado + +
+ +
+ + + +
+
+ +
+ + + + + + + + WhatsApp + + + +
+
@@ -1015,106 +1322,6 @@ require_once 'layout_header.php';
Última gestión:
Nota:
-
- - 0 ? (int) $orderUserId : null) : null; - $unassigned = $orderUserIdNorm === null; - $assignedAssessorKey = null; - if (!$unassigned) { - foreach ($assessors as $assessorKey => $assessor) { - if ((int) ($assessor['id'] ?? 0) === (int) $orderUserIdNorm) { - $assignedAssessorKey = (string) $assessorKey; - break; - } - } - } - - $assignFormClass = 'border rounded-3 p-2 text-start shadow-sm'; - if ($unassigned) { - $assignFormClass .= ' bg-light'; - } elseif ($assignedAssessorKey === 'KARINA') { - $assignFormClass .= ' bg-primary-subtle border-primary'; - } elseif ($assignedAssessorKey === 'ESTEFANYA') { - $assignFormClass .= ' bg-success-subtle border-success'; - } elseif ($assignedAssessorKey === 'CARMEN') { - $assignFormClass .= ' bg-warning-subtle border-warning'; - } else { - $assignFormClass .= ' bg-secondary-subtle border-secondary'; - } - - if ($unassigned) { - $selectedAssessorKeyForSelect = !empty($recommendedAssessorKey) ? (string) $recommendedAssessorKey : ''; - } else { - $selectedAssessorKeyForSelect = $assignedAssessorKey !== null ? (string) $assignedAssessorKey : ''; - } - - $buttonLabel = $unassigned - ? 'Asignar pedido' - : ($assignedAssessorKey !== null ? 'Guardar asignación' : 'Desasignar'); - ?> - -
- - - -
-
Asignar a asesora
- - Sin asignar - - KARINA - - ESTEFANYA - - CARMEN - - Asignado - -
- - - - -
- - - - - - - - - WhatsApp - - - -
-