Acceso denegado.
"; require_once 'layout_footer.php'; exit(); } function cc_test_parse_datetime(?string $value): ?DateTimeImmutable { $value = trim((string) $value); if ($value === '') { return null; } try { return new DateTimeImmutable($value); } catch (Throwable $exception) { return null; } } function cc_test_format_datetime(?string $value, string $fallback = 'Sin programar'): string { $date = cc_test_parse_datetime($value); return $date ? $date->format('d/m/Y h:i A') : $fallback; } function cc_test_format_datetime_input(?string $value): string { $date = cc_test_parse_datetime($value); return $date ? $date->format('Y-m-d\TH:i') : ''; } function cc_test_format_date(?string $value, string $fallback = 'Sin fecha'): string { $date = cc_test_parse_datetime($value); return $date ? $date->format('d/m/Y') : $fallback; } function cc_test_format_date_input(?string $value): string { $date = cc_test_parse_datetime($value); return $date ? $date->format('Y-m-d') : ''; } function cc_test_format_price(?string $value): string { $value = trim((string) $value); return $value !== '' ? 'S/ ' . $value : 'No registrado'; } function cc_test_display_value(?string $value, string $fallback = 'No registrado'): string { $value = trim((string) $value); return $value !== '' ? $value : $fallback; } 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, '#'); } return 'Sin número'; } function cc_test_badge_class(string $estado): string { return match (cc_test_normalize_state($estado)) { 'POR LLAMAR', 'DEVOLVER LLAMADA' => 'bg-white text-dark border', 'OBSERVADO' => 'bg-warning-subtle text-warning-emphasis', 'SE ENVIO NUMERO DE CUENTA' => 'bg-success-subtle text-success-emphasis', 'CONFIRMADO CONTRAENTREGA' => 'cc-badge-orange', 'CONFIRMADO ENVIO' => 'bg-success text-white', 'CANCELADO' => 'bg-danger-subtle text-danger-emphasis', 'REPETIDO' => 'bg-secondary-subtle text-secondary-emphasis', default => 'bg-white text-dark border', }; } function cc_test_order_time(array $order): int { foreach (['proxima_llamada_at', 'numero_cuenta_enviado_at', 'ultima_gestion_at', 'seguimiento_actualizado'] as $field) { $date = cc_test_parse_datetime($order[$field] ?? null); if ($date) { return $date->getTimestamp(); } } // Fallback recency for Drive imports where import_id is not a datetime (e.g. "#35898"). return cc_test_import_time($order); } function cc_test_import_time(array $order): int { if (!empty($order['is_agregado'])) { return (int) ($order['id'] ?? 0); } $importRaw = trim((string) ($order['import_id'] ?? '')); if ($importRaw !== '') { $date = cc_test_parse_datetime($importRaw); if ($date) { return $date->getTimestamp(); } $digits = preg_replace('/\D+/', '', $importRaw); if ($digits !== '') { return (int) $digits; } } $codigoRaw = trim((string) ($order['codigo'] ?? '')); $digits = preg_replace('/\D+/', '', $codigoRaw); return $digits !== '' ? (int) $digits : 0; } function cc_test_followup_semaforo(array $order): ?array { if (cc_test_normalize_state((string) ($order['estado'] ?? '')) !== 'SE ENVIO NUMERO DE CUENTA') { return null; } return cc_test_account_followup_semaforo($order['numero_cuenta_enviado_at'] ?? null); } $view = $_GET['view'] ?? 'pendientes_hoy'; $allowedViews = [ 'pendientes_hoy' => 'Bandeja principal', 'nuevos_hoy' => 'Nuevos de hoy', 'confirmados' => 'Confirmados', '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); $availableStores = drive_test_available_stores(); if (!isset($availableStores[$storeKey])) { $storeKey = 'flower'; } $storeConfig = $availableStores[$storeKey]; $storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey)); $usesIncrementalSync = !empty($storeConfig['incremental_sync']); $errorMessage = null; $noticeMessage = null; $assessors = []; $orders = []; $visibleOrders = []; $modalsHtml = []; $totalRows = 0; $agregadosCount = 0; $lastProcessedRow = null; $startRow = (int) ($storeConfig['startRow'] ?? 5552); $catalogoProductos = []; $stats = [ 'total' => 0, 'pendientes_hoy' => 0, 'nuevos_hoy' => 0, 'confirmados' => 0, 'seguimiento' => 0, 'promo_final' => 0, 'observados' => 0, 'cerrados' => 0, ]; try { $pdo = db(); cc_test_ensure_tracking_table($pdo); cc_test_ensure_historial_llamadas_table($pdo); $assessors = cc_test_fetch_assessors($pdo); try { $stmtProductos = $pdo->query("SELECT id, nombre FROM products ORDER BY nombre ASC"); $catalogoProductos = $stmtProductos->fetchAll(PDO::FETCH_ASSOC); } catch (Throwable $exception) { $catalogoProductos = []; } if ($usesIncrementalSync) { $syncInfo = drive_test_sync_orders_incremental($pdo, $storeKey, 0); $totalRows = (int) ($syncInfo['drive_rows_total'] ?? 0); $lastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0); $startRow = (int) ($syncInfo['next_start_row'] ?? $startRow); $orders = drive_test_fetch_orders_from_db($pdo, $storeKey); $agregadosCount = 0; foreach ($orders as $o) { if (!empty($o['is_agregado'])) { $agregadosCount++; } } } else { $preview = drive_test_fetch_orders(100, $startRow, $storeKey); $totalRows = (int) ($preview['total_rows'] ?? 0); $orders = $preview['orders'] ?? []; $agregadosOrders = drive_test_fetch_orders_from_db($pdo, $storeKey, true); $agregadosCount = count($agregadosOrders); if (!empty($agregadosOrders)) { $orders = array_merge($orders, $agregadosOrders); } } $tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key')); $orders = drive_test_merge_tracking($orders, $tracking); $orders = array_values(array_filter($orders, static function (array $order): bool { return empty($order['eliminado']); })); $orders = array_values(array_filter($orders, static function (array $order): bool { return empty($order['eliminado']); })); if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'assign_assessor') { $sourceKey = trim((string) ($_POST['source_key'] ?? '')); $targetValue = trim((string) ($_POST['target_assessor'] ?? '')); if ($sourceKey === '' || !preg_match('/^[a-f0-9]{40}$/', $sourceKey)) { throw new RuntimeException('Pedido de prueba inválido para asignar.'); } $targetKey = trim(mb_strtoupper($targetValue)); $targetKey = preg_replace('/\s+/', ' ', $targetKey) ?? $targetKey; $selectedAssessor = $targetKey !== '' ? ($assessors[$targetKey] ?? null) : null; if ($targetKey !== '' && !$selectedAssessor) { throw new RuntimeException('No se encontró la asesora seleccionada.'); } $targetUserId = $selectedAssessor ? (int) $selectedAssessor['id'] : null; $stmtCurrent = $pdo->prepare('SELECT user_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1'); $stmtCurrent->execute([$sourceKey]); $currentUserIdRaw = $stmtCurrent->fetchColumn(); $currentUserId = $currentUserIdRaw !== null ? ((int) $currentUserIdRaw > 0 ? (int) $currentUserIdRaw : null) : null; // Si el pedido ya está asignado, evitamos que se reasigne a otra asesora (solo permitimos dejarlo sin asignar). if ($currentUserId !== null && $targetUserId !== null && (int) $currentUserId !== (int) $targetUserId) { $currentLabel = 'otra asesora'; foreach ($assessors as $assessorKey => $assessor) { if ((int) ($assessor['id'] ?? 0) === (int) $currentUserId) { $currentLabel = (string) ($assessor['label'] ?? $assessorKey); break; } } throw new RuntimeException('Este pedido ya está asignado a ' . $currentLabel . '. Para cambiarlo primero déjalo en "Sin asignar".'); } cc_test_upsert_assignee($pdo, $sourceKey, $targetUserId); $noticeMessage = $selectedAssessor ? 'Pedido asignado a ' . $selectedAssessor['label'] . '.' : 'Pedido dejado sin asignar.'; $tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key')); $orders = drive_test_merge_tracking($orders, $tracking); } $sourceKeys = array_column($orders, 'source_key'); $callCounts = []; if (!empty($sourceKeys)) { $placeholders = implode(',', array_fill(0, count($sourceKeys), '?')); $stmtCalls = $pdo->prepare("SELECT pedido_id, COUNT(*) as total FROM historial_llamadas WHERE pedido_id IN ($placeholders) GROUP BY pedido_id"); $stmtCalls->execute($sourceKeys); $callCounts = $stmtCalls->fetchAll(PDO::FETCH_KEY_PAIR); } $role = (string) ($_SESSION['user_role'] ?? ''); $currentUserId = (int) ($_SESSION['user_id'] ?? 0); if (!in_array($role, ['Administrador', 'admin'], true)) { $orders = array_values(array_filter($orders, static function (array $order) use ($currentUserId): bool { return (int) ($order['user_id'] ?? 0) === $currentUserId; })); } $todayStart = new DateTimeImmutable('today'); $todayEnd = $todayStart->setTime(23, 59, 59); $openStates = cc_test_open_states(); $closedStates = cc_test_closed_states(); foreach ($orders as &$order) { $order['estado'] = cc_test_normalize_state((string) ($order['estado'] ?? '')); $order['total_llamadas'] = (int) ($callCounts[$order['source_key']] ?? 0); $importDate = cc_test_parse_datetime($order['import_id'] ?? null); $proximaDate = cc_test_parse_datetime($order['proxima_llamada_at'] ?? null); $followupDayInfo = cc_test_followup_day_info($order); $order['seguimiento_dia_info'] = $followupDayInfo; $order['dias_seguimiento'] = $followupDayInfo['day_number'] ?? null; $order['promo_final_habilitado'] = !empty($followupDayInfo['is_promo_final']); $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'] = $storeKey === 'otra_tienda' ? cc_test_pending_logistica_destination($order) : null; $order['pendiente_logistica'] = $order['pendiente_logistica_destino'] !== null; $order['es_nuevo_hoy'] = $importDate ? $importDate->format('Y-m-d') === $todayStart->format('Y-m-d') : false; $order['es_pendiente_hoy'] = !$order['es_promo_final'] && (in_array($order['estado'], $openStates, true) || $order['pendiente_logistica']); $order['es_cerrado'] = in_array($order['estado'], $closedStates, true); $stats['total']++; if ($order['es_nuevo_hoy']) { $stats['nuevos_hoy']++; } if ($order['es_promo_final']) { $stats['promo_final']++; } if ($order['es_pendiente_hoy']) { $stats['pendientes_hoy']++; } if (in_array($order['estado'], cc_test_confirmed_states(), true)) { $stats['confirmados']++; } if ($order['estado'] === 'SE ENVIO NUMERO DE CUENTA' && !$order['es_promo_final']) { $stats['seguimiento']++; } if ($order['estado'] === 'OBSERVADO' && !$order['es_promo_final']) { $stats['observados']++; } if ($order['es_cerrado']) { $stats['cerrados']++; } } unset($order); $visibleOrders = array_values(array_filter($orders, static function (array $order) use ($view): bool { return match ($view) { '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), 'seguimiento' => ($order['estado'] ?? '') === 'SE ENVIO NUMERO DE CUENTA' && !($order['es_promo_final'] ?? false), 'promo_final' => (bool) ($order['es_promo_final'] ?? false), 'observados' => ($order['estado'] ?? '') === 'OBSERVADO' && !($order['es_promo_final'] ?? false), 'cerrados' => (bool) ($order['es_cerrado'] ?? false), default => true, }; })); usort($visibleOrders, static function (array $a, array $b) use ($view, $storeKey): int { $aAgregado = !empty($a['is_agregado']); $bAgregado = !empty($b['is_agregado']); if ($aAgregado !== $bAgregado) { return $aAgregado ? -1 : 1; } $aImport = cc_test_import_time($a); $bImport = cc_test_import_time($b); if ($view === 'promo_final') { $aDays = (int) ($a['dias_seguimiento'] ?? 0); $bDays = (int) ($b['dias_seguimiento'] ?? 0); if ($aDays !== $bDays) { return $bDays <=> $aDays; } if ($aImport !== $bImport) { return $aImport <=> $bImport; } } if ($view === "pendientes_hoy") { $aPendientePromoFinal = !empty($a['promo_final_pendiente']); $bPendientePromoFinal = !empty($b['promo_final_pendiente']); if ($aPendientePromoFinal !== $bPendientePromoFinal) { return $aPendientePromoFinal ? -1 : 1; } $aPendienteLogistica = !empty($a['pendiente_logistica']); $bPendienteLogistica = !empty($b['pendiente_logistica']); if ($aPendienteLogistica !== $bPendienteLogistica) { return $aPendienteLogistica ? -1 : 1; } $aAssigned = cc_test_parse_datetime($a['assigned_at'] ?? null); $bAssigned = cc_test_parse_datetime($b['assigned_at'] ?? null); if ($aAssigned && $bAssigned && $aAssigned != $bAssigned) { return $bAssigned <=> $aAssigned; } if ($aAssigned && !$bAssigned) { return -1; } if (!$aAssigned && $bAssigned) { return 1; } $aDue = cc_test_parse_datetime($a["proxima_llamada_at"] ?? null); $bDue = cc_test_parse_datetime($b["proxima_llamada_at"] ?? null); if ($aDue && $bDue && $aDue != $bDue) { return $aDue <=> $bDue; } if ($aDue && !$bDue) { return 1; } if (!$aDue && $bDue) { return -1; } if ($storeKey === 'otra_tienda') { $aSourceRow = (int) ($a['source_row'] ?? 0); $bSourceRow = (int) ($b['source_row'] ?? 0); if ($aSourceRow > 0 && $bSourceRow > 0 && $aSourceRow !== $bSourceRow) { return $bSourceRow <=> $aSourceRow; } } } if ($aImport !== $bImport) { return $bImport <=> $aImport; } return cc_test_order_time($b) <=> cc_test_order_time($a); }); } catch (Throwable $exception) { $errorMessage = $exception->getMessage(); } require_once 'layout_header.php'; ?>

