From 891893f22183e528fc3a6108fc6c4084db118eab Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 25 May 2026 07:07:14 +0000 Subject: [PATCH] Autosave: 20260525-070710 --- gestiones_callcenter.php | 259 +++++++++++++++++++++++---- includes/callcenter_test_helpers.php | 21 ++- update_callcenter_test_tracking.php | 2 +- 3 files changed, 243 insertions(+), 39 deletions(-) diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index 91a43e9d..0f9beb07 100644 --- a/gestiones_callcenter.php +++ b/gestiones_callcenter.php @@ -70,11 +70,11 @@ function cc_test_order_label(array $order): string function cc_test_badge_class(string $estado): string { return match ($estado) { - 'CONFIRMADO FECHA', 'CONTRAENTREGA CONFIRMADO' => 'bg-success-subtle text-success-emphasis', + 'CONFIRMADO CONTRAENTREGA', 'CONFIRMADO CONTRAENTREGA FECHA', 'CONFIRMADO ENVIO', 'CONFIRMADO FECHA', 'CONTRAENTREGA CONFIRMADO' => 'bg-success-subtle text-success-emphasis', 'DEVOLVER LLAMADA' => 'bg-info-subtle text-info-emphasis', 'OBSERVADO' => 'bg-warning-subtle text-warning-emphasis', 'CANCELADO' => 'bg-danger-subtle text-danger-emphasis', - 'ENVIO REPETIDO' => 'bg-secondary-subtle text-secondary-emphasis', + 'REPETIDO', 'ENVIO REPETIDO' => 'bg-secondary-subtle text-secondary-emphasis', 'SE ENVIO NUMERO DE CUENTA' => 'bg-primary-subtle text-primary-emphasis', default => 'bg-dark-subtle text-dark-emphasis', }; @@ -97,6 +97,7 @@ $allowedViews = [ 'pendientes_hoy' => 'Pendientes de hoy', 'nuevos_hoy' => 'Nuevos de hoy', 'confirmados' => 'Confirmados', + 'observados' => 'Observados', 'cerrados' => 'Cerrados / descartados', 'todos' => 'Todos los pedidos cargados', ]; @@ -115,6 +116,7 @@ $stats = [ 'pendientes_hoy' => 0, 'nuevos_hoy' => 0, 'confirmados' => 0, + 'observados' => 0, 'cerrados' => 0, ]; @@ -164,6 +166,9 @@ try { if (in_array($order['estado'], cc_test_confirmed_states(), true)) { $stats['confirmados']++; } + if ($order['estado'] === 'OBSERVADO') { + $stats['observados']++; + } if ($order['es_cerrado']) { $stats['cerrados']++; } @@ -175,6 +180,7 @@ try { 'pendientes_hoy' => (bool) ($order['es_pendiente_hoy'] ?? false), 'nuevos_hoy' => (bool) ($order['es_nuevo_hoy'] ?? false), 'confirmados' => in_array(($order['estado'] ?? ''), cc_test_confirmed_states(), true), + 'observados' => ($order['estado'] ?? '') === 'OBSERVADO', 'cerrados' => (bool) ($order['es_cerrado'] ?? false), default => true, }; @@ -209,7 +215,7 @@ require_once 'layout_header.php';

Call Center de prueba

-

Ahora el panel trabaja por bandejas: Nuevos de hoy, Pendientes de hoy, Confirmados y Cerrados. Así no se mezcla todo cuando entran pedidos diarios.

+

Ahora el panel trabaja por bandejas: Nuevos de hoy, Pendientes de hoy, Confirmados, Observados y Cerrados. Así no se mezcla todo cuando entran pedidos diarios.

Drive detectado: filas @@ -229,7 +235,7 @@ require_once 'layout_header.php';

Flujo recomendado ya listo para prueba

-

Los pedidos nuevos entran desde Drive. Luego tú gestionas cada cliente con estado, próxima llamada y observaciones internas. Los casos abiertos siguen apareciendo en Pendientes de hoy hasta que los cierres o confirmes.

+

Los pedidos nuevos entran desde Drive. Luego tú gestionas cada cliente con estado, próxima llamada y observaciones internas. Los casos abiertos siguen apareciendo en Pendientes de hoy o Observados hasta que los cierres o confirmes.

Campos editables del módulo
@@ -269,6 +275,16 @@ require_once 'layout_header.php';
+ -
+
-
+
-
Todos los pedidos cargados
+
Todos
-
Usa esta vista solo para revisión general.
@@ -296,7 +311,8 @@ require_once 'layout_header.php';

-

Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado fecha 📅, Contraentrega confirmado, Cancelado y Envío repetido.

+

Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega 📅, Confirmado envío, Cancelado y Repetido.

+

El botón Llamar / AirDroid registra el intento, copia el número y muestra la ayuda visual para pegar en tu app de AirDroid.

pedidos en esta bandeja
@@ -366,9 +382,16 @@ require_once 'layout_header.php';
- - Llamar - + @@ -450,7 +473,7 @@ require_once 'layout_header.php';
-
Los estados abiertos vuelven a Pendientes de hoy; Confirmado fecha 📅 te permite programar la entrega.
+
Los estados abiertos vuelven a Pendientes de hoy; Confirmado contraentrega 📅 te permite programar la entrega.
@@ -546,6 +569,43 @@ require_once 'layout_header.php'; + + @@ -556,15 +616,16 @@ function toggleAgendaFields(sourceKey) { const deliveryGroup = document.getElementById('delivery-group-' + sourceKey); const nextCallInput = document.getElementById('proxima-' + sourceKey); const deliveryInput = document.getElementById('fecha-entrega-' + sourceKey); - const needsDeliveryDate = estado === 'CONFIRMADO FECHA'; + const needsDeliveryDate = estado === 'CONFIRMADO CONTRAENTREGA' || estado === 'CONFIRMADO CONTRAENTREGA FECHA' || estado === 'CONFIRMADO FECHA'; + const needsNextCall = ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO'].includes(estado); if (nextCallGroup) { - nextCallGroup.classList.toggle('d-none', needsDeliveryDate); + nextCallGroup.classList.toggle('d-none', !needsNextCall); } if (deliveryGroup) { deliveryGroup.classList.toggle('d-none', !needsDeliveryDate); } - if (needsDeliveryDate && nextCallInput) { + if (!needsNextCall && nextCallInput) { nextCallInput.value = ''; } if (!needsDeliveryDate && deliveryInput) { @@ -626,23 +687,129 @@ function actualizarContadorLlamadas(sourceKey, total) { }); } -function registrarLlamada(event, sourceKey, phoneUrl, trigger) { +function normalizarNumeroTelefono(value) { + return String(value || '').replace(/\D+/g, ''); +} + +function copyTextLegacy(text) { + const input = document.createElement('input'); + input.type = 'text'; + input.value = text; + input.setAttribute('readonly', 'readonly'); + input.style.position = 'fixed'; + input.style.opacity = '0'; + document.body.appendChild(input); + input.select(); + input.setSelectionRange(0, input.value.length); + let copied = false; + + try { + copied = document.execCommand('copy'); + } catch (error) { + copied = false; + } + + document.body.removeChild(input); + return copied; +} + +function copyPhoneToClipboard(phone) { + if (!phone) { + return Promise.resolve(false); + } + + if (navigator.clipboard && window.isSecureContext) { + return navigator.clipboard.writeText(phone) + .then(() => true) + .catch(() => copyTextLegacy(phone)); + } + + return Promise.resolve(copyTextLegacy(phone)); +} + +function openAirDroidWeb() { + return window.open('https://web.airdroid.com/', '_blank', 'noopener'); +} + +function showAirDroidHelper(details) { + const phone = details.phone || '-'; + const orderLabel = details.orderLabel || 'Sin número'; + const clientName = details.clientName || 'Cliente sin nombre'; + const registered = !!details.registered; + const copied = !!details.copied; + + const orderNode = document.getElementById('airDroidOrderLabel'); + const clientNode = document.getElementById('airDroidClientName'); + const phoneNode = document.getElementById('airDroidPhoneNumber'); + const statusNode = document.getElementById('airDroidStatusBox'); + const extraNode = document.getElementById('airDroidExtraStatus'); + const copyButton = document.getElementById('airDroidCopyButton'); + const openButton = document.getElementById('airDroidOpenButton'); + const modalElement = document.getElementById('airDroidAssistModal'); + + if (!modalElement) { + return; + } + + if (orderNode) orderNode.textContent = orderLabel; + if (clientNode) clientNode.textContent = clientName; + if (phoneNode) phoneNode.textContent = phone; + + if (statusNode) { + statusNode.className = 'alert ' + (registered ? 'alert-primary' : 'alert-warning') + ' py-2 small mb-3'; + statusNode.textContent = registered + ? 'Intento de llamada registrado. Ahora pega el número en tu app de AirDroid.' + : 'Se abrió la ayuda, pero el intento no se pudo registrar en el CRM.'; + } + + if (extraNode) { + extraNode.textContent = copied ? 'Número copiado correctamente.' : 'No se pudo copiar automáticamente; usa el botón Copiar número.'; + } + + if (copyButton) { + copyButton.dataset.phone = phone !== '-' ? phone : ''; + } + + if (openButton) { + openButton.onclick = function () { + openAirDroidWeb(); + }; + } + + if (window.bootstrap && window.bootstrap.Modal) { + window.bootstrap.Modal.getOrCreateInstance(modalElement).show(); + } +} + +function registrarLlamada(event, trigger) { if (event) { event.preventDefault(); } + const sourceKey = trigger?.dataset?.sourceKey || ''; + const phone = normalizarNumeroTelefono(trigger?.dataset?.phone || ''); + const orderLabel = trigger?.dataset?.orderLabel || ''; + const clientName = trigger?.dataset?.clientName || ''; + + if (!sourceKey) { + alert('No se encontró el pedido para registrar la llamada.'); + return false; + } + if (trigger) { trigger.classList.add('disabled'); trigger.setAttribute('aria-disabled', 'true'); } + const copyPromise = copyPhoneToClipboard(phone).catch(() => false); + const body = new URLSearchParams({ pedido_id: sourceKey, - resultado: 'Llamada iniciada', - observacion: 'Clic en botón llamar desde el panel' + resultado: 'Llamada iniciada - AirDroid', + observacion: 'Clic en botón Llamar / AirDroid desde el panel' }); - fetch('save_llamada.php', { + const savePromise = fetch('save_llamada.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: body.toString(), @@ -657,24 +824,56 @@ function registrarLlamada(event, sourceKey, phoneUrl, trigger) { if (typeof data.total_llamadas !== 'undefined') { actualizarContadorLlamadas(sourceKey, Number(data.total_llamadas) || 0); } - }) - .catch(error => { - console.error('Error al registrar llamada:', error); - alert(error.message || 'No se pudo registrar la llamada.'); - }) - .finally(() => { + + return true; + }); + + Promise.allSettled([savePromise, copyPromise]).then(results => { + const registerResult = results[0]; + const copyResult = results[1]; + const registered = registerResult.status === 'fulfilled' && registerResult.value === true; + const copied = copyResult.status === 'fulfilled' && copyResult.value === true; + + if (!registered) { + const error = registerResult.reason; + console.error('Error al registrar llamada:', error); + alert((error && error.message) || 'No se pudo registrar la llamada.'); + } + + showAirDroidHelper({ + phone, + orderLabel, + clientName, + registered, + copied + }); + }).finally(() => { if (trigger) { trigger.classList.remove('disabled'); trigger.removeAttribute('aria-disabled'); } - - if (phoneUrl) { - window.location.href = phoneUrl; - } }); return false; } + +const airDroidCopyButton = document.getElementById('airDroidCopyButton'); +if (airDroidCopyButton) { + airDroidCopyButton.addEventListener('click', function () { + const phone = this.dataset.phone || ''; + copyPhoneToClipboard(phone).then(copied => { + if (!copied) { + alert('No se pudo copiar el número automáticamente.'); + return; + } + + const extraNode = document.getElementById('airDroidExtraStatus'); + if (extraNode) { + extraNode.textContent = 'Número copiado correctamente. Si la pestaña no se abrió antes, usa Abrir AirDroid.'; + } + }); + }); +} diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index 7b605f4d..f627c02a 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -91,10 +91,10 @@ function cc_test_valid_states(): array 'DEVOLVER LLAMADA', 'OBSERVADO', 'SE ENVIO NUMERO DE CUENTA', - 'CONFIRMADO FECHA', - 'CONTRAENTREGA CONFIRMADO', + 'CONFIRMADO CONTRAENTREGA', + 'CONFIRMADO ENVIO', 'CANCELADO', - 'ENVIO REPETIDO', + 'REPETIDO', ]; } @@ -105,25 +105,30 @@ function cc_test_open_states(): array function cc_test_confirmed_states(): array { - return ['CONFIRMADO FECHA', 'CONTRAENTREGA CONFIRMADO']; + return ['CONFIRMADO CONTRAENTREGA', 'CONFIRMADO ENVIO', 'CONFIRMADO FECHA', 'CONTRAENTREGA CONFIRMADO']; } function cc_test_closed_states(): array { - return ['CANCELADO', 'ENVIO REPETIDO']; + return ['CANCELADO', 'REPETIDO']; } function cc_test_requires_delivery_date(string $estado): bool { - return $estado === 'CONFIRMADO FECHA'; + return in_array($estado, ['CONFIRMADO CONTRAENTREGA', 'CONFIRMADO FECHA'], true); } function cc_test_state_label(string $estado): string { return match ($estado) { 'SE ENVIO NUMERO DE CUENTA' => 'SE ENVIÓ NÚMERO DE CUENTA', - 'CONFIRMADO FECHA' => 'CONFIRMADO FECHA 📅', - 'ENVIO REPETIDO' => 'ENVÍO REPETIDO', + 'CONFIRMADO CONTRAENTREGA' => 'CONFIRMADO CONTRAENTREGA 📅', + 'CONFIRMADO CONTRAENTREGA FECHA' => 'CONFIRMADO CONTRAENTREGA 📅', + 'CONFIRMADO FECHA' => 'CONFIRMADO CONTRAENTREGA 📅', + 'CONFIRMADO ENVIO' => 'CONFIRMADO ENVIO', + 'CONTRAENTREGA CONFIRMADO' => 'CONFIRMADO ENVIO', + 'REPETIDO' => 'REPETIDO', + 'ENVIO REPETIDO' => 'REPETIDO', default => $estado, }; } diff --git a/update_callcenter_test_tracking.php b/update_callcenter_test_tracking.php index d9d6d0aa..3e3a7e22 100644 --- a/update_callcenter_test_tracking.php +++ b/update_callcenter_test_tracking.php @@ -78,7 +78,7 @@ try { } if (cc_test_requires_delivery_date($estado) && $fechaEntrega === null) { - throw new RuntimeException('Debes seleccionar la fecha de entrega para CONFIRMADO FECHA 📅.'); + throw new RuntimeException('Debes seleccionar la fecha de entrega para CONFIRMADO CONTRAENTREGA 📅.'); } if (!in_array($estado, cc_test_open_states(), true)) {