From d1def919d5ad888584b668046bed246081a37610 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 31 Jul 2026 16:44:21 +0000 Subject: [PATCH] Autosave: 20260731-164557 --- call_center_pro.php | 81 ++++++++++++++++++---------- gestiones_callcenter.php | 69 +++++++++++++++--------- includes/callcenter_test_helpers.php | 50 +++++++++++++++++ index.php | 3 ++ layout_header.php | 13 +++-- login.php | 4 ++ 6 files changed, 164 insertions(+), 56 deletions(-) diff --git a/call_center_pro.php b/call_center_pro.php index 5a1cc8e0..070b2fc1 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -179,18 +179,31 @@ function cc_test_find_assessor_key_by_user_id(?int $userId, array $assessors): ? function cc_test_order_label(array $order): string { - if (!empty($order['is_agregado'])) { - return 'AGREGADOS'; - } - $codigo = trim((string) ($order['codigo'] ?? '')); if ($codigo !== '') { return '#' . ltrim($codigo, '#'); } + if (!empty($order['is_agregado'])) { + return 'AGREGADOS'; + } + return 'Sin número'; } +function cc_test_promo_final_badge_info(array $order): ?array +{ + if (empty($order['es_promo_final'])) { + return null; + } + + return [ + 'label' => 'Promo Final', + 'title' => 'Pedido movido a Promo Final', + 'class' => 'bg-success-subtle text-success-emphasis border', + ]; +} + function cc_test_badge_class(string $estado): string { return match (cc_test_normalize_state($estado)) { @@ -422,7 +435,7 @@ $_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key $viewStatesNote = $isTuaniRecuperablesStore ? 'Estados disponibles: Carritos recuperables, Seguimiento, Observados, Confirmados, Recuperados y Todos. Esta bandeja compartida está disponible para cualquier asesora y, cuando una asesora gestiona un carrito recuperable, el sistema lo asigna automáticamente a quien lo trabaja. Si el teléfono ya existe en TUANI principal, se marca en gris como Pedido repetido en tienda solo en esta bandeja.' - : 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. Promo Final se habilita desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; cuando haya sustento pendiente en ese nuevo flujo, la tarjeta de Asignados hoy sigue visible y el celular aparece con candado hasta completarlo. Además, esta bandeja principal incluye reparto intercalado diario: en modo automático reparte pedidos sin asignar entre asesoras activas; en modo manual el supervisor los mueve pedido por pedido. También puedes usar la vista Últimos 3 días hábiles para concentrar el seguimiento reciente. Cuando un pedido del nuevo flujo tiene sustento pendiente, su celular se muestra con candado en cualquier vista donde aparezca hasta completar ese paso; la llamada queda bloqueada hasta completarlo.'; + : 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. Promo Final se habilita desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; desde ese momento el número queda visible para llamar y gestionar, y el sustento solo se usa para mover el pedido a Promo Final. Además, esta bandeja principal incluye reparto intercalado diario: en modo automático reparte pedidos sin asignar entre asesoras activas; en modo manual el supervisor los mueve pedido por pedido. También puedes usar la vista Últimos 3 días hábiles para concentrar el seguimiento reciente.'; $errorMessage = null; $noticeMessage = null; @@ -702,12 +715,8 @@ try { $order['promo_final_evidencia_cargada'] = trim((string) ($order['promo_final_evidencia_path'] ?? '')) !== ''; $order['es_promo_final'] = $order['promo_final_habilitado'] && $order['promo_final_evidencia_cargada']; $order['promo_final_pendiente'] = $order['promo_final_habilitado'] && !$order['promo_final_evidencia_cargada']; - $order['llamada_bloqueada_dia3'] = $followupDayInfo !== null - && (int) ($followupDayInfo['day_number'] ?? 0) >= 3 - && !$order['promo_final_evidencia_cargada']; - $order['llamada_bloqueada_dia3_mensaje'] = $order['llamada_bloqueada_dia3'] - ? 'Celular oculto hasta completar el sustento de Día 3.' - : ''; + $order['llamada_bloqueada_dia3'] = false; + $order['llamada_bloqueada_dia3_mensaje'] = ''; $order['pendiente_logistica_destino'] = cc_test_is_tuani_store_key($storeKey) ? cc_test_pending_logistica_destination($order) : null; $order['pendiente_logistica'] = $order['pendiente_logistica_destino'] !== null; @@ -756,6 +765,8 @@ try { } unset($order); + $pendingPromoFinalCounts = cc_test_followup_pending_promo_final_counts($orders); + $orders = cc_test_mark_recoverables_duplicate_orders($pdo, $orders, $storeKey); $performanceOrders = $orders; @@ -987,15 +998,19 @@ try { $assessorInboxLockedOrders = []; if (!$isAdmin && !$isTuaniRecuperablesStore) { - $assessorInboxLockedOrders = array_values(array_filter($orders, static function (array $order): bool { - return !empty($order['llamada_bloqueada_dia3']); - })); + $currentUserLockKey = $currentUserId > 0 ? 'user:' . $currentUserId : ''; + if ($currentUserLockKey !== '') { + $assessorInboxLockedOrders = array_values(array_filter($orders, static function (array $order) use ($currentUserLockKey): bool { + return cc_test_followup_owner_lock_key($order) === $currentUserLockKey + && !empty($order['promo_final_pendiente']); + })); + } } $assessorInboxLockedCount = count($assessorInboxLockedOrders); $assessorInboxLocked = $assessorInboxLockedCount > 0; $assessorInboxLockedView = $assessorInboxLocked && $view === 'nuevos_hoy'; $assessorInboxLockNotice = $assessorInboxLocked - ? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' con sustento pendiente para Promo Final. En esos casos, el celular aparece con candado y la llamada queda bloqueada hasta completar ese paso.' + ? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' pendiente' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' de Promo Final. Mientras existan pendientes, los pedidos de Día 1 mantienen el número oculto; cuando los muevas todos a Promo Final, el celular volverá a mostrarse.' : ''; $visibleOrdersTotalCount = count($visibleOrders); @@ -1360,7 +1375,7 @@ require_once 'layout_header.php';
-
Con candado en celulares
+
Número oculto en Día 1 hasta completar Promo Final
@@ -1422,7 +1437,7 @@ require_once 'layout_header.php'; pedidos únicos en esta bandeja - Candado en celulares por sustento de Día 3 + Pedidos con sustento pendiente para Promo Final Página de · 200 por página @@ -1434,7 +1449,7 @@ require_once 'layout_header.php';
-
Celulares con candado por sustento de Día 3
+
Pedidos con sustento pendiente para Promo Final
Usa Últimos 3 días hábiles si necesitas completar ese sustento más rápido.
Ir a Últimos 3 días @@ -1476,6 +1491,7 @@ require_once 'layout_header.php'; $modalId = 'modalDriveTest' . $order['source_key']; $badgeClass = cc_test_badge_class($order['estado']); $followupDayInfo = $order['seguimiento_dia_info'] ?? null; + $promoFinalBadge = cc_test_promo_final_badge_info($order); $semaforoCuenta = cc_test_followup_semaforo($order); $historial = cc_test_fetch_historial(db(), $order['source_key']); $logisticaPendienteLabel = !empty($order['pendiente_logistica']) ? cc_test_pending_logistica_label($order) : null; @@ -1514,19 +1530,19 @@ require_once 'layout_header.php'; if ($isTuaniRecuperablesStore && !empty($order['pedido_repetido_en_tienda'])) { $rowBackgroundStyle .= '--cc-row-bg:#CCCCCC;'; } - $phoneLocked = !empty($order['llamada_bloqueada_dia3']); + $phoneLocked = false; $phoneLockMessage = (string) ($order['llamada_bloqueada_dia3_mensaje'] ?? ''); if ($phoneLocked && $phoneLockMessage === '') { $phoneLockMessage = 'Celular oculto mientras completas los pendientes del Día 3.'; } - $phoneHiddenByDay1 = $followupDayInfo !== null - && (int) ($followupDayInfo['day_number'] ?? 0) === 1; + $phoneHiddenByDay1 = cc_test_followup_day1_phone_hidden($order, $pendingPromoFinalCounts); $phoneRowHiddenLabel = 'Número oculto'; - $phoneRowHiddenTitle = 'Número oculto'; + $phoneRowHiddenTitle = 'Número oculto hasta completar Promo Final'; ob_start(); ?> +
DÍA @@ -1565,6 +1581,11 @@ require_once 'layout_header.php';
+ + + + + @@ -1664,6 +1685,7 @@ require_once 'layout_header.php'; type="button" class="btn btn-sm btn-primary cc-callcenter-action-btn" data-source-key="" + data-modal-id="" data-phone-locked="" data-phone-hidden-day1="" data-phone="" @@ -1696,6 +1718,11 @@ require_once 'layout_header.php';
+ + + + + @@ -2789,7 +2816,7 @@ function showAirDroidHelper(details) { if (clientNode) clientNode.textContent = clientName; if (phoneNode) { if (phoneHiddenByDay1) { - phoneNode.innerHTML = 'Número oculto'; + phoneNode.innerHTML = 'Número oculto'; } else { phoneNode.textContent = phone !== '-' ? phone : 'Sin celular'; } @@ -2800,7 +2827,7 @@ function showAirDroidHelper(details) { copyButton.dataset.phone = ''; copyButton.disabled = true; copyButton.textContent = 'Número oculto'; - copyButton.setAttribute('title', 'Número oculto en Día 1'); + copyButton.setAttribute('title', 'Número oculto hasta completar Promo Final'); } else { copyButton.dataset.phone = phone !== '-' ? phone : ''; copyButton.disabled = false; @@ -2824,12 +2851,8 @@ function registrarLlamada(event, trigger) { const phone = normalizarNumeroTelefono(trigger?.dataset?.phone || ''); const orderLabel = trigger?.dataset?.orderLabel || ''; const clientName = trigger?.dataset?.clientName || ''; - const phoneLocked = trigger?.dataset?.phoneLocked === '1'; + const phoneLocked = false; const phoneHiddenByDay1 = trigger?.dataset?.phoneHiddenDay1 === '1'; - if (phoneLocked) { - alert('Este pedido tiene el celular bloqueado hasta completar el sustento de Día 3.'); - return false; - } if (!sourceKey) { alert('No se encontró el pedido para registrar la llamada.'); diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index b4815a20..7ab8ceaa 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -101,18 +101,31 @@ function cc_test_build_url(string $path, array $params = []): string function cc_test_order_label(array $order): string { - if (!empty($order['is_agregado'])) { - return 'AGREGADOS'; - } - $codigo = trim((string) ($order['codigo'] ?? '')); if ($codigo !== '') { return '#' . ltrim($codigo, '#'); } + if (!empty($order['is_agregado'])) { + return 'AGREGADOS'; + } + return 'Sin número'; } +function cc_test_promo_final_badge_info(array $order): ?array +{ + if (empty($order['es_promo_final'])) { + return null; + } + + return [ + 'label' => 'Promo Final', + 'title' => 'Pedido movido a Promo Final', + 'class' => 'bg-success-subtle text-success-emphasis border', + ]; +} + function cc_test_badge_class(string $estado): string { return match (cc_test_normalize_state($estado)) { @@ -330,7 +343,7 @@ $_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key $viewStatesNote = $isTuaniRecuperablesStore ? 'Estados disponibles: Carritos recuperables, Seguimiento, Observados, Confirmados, Recuperados y Todos. Esta bandeja compartida está disponible para cualquier asesora y, cuando una asesora gestiona un carrito recuperable, el sistema lo asigna automáticamente a quien lo trabaja. Si el teléfono ya existe en TUANI principal, se marca en gris como Pedido repetido en tienda solo en esta bandeja.' - : 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. Promo Final se habilita desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; exige imagen de sustento para mover el pedido. En las vistas Todos y Últimos 3 días hábiles, los pedidos del nuevo flujo con sustento pendiente se muestran con candado en el número hasta completar ese paso; la llamada sigue disponible desde el panel.'; + : 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. Promo Final se habilita desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; desde ese momento el número se mantiene visible para llamar y gestionar, y el sustento solo se usa para mover el pedido a Promo Final. En las vistas Todos y Últimos 3 días hábiles, la llamada y la gestión siguen disponibles desde el panel.'; $errorMessage = null; $noticeMessage = null; @@ -897,12 +910,8 @@ try { $order['promo_final_evidencia_cargada'] = trim((string) ($order['promo_final_evidencia_path'] ?? '')) !== ''; $order['es_promo_final'] = $order['promo_final_habilitado'] && $order['promo_final_evidencia_cargada']; $order['promo_final_pendiente'] = $order['promo_final_habilitado'] && !$order['promo_final_evidencia_cargada']; - $order['llamada_bloqueada_dia3'] = $followupDayInfo !== null - && (int) ($followupDayInfo['day_number'] ?? 0) >= 3 - && !$order['promo_final_evidencia_cargada']; - $order['llamada_bloqueada_dia3_mensaje'] = $order['llamada_bloqueada_dia3'] - ? 'Celular oculto hasta completar el sustento de Día 3.' - : ''; + $order['llamada_bloqueada_dia3'] = false; + $order['llamada_bloqueada_dia3_mensaje'] = ''; $order['pendiente_logistica_destino'] = cc_test_is_tuani_store_key($storeKey) ? cc_test_pending_logistica_destination($order) : null; $order['pendiente_logistica'] = $order['pendiente_logistica_destino'] !== null; @@ -957,6 +966,8 @@ try { } unset($order); + $pendingPromoFinalCounts = cc_test_followup_pending_promo_final_counts($orders); + $orders = cc_test_mark_recoverables_duplicate_orders($pdo, $orders, $storeKey); $performanceOrders = $selectedAssessorFilter !== '' @@ -1616,7 +1627,7 @@ require_once 'layout_header.php';
Para: . - Los pedidos cargados se muestran como AGREGADOS y no afectan la secuencia de Drive. + Los pedidos cargados conservan su número original y no afectan la secuencia de Drive.
Descargar plantilla Excel @@ -1931,8 +1942,8 @@ require_once 'layout_header.php'; $modalId = 'modalDriveTest' . $order['source_key']; $badgeClass = cc_test_badge_class($order['estado']); $followupDayInfo = $order['seguimiento_dia_info'] ?? null; - $phoneHiddenByDay1 = $followupDayInfo !== null - && (int) ($followupDayInfo['day_number'] ?? 0) === 1; + $promoFinalBadge = cc_test_promo_final_badge_info($order); + $phoneHiddenByDay1 = cc_test_followup_day1_phone_hidden($order, $pendingPromoFinalCounts); $semaforoCuenta = cc_test_followup_semaforo($order); $historial = cc_test_fetch_historial(db(), $order['source_key']); $logisticaPendienteLabel = !empty($order['pendiente_logistica']) ? cc_test_pending_logistica_label($order) : null; @@ -1971,7 +1982,9 @@ require_once 'layout_header.php'; ?> +
+
-
+
+
+ + + + + +
ID Drive:
@@ -2187,6 +2207,11 @@ require_once 'layout_header.php';
+ + + + + @@ -3326,7 +3351,7 @@ function showAirDroidHelper(details) { if (clientNode) clientNode.textContent = clientName; if (phoneNode) { if (phoneHiddenByDay1) { - phoneNode.innerHTML = 'Número oculto'; + phoneNode.innerHTML = 'Número oculto'; } else { phoneNode.textContent = phone !== '-' ? phone : 'Sin celular'; } @@ -3337,7 +3362,7 @@ function showAirDroidHelper(details) { copyButton.dataset.phone = ''; copyButton.disabled = true; copyButton.textContent = 'Número oculto'; - copyButton.setAttribute('title', 'Número oculto en Día 1'); + copyButton.setAttribute('title', 'Número oculto hasta completar Promo Final'); } else { copyButton.dataset.phone = phone !== '-' ? phone : ''; copyButton.disabled = false; @@ -3361,12 +3386,8 @@ function registrarLlamada(event, trigger) { const phone = normalizarNumeroTelefono(trigger?.dataset?.phone || ''); const orderLabel = trigger?.dataset?.orderLabel || ''; const clientName = trigger?.dataset?.clientName || ''; - const phoneLocked = trigger?.dataset?.phoneLocked === '1'; + const phoneLocked = false; const phoneHiddenByDay1 = trigger?.dataset?.phoneHiddenDay1 === '1'; - if (phoneLocked) { - alert('Este pedido tiene el celular bloqueado hasta completar el sustento de Día 3.'); - return false; - } if (!sourceKey) { alert('No se encontró el pedido para registrar la llamada.'); diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index 12279991..f8e48a3b 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -1406,6 +1406,56 @@ function cc_test_followup_day_info(array $order): ?array ]; } +function cc_test_followup_owner_lock_key(array $order): string +{ + $userId = (int) ($order['user_id'] ?? 0); + if ($userId > 0) { + return 'user:' . $userId; + } + + $assessorKey = trim((string) ($order['assessor_key'] ?? '')); + if ($assessorKey !== '' && mb_strtolower($assessorKey) !== 'sin_asignar') { + return 'assessor:' . mb_strtolower($assessorKey); + } + + return ''; +} + +function cc_test_followup_pending_promo_final_counts(array $orders): array +{ + $counts = []; + + foreach ($orders as $order) { + if (empty($order['promo_final_pendiente'])) { + continue; + } + + $ownerKey = cc_test_followup_owner_lock_key($order); + if ($ownerKey === '') { + continue; + } + + $counts[$ownerKey] = ($counts[$ownerKey] ?? 0) + 1; + } + + return $counts; +} + +function cc_test_followup_day1_phone_hidden(array $order, array $pendingPromoFinalCounts = []): bool +{ + $followupDayInfo = $order['seguimiento_dia_info'] ?? null; + if (!is_array($followupDayInfo) || (int) ($followupDayInfo['day_number'] ?? 0) !== 1) { + return false; + } + + $ownerKey = cc_test_followup_owner_lock_key($order); + if ($ownerKey === '') { + return false; + } + + return !empty($pendingPromoFinalCounts[$ownerKey]); +} + function cc_test_dashboard_date_key(?string $value): ?string { $date = cc_test_followup_parse_datetime($value); diff --git a/index.php b/index.php index 93f82418..4c3c3b06 100644 --- a/index.php +++ b/index.php @@ -10,6 +10,9 @@ if (in_array($userRole, ['Administrador', 'admin'], true)) { } elseif (strcasecmp(trim($userRole), 'Marketing') === 0) { $homeUrl = 'marketing_produccion.php'; $homeLabel = 'Ir a Producción de Video'; +} elseif (strcasecmp(trim($userRole), 'Asesor') === 0) { + $homeUrl = 'kanban.php'; + $homeLabel = 'Ir al panel'; } $callCenterParams = []; foreach ([ diff --git a/layout_header.php b/layout_header.php index 8da25655..3fee649d 100644 --- a/layout_header.php +++ b/layout_header.php @@ -375,8 +375,8 @@ $navItems = [ ] ], 'modulo_pruebas_group' => [ - 'icon' => 'fa-flask', - 'text' => 'Módulo de Pruebas', + 'icon' => 'fa-phone', + 'text' => 'Gestión de pedidos', 'roles' => ['Administrador', 'admin', 'Asesor'], 'submenu' => [ 'call_center' => [ @@ -453,7 +453,14 @@ $navItems = [