Pedidos Asignados

Drive detectado: filas Agregados: : distribución desde fila · último procesado Extracción: desde fila Auto actualización: cada 10 min Ver vista previa Drive

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
'Falta subirlo a Ruta Contraentrega.', 'rotulado' => 'Falta subirlo a Pedidos Rotulados.', default => '', }; $subirLogisticaButtonLabel = 'Subir pedido'; $subirLogisticaNoteClass = 'small mt-2 text-muted'; $subirLogisticaNoteHtml = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO para enviarlo a logística.'; if ($storeKey === 'otra_tienda') { if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') { if (!empty($order['ruta_contraentrega_pedido_id'])) { $subirLogisticaButtonLabel = 'Actualizar en ruta'; $subirLogisticaNoteClass = 'small mt-2 text-success fw-semibold'; $subirLogisticaNoteHtml = 'En Ruta Contraentrega #' . (int) $order['ruta_contraentrega_pedido_id'] . '. Si cambias algo, usa este botón para actualizarlo.'; } else { $subirLogisticaButtonLabel = 'Subir a ruta'; $subirLogisticaNoteClass = 'small mt-2 text-warning-emphasis'; $subirLogisticaNoteHtml = 'PENDIENTE A SUBIR: este pedido ya está en CONFIRMADO CONTRAENTREGA. Súbelo a Ruta Contraentrega con este botón.'; } } elseif ($order['estado'] === 'CONFIRMADO ENVIO') { if (!empty($order['pedido_rotulado_pedido_id'])) { $subirLogisticaButtonLabel = 'Actualizar rotulado'; $subirLogisticaNoteClass = 'small mt-2 text-success fw-semibold'; $subirLogisticaNoteHtml = 'En Pedidos Rotulados #' . (int) $order['pedido_rotulado_pedido_id'] . '. Si cambias algo, usa este botón para actualizarlo.'; } else { $subirLogisticaButtonLabel = 'Subir a rotulados'; $subirLogisticaNoteClass = 'small mt-2 text-warning-emphasis'; $subirLogisticaNoteHtml = 'PENDIENTE A SUBIR: este pedido ya está en CONFIRMADO ENVIO. Súbelo a Pedidos Rotulados con este botón.'; } } } ob_start(); ?>
N° Pedido Cliente Ubicación editable Pedido Gestión Acciones
No hay pedidos en esta bandeja por ahora.
ID Drive:
Celular:
DNI:
Dirección:
Referencia:
Departamento:
Provincia:
Distrito:
Coordenadas:
DISTRITO 1:
Cantidad: ·
Ingreso Drive:
Observación Drive:
llamadas Seguimiento
Número de cuenta enviado hace día ·
Seguimiento: · desde
Próxima llamada:
Entrega programada:
Subido a Ruta Contraentrega # ·
Subido a Pedidos Rotulados # ·
Última gestión:
Nota:
0 ? (int) $orderUserId : null) : null; $unassigned = $orderUserIdNorm === null; $assignedAssessorKey = null; if (!$unassigned) { foreach ($assessors as $assessorKey => $assessor) { if ((int) ($assessor['id'] ?? 0) === (int) $orderUserIdNorm) { $assignedAssessorKey = (string) $assessorKey; break; } } } $assignFormClass = 'border rounded-3 p-1 text-start shadow-sm cc-callcenter-assign-form'; $assignFormStyle = ''; if ($unassigned) { $assignFormClass .= ' bg-light'; } elseif ($assignedAssessorKey !== null) { $assignFormClass .= ' has-accent'; $assignFormStyle = cc_test_assessor_css_vars($assignedAssessorKey, $assessors[$assignedAssessorKey]['color_hex'] ?? null); } else { $assignFormClass .= ' bg-secondary-subtle border-secondary'; } $selectedAssessorKeyForSelect = $unassigned ? '' : ($assignedAssessorKey !== null ? (string) $assignedAssessorKey : ''); $buttonLabel = $unassigned ? 'Asignar pedido' : ($assignedAssessorKey !== null ? 'Guardar asignación' : 'Desasignar'); ?>
>
Asignar a asesora
Sin asignar Asignado
WhatsApp
Llamada preparada para AirDroid

Número listo para copiar

Pedido
-
Cliente
-
Número
-