From a902ed41a0ff1f74c70cf2bd1f3bb8097ca1554f Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 31 Jul 2026 07:08:01 +0000 Subject: [PATCH] Autosave: 20260731-070936 --- assets/css/style.css | 29 ++++++++++++++++ call_center_pro.php | 50 ++++++++++++++++++++++++---- configuracion.php | 32 +++++++++++++++--- gestiones_callcenter.php | 11 +++--- includes/callcenter_test_helpers.php | 42 ++++++++++++++++------- save_info_producto.php | 12 ++++--- update_callcenter_test_tracking.php | 3 +- 7 files changed, 148 insertions(+), 31 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 24e98539..cdffb0e2 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -437,6 +437,35 @@ h1, .h1 { border: 1px solid #ff9800 !important; } +/* Call center: badge para día de seguimiento */ +.cc-followup-day-badge { + display: inline-flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: .06rem; + min-width: 3.9rem; + min-height: 3.9rem; + padding: .4rem .45rem; + border-radius: .95rem; + text-align: center; + font-weight: 800; + line-height: 1; + letter-spacing: .02em; + box-shadow: 0 .45rem 1rem rgba(15, 23, 42, .08); + margin-bottom: .55rem; +} + +.cc-followup-day-badge-label { + font-size: .62rem; + letter-spacing: .08em; + text-transform: uppercase; +} + +.cc-followup-day-badge-number { + font-size: 1.15rem; +} + /* Call center: Estado de fila (sombreado completo) */ .cc-callcenter-row td { background-color: var(--cc-row-bg, #ffffff) !important; diff --git a/call_center_pro.php b/call_center_pro.php index a40f36c3..958e65ef 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 4 y exige imagen de sustento para mover el pedido. 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 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.'; $errorMessage = null; $noticeMessage = null; @@ -979,6 +979,20 @@ try { return cc_test_order_time($b) <=> cc_test_order_time($a); }); + $assessorInboxLockedOrders = []; + if (!$isAdmin && !$isTuaniRecuperablesStore) { + $assessorInboxLockedOrders = array_values(array_filter($orders, static function (array $order): bool { + return !empty($order['promo_final_pendiente']) + && (int) ($order['seguimiento_dia_info']['promo_final_threshold_day'] ?? 4) === 3; + })); + } + $assessorInboxLockedCount = count($assessorInboxLockedOrders); + $assessorInboxLocked = $assessorInboxLockedCount > 0; + $assessorInboxLockedView = $assessorInboxLocked && $view === 'pendientes_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.' + : ''; + $visibleOrdersTotalCount = count($visibleOrders); $todosPageSize = 200; $todosPage = 1; @@ -1340,6 +1354,9 @@ require_once 'layout_header.php';
+ +
Bloqueado
+
@@ -1388,20 +1405,38 @@ require_once 'layout_header.php';

-

+ +

+ +

+
Asesora: pedidos únicos en esta bandeja + + Bloqueado por sustento de Día 3 + Página de · 200 por página
-
+ +
+
+
+
Bandeja bloqueada hasta completar el sustento
+
Usa Últimos 3 días hábiles para subir la imagen y mover esos pedidos a Promo Final.
+
+ Ir a Últimos 3 días +
+
+ +
@@ -1479,8 +1514,11 @@ require_once 'layout_header.php'; @@ -1874,7 +1912,7 @@ require_once 'layout_header.php';
-
Obligatoria al mover el pedido a Promo Final desde el Día 4. La supervisora podrá revisarla.
+
Obligatoria al mover el pedido a Promo Final desde el Día . La supervisora podrá revisarla.
Sustento actual: ver imagen · . diff --git a/configuracion.php b/configuracion.php index 9726346d..5fb610ac 100644 --- a/configuracion.php +++ b/configuracion.php @@ -218,9 +218,13 @@ if (isset($_SESSION['error_message'])) {
- + - Sube una imagen para añadir o reemplazar la actual. + + Sube una imagen para añadir o reemplazar la actual, o marca la opción para eliminar la imagen existente.
@@ -428,13 +432,19 @@ function editCard(card) { document.getElementById('texto_informativo').value = card.texto_informativo; document.getElementById('column_id').value = card.column_id; document.getElementById('current_imagen').value = card.imagen_url; - + const imagePreview = document.getElementById('image-preview'); + const removeImageWrap = document.getElementById('remove-image-wrap'); + const removeImageCheckbox = document.getElementById('remove_imagen'); imagePreview.innerHTML = ''; + removeImageCheckbox.checked = false; if (card.imagen_url) { imagePreview.innerHTML = `

Imagen actual:

`; + removeImageWrap.style.display = 'block'; + } else { + removeImageWrap.style.display = 'none'; } - + const formCard = document.querySelector('#form-title').closest('.card'); formCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); } @@ -444,7 +454,21 @@ function resetForm() { document.getElementById('info-id').value = ''; document.querySelector('form[action="save_info_producto.php"]').reset(); document.getElementById('image-preview').innerHTML = ''; + document.getElementById('remove-image-wrap').style.display = 'none'; + document.getElementById('remove_imagen').checked = false; } + +document.getElementById('imagen').addEventListener('change', function () { + if (this.files.length > 0) { + document.getElementById('remove_imagen').checked = false; + } +}); + +document.getElementById('remove_imagen').addEventListener('change', function () { + if (this.checked) { + document.getElementById('imagen').value = ''; + } +}); \ No newline at end of file diff --git a/gestiones_callcenter.php b/gestiones_callcenter.php index 2ae77e17..81de732e 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 4 y 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.'; $errorMessage = null; $noticeMessage = null; @@ -2070,8 +2070,11 @@ require_once 'layout_header.php';
@@ -2384,7 +2387,7 @@ require_once 'layout_header.php';
-
Obligatoria al mover el pedido a Promo Final desde el Día 4. La supervisora podrá revisarla.
+
Obligatoria al mover el pedido a Promo Final desde el Día . La supervisora podrá revisarla.
Sustento actual: ver imagen · . diff --git a/includes/callcenter_test_helpers.php b/includes/callcenter_test_helpers.php index f7f3b9d8..d7e6a505 100644 --- a/includes/callcenter_test_helpers.php +++ b/includes/callcenter_test_helpers.php @@ -1330,6 +1330,22 @@ function cc_test_followup_started_at(array $order): ?DateTimeImmutable return null; } +function cc_test_followup_promo_final_cutoff(): DateTimeImmutable +{ + return new DateTimeImmutable('2026-07-29 00:00:00'); +} + +function cc_test_followup_promo_final_threshold_day(array $order): int +{ + $startedAt = cc_test_followup_started_at($order); + if (!$startedAt instanceof DateTimeImmutable) { + return 4; + } + + $cutoff = cc_test_followup_promo_final_cutoff(); + return $startedAt->format('Y-m-d') >= $cutoff->format('Y-m-d') ? 3 : 4; +} + function cc_test_followup_day_info(array $order): ?array { $estado = cc_test_normalize_state((string) ($order['estado'] ?? '')); @@ -1350,28 +1366,29 @@ function cc_test_followup_day_info(array $order): ?array } $dayNumber = $daysElapsed + 1; + $promoFinalThresholdDay = cc_test_followup_promo_final_threshold_day($order); $badgeClass = 'bg-primary-subtle text-primary-emphasis border'; $textClass = 'text-primary-emphasis'; $description = 'Seguimiento dentro de la ventana óptima.'; $noticeText = ''; $rowNoticeClass = ''; - if ($dayNumber === 2) { - $badgeClass = 'bg-info-subtle text-info-emphasis border'; - $textClass = 'text-info-emphasis'; - $description = 'Aún está dentro de los 3 días de seguimiento.'; - } elseif ($dayNumber === 3) { - $badgeClass = 'bg-warning-subtle text-warning-emphasis border'; - $textClass = 'text-warning-emphasis'; - $description = 'Último día de seguimiento antes de Promo Final.'; - $noticeText = 'Último día de seguimiento. Mañana debe pasar a Promo Final con imagen de sustento.'; - $rowNoticeClass = 'text-warning-emphasis'; - } elseif ($dayNumber >= 4) { + if ($dayNumber >= $promoFinalThresholdDay) { $badgeClass = 'bg-danger-subtle text-danger-emphasis border'; $textClass = 'text-danger-emphasis'; $description = 'Ya debe pasar a Promo Final.'; $noticeText = 'Este pedido lleva ' . $dayNumber . ' días y debe moverse a Promo Final con imagen de sustento.'; $rowNoticeClass = 'text-danger-emphasis'; + } elseif ($dayNumber === max(1, $promoFinalThresholdDay - 1)) { + $badgeClass = 'bg-warning-subtle text-warning-emphasis border'; + $textClass = 'text-warning-emphasis'; + $description = 'Último día de seguimiento antes de Promo Final.'; + $noticeText = 'Último día de seguimiento. Mañana debe pasar a Promo Final con imagen de sustento.'; + $rowNoticeClass = 'text-warning-emphasis'; + } elseif ($dayNumber === 2) { + $badgeClass = 'bg-info-subtle text-info-emphasis border'; + $textClass = 'text-info-emphasis'; + $description = 'Aún está dentro de los 3 días de seguimiento.'; } return [ @@ -1384,7 +1401,8 @@ function cc_test_followup_day_info(array $order): ?array 'notice_text' => $noticeText, 'row_notice_class' => $rowNoticeClass, 'started_at_label' => $startedAt->format('d/m/Y'), - 'is_promo_final' => $dayNumber >= 4, + 'promo_final_threshold_day' => $promoFinalThresholdDay, + 'is_promo_final' => $dayNumber >= $promoFinalThresholdDay, ]; } diff --git a/save_info_producto.php b/save_info_producto.php index f289236f..77987301 100644 --- a/save_info_producto.php +++ b/save_info_producto.php @@ -78,7 +78,8 @@ if (isset($_POST['banner_text'])) { $producto_id = !empty($_POST['producto_id']) ? filter_input(INPUT_POST, 'producto_id', FILTER_VALIDATE_INT) : null; $texto_informativo = $_POST['texto_informativo'] ?? ''; $column_id = filter_input(INPUT_POST, 'column_id', FILTER_VALIDATE_INT); - $current_imagen = $_POST['current_imagen'] ?? ''; + $current_imagen = trim($_POST['current_imagen'] ?? ''); + $remove_imagen = !empty($_POST['remove_imagen']) && $_POST['remove_imagen'] === '1'; $is_new_entry = empty($id); $has_new_image = isset($_FILES['imagen']) && $_FILES['imagen']['error'] === UPLOAD_ERR_OK; @@ -89,11 +90,14 @@ if (isset($_POST['banner_text'])) { exit; } - - $imagen_name = $current_imagen; - if (isset($_FILES['imagen']) && $_FILES['imagen']['error'] === UPLOAD_ERR_OK) { + if ($remove_imagen) { + if (!empty($current_imagen) && file_exists($current_imagen)) { + unlink($current_imagen); + } + $imagen_name = ''; + } elseif ($has_new_image) { $file = $_FILES['imagen']; $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!in_array($file['type'], $allowedTypes)) { diff --git a/update_callcenter_test_tracking.php b/update_callcenter_test_tracking.php index aa67c6ec..3c036231 100644 --- a/update_callcenter_test_tracking.php +++ b/update_callcenter_test_tracking.php @@ -1078,7 +1078,8 @@ try { 'import_id' => $sourceOrderForPromo['import_id'] ?? null, ]); if (empty($promoFinalDayInfo['is_promo_final'])) { - throw new RuntimeException('Este pedido aún no puede pasar a Promo Final. Recién se habilita desde el Día 4.'); + $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 . '.'); } if ($promoFinalEvidenceFile === null && $promoFinalEvidencePath === null) {
-
- +
+ DÍA + +
+
ID Drive:
-
- +
+ DÍA + +
+
ID Drive: