diff --git a/call_center_pro.php b/call_center_pro.php index 958e65ef..8213c6a2 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -422,7 +422,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 bandeja principal queda visible pero bloqueada 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.'; + : '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. 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 celular hasta completar ese paso; la llamada sigue disponible desde el panel.'; $errorMessage = null; $noticeMessage = null; @@ -702,6 +702,12 @@ 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 + && !empty($order['promo_final_pendiente']) + && (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4) === 3; + $order['llamada_bloqueada_dia3_mensaje'] = $order['llamada_bloqueada_dia3'] + ? 'Celular oculto hasta completar el sustento de Día 3.' + : ''; $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; @@ -988,9 +994,9 @@ try { } $assessorInboxLockedCount = count($assessorInboxLockedOrders); $assessorInboxLocked = $assessorInboxLockedCount > 0; - $assessorInboxLockedView = $assessorInboxLocked && $view === 'pendientes_hoy'; + $assessorInboxLockedView = $assessorInboxLocked && $view === 'nuevos_hoy'; $assessorInboxLockNotice = $assessorInboxLocked - ? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' del nuevo flujo con sustento pendiente para Promo Final. La bandeja principal queda bloqueada hasta completar ese paso.' + ? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' del nuevo flujo con sustento pendiente para Promo Final. En esos casos, el celular aparece con candado hasta completar ese paso.' : ''; $visibleOrdersTotalCount = count($visibleOrders); @@ -1354,8 +1360,8 @@ require_once 'layout_header.php';
- -
Bloqueado
+ +
Con candado en celulares
@@ -1417,7 +1423,7 @@ require_once 'layout_header.php'; pedidos únicos en esta bandeja - Bloqueado por sustento de Día 3 + Candado en celulares por sustento de Día 3 Página de · 200 por página @@ -1427,16 +1433,16 @@ require_once 'layout_header.php';
-
+
-
Bandeja bloqueada hasta completar el sustento
-
Usa Últimos 3 días hábiles para subir la imagen y mover esos pedidos a Promo Final.
+
Celulares con candado por sustento de Día 3
+
Usa Últimos 3 días hábiles si necesitas completar ese sustento más rápido.
- Ir a Últimos 3 días + Ir a Últimos 3 días
-
+
@@ -1524,7 +1530,16 @@ require_once 'layout_header.php';
-
Celular:
+
+ Celular: + + + Celular oculto + + + + +
DNI:
@@ -1641,14 +1656,14 @@ require_once 'layout_header.php'; - - + -
·
+
+ · + + + Celular oculto + + + + +
- +
@@ -1730,11 +1754,19 @@ require_once 'layout_header.php';
Llamar / Gestionar
-

Número listo para copiar

-
+

+
+ + + Celular oculto + + + + +
- +
@@ -2737,6 +2769,7 @@ function showAirDroidHelper(details) { const registered = !!details.registered; const copied = !!details.copied; const modalId = details.modalId || ''; + const phoneLocked = !!details.phoneLocked; if (modalId) { const modalElement = document.getElementById(modalId); @@ -2753,10 +2786,26 @@ function showAirDroidHelper(details) { if (orderNode) orderNode.textContent = orderLabel; if (clientNode) clientNode.textContent = clientName; - if (phoneNode) phoneNode.textContent = phone; + if (phoneNode) { + if (phoneLocked) { + phoneNode.innerHTML = 'Celular oculto'; + } else { + phoneNode.textContent = phone; + } + } if (copyButton) { - copyButton.dataset.phone = phone !== '-' ? phone : ''; + if (phoneLocked) { + copyButton.dataset.phone = ''; + copyButton.disabled = true; + copyButton.textContent = 'Número oculto'; + copyButton.setAttribute('title', 'Disponible cuando se complete el sustento de Día 3'); + } else { + copyButton.dataset.phone = phone !== '-' ? phone : ''; + copyButton.disabled = false; + copyButton.textContent = 'Copiar número'; + copyButton.removeAttribute('title'); + } } if (openButton) { @@ -2776,6 +2825,7 @@ 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'; if (!sourceKey) { alert('No se encontró el pedido para registrar la llamada.'); @@ -2837,6 +2887,7 @@ function registrarLlamada(event, trigger) { sourceKey, modalId, phone, + phoneLocked, orderLabel, clientName, registered, diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index 81de732e..9fddbcae 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -320,7 +320,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.'; + : '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.'; $errorMessage = null; $noticeMessage = null; @@ -887,6 +887,12 @@ 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 + && !empty($order['promo_final_pendiente']) + && (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4) === 3; + $order['llamada_bloqueada_dia3_mensaje'] = $order['llamada_bloqueada_dia3'] + ? 'Celular oculto hasta completar el sustento de Día 3.' + : ''; $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; @@ -2042,14 +2048,14 @@ require_once 'layout_header.php'; -
- +
-
Celular:
+
+ Celular: + + + Celular oculto + + + + +
DNI:
@@ -2160,10 +2175,19 @@ require_once 'layout_header.php'; -
·
+
+ · + + + Celular oculto + + + + +
- +
@@ -2202,11 +2226,19 @@ require_once 'layout_header.php';
Llamar / Gestionar
-

Número listo para copiar

-
+

+
+ + + Celular oculto + + + + +
- +
@@ -3253,6 +3285,7 @@ function showAirDroidHelper(details) { const registered = !!details.registered; const copied = !!details.copied; const modalId = details.modalId || ''; + const phoneLocked = !!details.phoneLocked; if (modalId) { const modalElement = document.getElementById(modalId); @@ -3269,10 +3302,26 @@ function showAirDroidHelper(details) { if (orderNode) orderNode.textContent = orderLabel; if (clientNode) clientNode.textContent = clientName; - if (phoneNode) phoneNode.textContent = phone; + if (phoneNode) { + if (phoneLocked) { + phoneNode.innerHTML = 'Celular oculto'; + } else { + phoneNode.textContent = phone; + } + } if (copyButton) { - copyButton.dataset.phone = phone !== '-' ? phone : ''; + if (phoneLocked) { + copyButton.dataset.phone = ''; + copyButton.disabled = true; + copyButton.textContent = 'Número oculto'; + copyButton.setAttribute('title', 'Disponible cuando se complete el sustento de Día 3'); + } else { + copyButton.dataset.phone = phone !== '-' ? phone : ''; + copyButton.disabled = false; + copyButton.textContent = 'Copiar número'; + copyButton.removeAttribute('title'); + } } if (openButton) { @@ -3292,6 +3341,7 @@ 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'; if (!sourceKey) { alert('No se encontró el pedido para registrar la llamada.'); @@ -3353,6 +3403,7 @@ function registrarLlamada(event, trigger) { sourceKey, modalId, phone, + phoneLocked, orderLabel, clientName, registered, diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index d7e6a505..12279991 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -1320,7 +1320,7 @@ function cc_test_followup_parse_datetime(?string $value): ?DateTimeImmutable function cc_test_followup_started_at(array $order): ?DateTimeImmutable { - foreach (['drive_imported_at', 'first_seen_at', 'import_id'] as $field) { + foreach (['assigned_at', 'drive_imported_at', 'first_seen_at', 'import_id'] as $field) { $date = cc_test_followup_parse_datetime($order[$field] ?? null); if ($date instanceof DateTimeImmutable) { return $date; diff --git a/save_llamada.php b/save_llamada.php index 23c5790b..32a6c32a 100644 --- a/save_llamada.php +++ b/save_llamada.php @@ -30,10 +30,12 @@ try { $role = (string) ($_SESSION['user_role'] ?? ''); $isAdmin = in_array($role, ['Administrador', 'admin'], true); + $tracking = null; + if (!$isAdmin) { cc_test_ensure_tracking_table($pdo); - $stmtTrack = $pdo->prepare('SELECT user_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1'); + $stmtTrack = $pdo->prepare('SELECT user_id, estado, assigned_at, promo_final_evidencia_path FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1'); $stmtTrack->execute([$pedido_id]); $tracking = $stmtTrack->fetch(PDO::FETCH_ASSOC); diff --git a/update_callcenter_test_tracking.php b/update_callcenter_test_tracking.php index 3c036231..a13f43df 100644 --- a/update_callcenter_test_tracking.php +++ b/update_callcenter_test_tracking.php @@ -866,7 +866,7 @@ try { $pdo = db(); cc_test_ensure_tracking_table($pdo); - $stmtCurrent = $pdo->prepare('SELECT estado, numero_cuenta_enviado_at, numero_cuenta_sede_id, numero_cuenta_dni, user_id, 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, pedido_rotulado_pedido_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1'); + $stmtCurrent = $pdo->prepare('SELECT estado, numero_cuenta_enviado_at, numero_cuenta_sede_id, numero_cuenta_dni, user_id, assigned_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, pedido_rotulado_pedido_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1'); $stmtCurrent->execute([$sourceKey]); $currentTracking = $stmtCurrent->fetch(PDO::FETCH_ASSOC) ?: null; @@ -1071,12 +1071,10 @@ try { throw new RuntimeException('No encontré el pedido base para moverlo a Promo Final.'); } - $promoFinalDayInfo = cc_test_followup_day_info([ + $promoFinalDayInfo = cc_test_followup_day_info(array_merge($sourceOrderForPromo, [ 'estado' => $estado, - 'drive_imported_at' => $sourceOrderForPromo['drive_imported_at'] ?? null, - 'first_seen_at' => $sourceOrderForPromo['first_seen_at'] ?? null, - 'import_id' => $sourceOrderForPromo['import_id'] ?? null, - ]); + 'assigned_at' => $currentTracking['assigned_at'] ?? null, + ])); if (empty($promoFinalDayInfo['is_promo_final'])) { $promoFinalThresholdDay = (int) ($promoFinalDayInfo['promo_final_threshold_day'] ?? 4); throw new RuntimeException('Este pedido aún no puede pasar a Promo Final. Recién se habilita desde el Día ' . $promoFinalThresholdDay . '.');