diff --git a/call_center_pro.php b/call_center_pro.php index b9d2cc4d..c273c50a 100644 --- a/call_center_pro.php +++ b/call_center_pro.php @@ -25,6 +25,12 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'panel_status') { header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); + $panelStoreKey = trim(mb_strtolower((string) ($_GET['store'] ?? 'flower'))); + $availablePanelStores = drive_test_available_stores(); + if (!isset($availablePanelStores[$panelStoreKey])) { + $panelStoreKey = 'flower'; + } + $currentUserId = (int) ($_SESSION['user_id'] ?? 0); if ($currentUserId <= 0) { http_response_code(401); @@ -36,9 +42,23 @@ if (isset($_GET['ajax']) && $_GET['ajax'] === 'panel_status') { } try { - $stmtPanel = $pdo->prepare("\n SELECT source_key, user_id, estado, assigned_at, updated_at, eliminado_at\n FROM callcenter_test_tracking\n WHERE user_id = ? AND eliminado_at IS NULL\n ORDER BY source_key\n "); - $stmtPanel->execute([$currentUserId]); - $panelOrders = $stmtPanel->fetchAll(PDO::FETCH_ASSOC); + if ($panelStoreKey === 'tuani_recuperables') { + $panelOrders = drive_test_fetch_orders_from_db($pdo, $panelStoreKey); + $panelTracking = drive_test_fetch_tracking($pdo, array_column($panelOrders, 'source_key')); + $panelOrders = drive_test_merge_tracking($panelOrders, $panelTracking); + $panelOrders = array_values(array_filter($panelOrders, static function (array $order): bool { + return empty($order['eliminado']); + })); + } else { + $stmtPanel = $pdo->prepare(" + SELECT source_key, user_id, estado, assigned_at, updated_at, eliminado_at + FROM callcenter_test_tracking + WHERE user_id = ? AND eliminado_at IS NULL + ORDER BY source_key + "); + $stmtPanel->execute([$currentUserId]); + $panelOrders = $stmtPanel->fetchAll(PDO::FETCH_ASSOC); + } echo json_encode([ 'success' => true, @@ -247,20 +267,6 @@ function cc_test_panel_orders_signature(array $orders): string } $view = $_GET['view'] ?? 'pendientes_hoy'; -$allowedViews = [ - 'pendientes_hoy' => 'Bandeja principal', - 'nuevos_hoy' => 'Pedidos asignados hoy', - 'confirmados' => 'Confirmados', - 'recuperados' => 'Recuperados', - 'seguimiento' => 'Seguimiento', - 'promo_final' => 'Promo Final', - 'observados' => 'Observados', - 'cerrados' => 'Cerrados / descartados', - 'todos' => 'Todos los pedidos cargados', -]; -if (!isset($allowedViews[$view])) { - $view = 'pendientes_hoy'; -} $storeKey = trim((string) ($_GET['store'] ?? 'flower')); $storeKey = mb_strtolower($storeKey); @@ -271,6 +277,122 @@ if (!isset($availableStores[$storeKey])) { $storeConfig = $availableStores[$storeKey]; $storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey)); $usesIncrementalSync = !empty($storeConfig['incremental_sync']); +$storeNav = drive_test_store_navigation_state($availableStores, $storeKey); +$storeNavGroups = $storeNav['groups']; +$activeStoreGroupKey = (string) ($storeNav['active_group_key'] ?? $storeKey); +$activeStoreGroupLabel = (string) ($storeNav['active_group_label'] ?? $storeLabel); +$activeStoreGroupChildren = $storeNav['active_group_children'] ?? []; +$parentStoreLabel = (string) ($storeNav['parent_label'] ?? ''); + +$isMainTuaniStore = cc_test_is_tuani_main_store_key($storeKey); +$isTuaniRecuperablesStore = $storeKey === 'tuani_recuperables'; +$viewCardColumnClass = $isTuaniRecuperablesStore ? 'col-md-6 col-xl-2' : 'col-md-6 col-xl-2'; + +$viewCards = $isTuaniRecuperablesStore ? [ + 'pendientes_hoy' => [ + 'label' => 'Carritos recuperables', + 'heading' => 'Carritos recuperables', + 'stat_key' => 'pendientes_hoy', + 'active_class' => 'bg-dark text-white', + ], + 'confirmados' => [ + 'label' => 'Confirmados', + 'heading' => 'Confirmados', + 'stat_key' => 'confirmados', + 'active_class' => 'bg-success-subtle border border-success', + ], + 'recuperados' => [ + 'label' => 'Recuperados', + 'heading' => 'Recuperados', + 'stat_key' => 'recuperados', + 'active_class' => 'bg-warning-subtle border border-warning', + ], + 'seguimiento' => [ + 'label' => 'Seguimiento', + 'heading' => 'Seguimiento', + 'stat_key' => 'seguimiento', + 'active_class' => 'bg-primary-subtle border border-primary', + ], + 'observados' => [ + 'label' => 'Observados', + 'heading' => 'Observados', + 'stat_key' => 'observados', + 'active_class' => 'bg-warning-subtle border border-warning', + ], + 'todos' => [ + 'label' => 'Todos', + 'heading' => 'Todos los pedidos cargados', + 'stat_key' => 'total', + 'active_class' => 'bg-light border', + ], +] : [ + 'pendientes_hoy' => [ + 'label' => 'Bandeja principal', + 'heading' => 'Bandeja principal', + 'stat_key' => 'pendientes_hoy', + 'active_class' => 'bg-dark text-white', + ], + 'nuevos_hoy' => [ + 'label' => 'Asignados hoy', + 'heading' => 'Pedidos asignados hoy', + 'stat_key' => 'nuevos_hoy', + 'active_class' => 'bg-primary-subtle border border-primary', + ], + 'confirmados' => [ + 'label' => 'Confirmados', + 'heading' => 'Confirmados', + 'stat_key' => 'confirmados', + 'active_class' => 'bg-success-subtle border border-success', + ], + 'recuperados' => [ + 'label' => 'Recuperados', + 'heading' => 'Recuperados', + 'stat_key' => 'recuperados', + 'active_class' => 'bg-warning-subtle border border-warning', + ], + 'seguimiento' => [ + 'label' => 'Seguimiento', + 'heading' => 'Seguimiento', + 'stat_key' => 'seguimiento', + 'active_class' => 'bg-primary-subtle border border-primary', + ], + 'promo_final' => [ + 'label' => 'Promo Final', + 'heading' => 'Promo Final', + 'stat_key' => 'promo_final', + 'active_class' => 'bg-danger-subtle border border-danger', + ], + 'observados' => [ + 'label' => 'Observados', + 'heading' => 'Observados', + 'stat_key' => 'observados', + 'active_class' => 'bg-warning-subtle border border-warning', + ], + 'cerrados' => [ + 'label' => 'Cerrados', + 'heading' => 'Cerrados / descartados', + 'stat_key' => 'cerrados', + 'active_class' => 'bg-secondary-subtle border border-secondary', + ], + 'todos' => [ + 'label' => 'Todos', + 'heading' => 'Todos los pedidos cargados', + 'stat_key' => 'total', + 'active_class' => 'bg-light border', + ], +]; + +$allowedViews = []; +foreach ($viewCards as $viewKey => $cardConfig) { + $allowedViews[$viewKey] = $cardConfig['heading']; +} +if (!isset($allowedViews[$view])) { + $view = array_key_first($allowedViews) ?: 'pendientes_hoy'; +} + +$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.' + : '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.'; $errorMessage = null; $noticeMessage = null; @@ -410,7 +532,7 @@ try { ? (string) ($assessors[$selectedAssessorFilter]['label'] ?? $selectedAssessorFilter) : ''; - if (!$isAdmin) { + if (!$isAdmin && !$isTuaniRecuperablesStore) { $orders = array_values(array_filter($orders, static function (array $order) use ($currentUserId): bool { return (int) ($order['user_id'] ?? 0) === $currentUserId; })); @@ -433,9 +555,7 @@ 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['pendiente_logistica_destino'] = cc_test_is_tuani_store_key($storeKey) - ? cc_test_pending_logistica_destination($order) - : null; + $order['pendiente_logistica_destino'] = $isMainTuaniStore ? cc_test_pending_logistica_destination($order) : null; $order['pendiente_logistica'] = $order['pendiente_logistica_destino'] !== null; $order['es_nuevo_hoy'] = $assignedDate ? $assignedDate->format('Y-m-d') === $todayStart->format('Y-m-d') : false; @@ -487,20 +607,35 @@ try { } $performanceDashboard = cc_test_build_performance_dashboard($performanceOrders, $assessors, 7); - $performanceTitle = $isAdmin - ? ($selectedAssessorFilterLabel !== '' ? 'KPI diario · ' . $selectedAssessorFilterLabel : 'KPI diario por asesora') - : 'Mi KPI diario'; - $performanceSubtitle = $isAdmin - ? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados hoy.' - : 'Tus KPIs de hoy se calculan sobre tus pedidos asignados hoy. Confirmación = asignados hoy confirmados hoy. Recuperación = asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados hoy.'; + if ($isTuaniRecuperablesStore) { + $performanceTitle = 'KPI compartido de recuperación'; + $performanceSubtitle = 'Los indicadores de esta bandeja se calculan sobre todos los pedidos visibles para cualquier asesora.'; + $performanceChart1Title = 'Distribución de estados de la bandeja'; + $performanceChart1Note = 'La dona separa los pedidos visibles por estado en esta bandeja compartida.'; + $performanceChart2Title = 'Evolución de los últimos 7 días'; + $performanceChart2Note = 'La línea muestra cómo se mueve la bandeja compartida en los últimos 7 días.'; + $performanceFootnote = 'Los indicadores se calculan sobre todos los pedidos visibles de esta bandeja compartida.'; + } else { + $performanceTitle = $isAdmin + ? ($selectedAssessorFilterLabel !== '' ? 'KPI diario · ' . $selectedAssessorFilterLabel : 'KPI diario por asesora') + : 'Mi KPI diario'; + $performanceSubtitle = $isAdmin + ? 'Confirmación = pedidos asignados hoy confirmados hoy. Recuperación = pedidos asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados hoy.' + : 'Tus KPIs de hoy se calculan sobre tus pedidos asignados hoy. Confirmación = asignados hoy confirmados hoy. Recuperación = asignados antes de hoy confirmados hoy. Rendimiento del día = confirmados totales (nuevos + recuperados) sobre asignados hoy.'; + $performanceChart1Title = $isAdmin ? 'Distribución de estados' : 'Mi distribución de estados'; + $performanceChart1Note = 'La dona separa los pedidos de hoy por estado: confirmados nuevos, recuperados, POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO, SE ENVIO NUMERO DE CUENTA, repetidos y cancelados.'; + $performanceChart2Title = $isAdmin ? 'Evolución de los últimos 7 días' : 'Tu evolución de los últimos 7 días'; + $performanceChart2Note = 'La línea de confirmados separa pedidos asignados hoy y pedidos asignados antes de hoy.'; + $performanceFootnote = 'Seguimiento abierto = pedidos asignados hoy en POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO y SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.'; + } $performanceDashboardHtml = cc_test_render_performance_dashboard($performanceDashboard, [ 'title' => $performanceTitle, 'subtitle' => $performanceSubtitle, - 'chart1_title' => $isAdmin ? 'Distribución de estados' : 'Mi distribución de estados', - 'chart1_note' => 'La dona separa los pedidos de hoy por estado: confirmados nuevos, recuperados, POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO, SE ENVIO NUMERO DE CUENTA, repetidos y cancelados.', - 'chart2_title' => $isAdmin ? 'Evolución de los últimos 7 días' : 'Tu evolución de los últimos 7 días', - 'chart2_note' => 'La línea de confirmados separa pedidos asignados hoy y pedidos asignados antes de hoy.', - 'footnote' => 'Seguimiento abierto = pedidos asignados hoy en POR LLAMAR, DEVOLVER LLAMADA, OBSERVADO y SE ENVIO NUMERO DE CUENTA que todavía no pasan a Promo Final.', + 'chart1_title' => $performanceChart1Title, + 'chart1_note' => $performanceChart1Note, + 'chart2_title' => $performanceChart2Title, + 'chart2_note' => $performanceChart2Note, + 'footnote' => $performanceFootnote, ]); $panelSignature = cc_test_panel_orders_signature($orders); @@ -631,6 +766,16 @@ if ($selectedAssessorFilter !== '') { $callCenterParams['assessor'] = $selectedAssessorFilter; } +if ($isTuaniRecuperablesStore) { + $pageTitle = 'Carritos recuperables | Bandeja compartida'; + $pageDescription = 'Bandeja compartida para que cualquier asesora gestione carritos recuperables con vistas de seguimiento y observados desde Drive.'; +} else { + $pageTitle = 'Pedidos Asignados | Bandeja de la asesora'; + $pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con la misma interfaz, estados, historial y acciones.'; +} + +$callCenterSectionLabel = $isTuaniRecuperablesStore ? 'Carritos recuperables' : 'Pedidos Asignados'; + require_once 'layout_header.php'; ?> @@ -797,22 +942,36 @@ require_once 'layout_header.php';
-

Pedidos Asignados

+

+ +
+
Dentro de
+
+ Pedidos + $childConfig): ?> + + + + +
+
+
Drive detectado: filas @@ -839,98 +998,22 @@ require_once 'layout_header.php';
- - - - - - - - - + $viewCard): ?> + + + + +
-
@@ -967,7 +1050,7 @@ require_once 'layout_header.php';

-

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.

+

@@ -1023,7 +1106,7 @@ require_once 'layout_header.php'; $subirLogisticaButtonLabel = 'Subir pedido'; $subirLogisticaNoteClass = 'small mt-2 text-muted'; $subirLogisticaNoteHtml = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO para enviarlo a logística.'; - if (cc_test_is_tuani_store_key($storeKey)) { + if ($isMainTuaniStore) { if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') { if (!empty($order['ruta_contraentrega_pedido_id'])) { $subirLogisticaButtonLabel = 'Actualizar en ruta'; @@ -1218,7 +1301,7 @@ require_once 'layout_header.php';
- +
@@ -1253,7 +1336,7 @@ require_once 'layout_header.php';
- +
@@ -1596,7 +1679,7 @@ require_once 'layout_header.php';
-
- -
-
-
Bandeja principal
-
-
-
-
-
-
- -
-
-
Nuevos de hoy
-
-
-
-
-
-
- -
-
-
Confirmados
-
-
-
-
-
-
- -
-
-
Recuperados
-
-
-
-
-
-
- -
-
-
Seguimiento
-
-
-
-
-
-
- -
-
-
Promo Final
-
-
-
-
-
-
- -
-
-
Observados
-
-
-
-
-
-
- -
-
-
Cerrados
-
-
-
-
-
-
- -
-
-
Todos
-
-
-
-
-
+ $viewCard): ?> + + + +
+ +
+
+
+
+
+
+
+
+
-
@@ -1310,7 +1371,7 @@ require_once 'layout_header.php';

-

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.

+

pedidos en esta bandeja
@@ -1380,7 +1441,7 @@ require_once 'layout_header.php'; $subirLogisticaButtonLabel = 'Subir pedido'; $subirLogisticaNoteClass = 'small mt-2 text-muted'; $subirLogisticaNoteHtml = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO para enviarlo a logística.'; - if (cc_test_is_tuani_store_key($storeKey)) { + if ($isMainTuaniStore) { if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') { if (!empty($order['ruta_contraentrega_pedido_id'])) { $subirLogisticaButtonLabel = 'Actualizar en ruta'; @@ -1606,7 +1667,7 @@ require_once 'layout_header.php';
- +
@@ -1641,7 +1702,7 @@ require_once 'layout_header.php';
- +
@@ -1984,7 +2045,7 @@ require_once 'layout_header.php';