Autosave: 20260724-052546

This commit is contained in:
Flatlogic Bot 2026-07-24 05:24:22 +00:00
parent 4a8c7a39ad
commit cfd8e99b49
5 changed files with 127 additions and 20 deletions

View File

@ -1,5 +1,8 @@
<?php
session_start();
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
require_once 'db/config.php';
require_once 'includes/callcenter_test_helpers.php';
require_once 'includes/drive_test_orders.php';
@ -415,7 +418,7 @@ $selectedAssessorFilterLabel = '';
$totalRows = 0;
$agregadosCount = 0;
$lastProcessedRow = null;
$startRow = (int) ($storeConfig['startRow'] ?? 5552);
$startRow = (int) ($storeConfig['startRow'] ?? 0);
$catalogoProductos = [];
$stats = [
'total' => 0,
@ -627,8 +630,10 @@ try {
$role = (string) ($_SESSION['user_role'] ?? '');
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
$selectedAssessorFilter = cc_test_normalize_user_key($_GET['assessor'] ?? '');
if (!$isAdmin || ($selectedAssessorFilter !== '' && !isset($assessors[$selectedAssessorFilter]))) {
if ($isAdmin) {
$selectedAssessorFilter = cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, true, true);
} else {
unset($_SESSION[cc_test_callcenter_assessor_filter_session_key()]);
$selectedAssessorFilter = '';
}
$selectedAssessorFilterLabel = $selectedAssessorFilter !== ''
@ -1151,7 +1156,7 @@ require_once 'layout_header.php';
<div class="col-12 col-lg-4">
<label for="assessorFilter" class="form-label small text-uppercase text-muted fw-semibold mb-1">Filtrar por asesora</label>
<select name="assessor" id="assessorFilter" class="form-select">
<option value="">Todas las asesoras</option>
<option value="TODAS"<?php echo $selectedAssessorFilter === "" ? ' selected' : ''; ?>>Todas las asesoras</option>
<?php foreach ($assessors as $assessorKey => $assessor): ?>
<option value="<?php echo htmlspecialchars((string) $assessorKey); ?>"<?php echo $selectedAssessorFilter === $assessorKey ? ' selected' : ''; ?>><?php echo htmlspecialchars((string) ($assessor['label'] ?? $assessorKey)); ?></option>
<?php endforeach; ?>
@ -1161,7 +1166,7 @@ require_once 'layout_header.php';
<div class="col-auto d-flex gap-2">
<button type="submit" class="btn btn-primary">Aplicar filtro</button>
<?php if ($selectedAssessorFilter !== ''): ?>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', $callCenterBaseParams)); ?>" class="btn btn-outline-secondary">Ver todas</a>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', array_merge($callCenterBaseParams, ['assessor' => 'TODAS']))); ?>" class="btn btn-outline-secondary">Ver todas</a>
<?php endif; ?>
</div>

View File

@ -1,5 +1,8 @@
<?php
session_start();
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
require_once 'db/config.php';
require_once 'includes/callcenter_test_helpers.php';
require_once 'includes/drive_test_orders.php';
@ -321,6 +324,9 @@ $modalsHtml = [];
$totalRows = 0;
$agregadosCount = 0;
$lastProcessedRow = null;
$selectedAssessorFilter = '';
$selectedAssessorFilterLabel = '';
$persistedAssessorParams = [];
$repartoSettings = [
'store_key' => $storeKey,
@ -350,7 +356,7 @@ $cupoObjetivoPorAsesora = 10;
$advisorOpenCounts = [];
$advisorTotalCounts = [];
$recommendedAssessorKey = null;
$startRow = (int) ($storeConfig['startRow'] ?? 5552);
$startRow = (int) ($storeConfig['startRow'] ?? 0);
$catalogoProductos = [];
$stats = [
'total' => 0,
@ -369,6 +375,16 @@ try {
cc_test_ensure_tracking_table($pdo);
cc_test_ensure_historial_llamadas_table($pdo);
$assessors = cc_test_fetch_assessors($pdo);
$selectedAssessorFilter = cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, true, true);
$selectedAssessorFilterLabel = $selectedAssessorFilter !== ''
? (string) ($assessors[$selectedAssessorFilter]['label'] ?? $selectedAssessorFilter)
: '';
$persistedAssessorParams = $selectedAssessorFilter !== ''
? ['assessor' => $selectedAssessorFilter]
: [];
if ($selectedAssessorFilterLabel !== '') {
$pageTitle = 'Base de Datos Pedidos | Asignación · ' . $selectedAssessorFilterLabel;
}
if ($isMainTuaniStore) {
$repartoSettings = cc_test_fetch_reparto_settings($pdo, $storeKey, $assessors);
}
@ -866,6 +882,15 @@ try {
$order['es_nuevo_hoy'] = $firstSeenDate ? $firstSeenDate->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);
$order['assessor_key'] = cc_test_dashboard_bucket_key($order, $assessors, false);
$order['assessor_label'] = $order['assessor_key'] !== ''
? (string) ($assessors[$order['assessor_key']]['label'] ?? $order['assessor_key'])
: 'Sin asignar';
$order['matches_assessor_filter'] = $selectedAssessorFilter === '' || $order['assessor_key'] === $selectedAssessorFilter;
if ($selectedAssessorFilter !== '' && !($order['matches_assessor_filter'] ?? false)) {
continue;
}
if ($order['user_id'] !== null) {
$userId = (int) $order['user_id'];
@ -907,7 +932,13 @@ try {
$orders = cc_test_mark_recoverables_duplicate_orders($pdo, $orders, $storeKey);
$performanceDashboard = cc_test_build_performance_dashboard($orders, $assessors, 7);
$performanceOrders = $selectedAssessorFilter !== ''
? array_values(array_filter($orders, static function (array $order) use ($selectedAssessorFilter): bool {
return ($order['matches_assessor_filter'] ?? false) === true;
}))
: $orders;
$performanceDashboard = cc_test_build_performance_dashboard($performanceOrders, $assessors, 7);
$performanceDashboardHtml = cc_test_render_performance_dashboard($performanceDashboard, [
'title' => 'KPI diario por asesora',
'subtitle' => '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.',
@ -985,7 +1016,7 @@ try {
];
}
$visibleOrders = array_values(array_filter($orders, static function (array $order) use ($view, $todayStart): bool {
$visibleOrders = array_values(array_filter($performanceOrders, static function (array $order) use ($view, $todayStart): bool {
return match ($view) {
'pendientes_hoy' => (bool) ($order['es_pendiente_hoy'] ?? false),
'nuevos_hoy' => (bool) ($order['es_nuevo_hoy'] ?? false),
@ -1330,14 +1361,14 @@ require_once 'layout_header.php';
<div>
<h1 class="h2 fw-bold mb-1"><i class="bi bi-headset text-primary"></i> Base de Datos Pedidos</h1>
<div class="d-flex flex-wrap gap-2 mt-3">
<a href="?view=pendientes_hoy&store=<?php echo htmlspecialchars($storeKey); ?>" class="btn btn-sm <?php echo $view !== 'todos' ? 'btn-primary' : 'btn-outline-primary'; ?>">Bandeja principal</a>
<a href="call_center_pro.php?view=<?php echo urlencode($view); ?>&store=<?php echo htmlspecialchars($storeKey); ?>" class="btn btn-sm btn-outline-dark"><?php echo htmlspecialchars($callCenterSectionLabel); ?></a>
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => 'pendientes_hoy', 'store' => $storeKey], $persistedAssessorParams))); ?>" class="btn btn-sm <?php echo $view !== 'todos' ? 'btn-primary' : 'btn-outline-primary'; ?>">Bandeja principal</a>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', array_merge(['view' => $view, 'store' => $storeKey], $persistedAssessorParams))); ?>" class="btn btn-sm btn-outline-dark"><?php echo htmlspecialchars($callCenterSectionLabel); ?></a>
</div>
<ul class="nav nav-pills mt-3 flex-wrap gap-2" role="tablist" aria-label="Selector de tienda">
<?php foreach ($storeNavGroups as $groupKey => $group): ?>
<?php $groupConfig = $group['config'] ?? []; ?>
<li class="nav-item">
<a class="nav-link <?php echo $activeStoreGroupKey === $groupKey ? 'active' : ''; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['view' => $view, 'store' => $groupKey])); ?>">
<a class="nav-link <?php echo $activeStoreGroupKey === $groupKey ? 'active' : ''; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $view, 'store' => $groupKey], $persistedAssessorParams))); ?>">
<?php echo htmlspecialchars((string) ($groupConfig['label'] ?? strtoupper((string) $groupKey))); ?>
</a>
</li>
@ -1347,9 +1378,9 @@ require_once 'layout_header.php';
<div class="mt-3 p-3 rounded-4 border bg-light">
<div class="small text-uppercase text-muted fw-semibold mb-2">Dentro de <?php echo htmlspecialchars($activeStoreGroupLabel); ?></div>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-sm <?php echo $storeKey === $activeStoreGroupKey ? 'btn-primary' : 'btn-outline-primary'; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['view' => $view, 'store' => $activeStoreGroupKey])); ?>">Pedidos <?php echo htmlspecialchars($activeStoreGroupLabel); ?></a>
<a class="btn btn-sm <?php echo $storeKey === $activeStoreGroupKey ? 'btn-primary' : 'btn-outline-primary'; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $view, 'store' => $activeStoreGroupKey], $persistedAssessorParams))); ?>">Pedidos <?php echo htmlspecialchars($activeStoreGroupLabel); ?></a>
<?php foreach ($activeStoreGroupChildren as $childKey => $childConfig): ?>
<a class="btn btn-sm <?php echo $storeKey === $childKey ? 'btn-primary' : 'btn-outline-primary'; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['view' => $view, 'store' => $childKey])); ?>">
<a class="btn btn-sm <?php echo $storeKey === $childKey ? 'btn-primary' : 'btn-outline-primary'; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $view, 'store' => $childKey], $persistedAssessorParams))); ?>">
<?php echo htmlspecialchars((string) ($childConfig['label'] ?? strtoupper((string) $childKey))); ?>
</a>
<?php endforeach; ?>
@ -1362,6 +1393,10 @@ require_once 'layout_header.php';
<span class="badge rounded-pill text-bg-light border px-3 py-2">Agregados: <?php echo (int) $agregadosCount; ?></span>
<span class="badge rounded-pill text-bg-light border px-3 py-2"><?php if ($usesIncrementalSync && $lastProcessedRow !== null): ?><?php echo htmlspecialchars(mb_strtoupper($storeLabel)); ?>: distribución desde fila <?php echo (int) ($storeConfig['startRow'] ?? $startRow); ?> · último procesado <?php echo (int) $lastProcessedRow; ?><?php else: ?>Extracción: desde fila <?php echo (int) $startRow; ?><?php endif; ?></span>
<span class="badge rounded-pill text-bg-light border px-3 py-2">Auto actualización: cada 10 min</span>
<?php if ($selectedAssessorFilterLabel !== ''): ?>
<span class="badge rounded-pill bg-primary-subtle text-primary-emphasis border px-3 py-2">Asesora: <?php echo htmlspecialchars($selectedAssessorFilterLabel); ?></span>
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['view' => $view, 'store' => $storeKey, 'assessor' => 'TODAS'])); ?>" class="btn btn-outline-secondary btn-sm">Ver todas</a>
<?php endif; ?>
<a href="test_importar_drive.php?store=<?php echo htmlspecialchars($storeKey); ?>" class="btn btn-outline-primary btn-sm">Ver vista previa Drive</a>
</div>
</div>
@ -1429,7 +1464,7 @@ require_once 'layout_header.php';
<?php $cardClass = $isActiveCard ? $viewCard['active_class'] : 'bg-white'; ?>
<?php $labelClass = $isActiveCard && str_contains((string) ($viewCard['active_class'] ?? ''), 'text-white') ? 'opacity-75' : 'text-muted'; ?>
<div class="<?php echo htmlspecialchars($viewCardColumnClass); ?>">
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['view' => $viewKey, 'store' => $storeKey])); ?>" class="text-decoration-none">
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $viewKey, 'store' => $storeKey], $persistedAssessorParams))); ?>" class="text-decoration-none">
<article class="card border-0 shadow-sm h-100 <?php echo htmlspecialchars($cardClass); ?>">
<div class="card-body">
<div class="small text-uppercase <?php echo htmlspecialchars($labelClass); ?> mb-2"><?php echo htmlspecialchars((string) ($viewCard['label'] ?? $viewKey)); ?></div>

View File

@ -812,6 +812,53 @@ function cc_test_normalize_user_key(?string $value): string
return mb_strtoupper($value);
}
function cc_test_callcenter_assessor_filter_session_key(): string
{
return 'callcenter_assessor_filter';
}
function cc_test_callcenter_assessor_filter_clear_values(): array
{
return ['ALL', 'TODAS', 'TODOS', '__ALL__'];
}
function cc_test_resolve_callcenter_assessor_filter(array $assessors, ?string $incomingFilter = null, bool $useSessionFallback = true, bool $persistSession = true): string
{
$sessionKey = cc_test_callcenter_assessor_filter_session_key();
$incomingFilter = cc_test_normalize_user_key($incomingFilter ?? '');
$sessionFilter = cc_test_normalize_user_key($_SESSION[$sessionKey] ?? '');
if ($incomingFilter !== '') {
if (in_array($incomingFilter, cc_test_callcenter_assessor_filter_clear_values(), true)) {
unset($_SESSION[$sessionKey]);
return '';
}
if (isset($assessors[$incomingFilter])) {
if ($persistSession) {
$_SESSION[$sessionKey] = $incomingFilter;
}
return $incomingFilter;
}
if ($persistSession) {
unset($_SESSION[$sessionKey]);
}
return '';
}
if ($useSessionFallback && $sessionFilter !== '' && isset($assessors[$sessionFilter])) {
return $sessionFilter;
}
if (!$persistSession) {
unset($_SESSION[$sessionKey]);
}
return '';
}
function cc_test_allowed_module_user_keys(): array
{
return cc_test_default_assessor_keys();

View File

@ -29,8 +29,8 @@ function drive_test_available_stores(): array
'sheet_title' => 'TUANI PEDIDOS',
'spreadsheet_id' => '1QYKeBJIIqYm6yW6Ka-5gKdxhUHwXOAc0No7RjnimxTw',
'sheet_gid' => 1523126328,
'startRow' => 5552,
'enforce_db_start_row' => false,
'startRow' => 6804,
'enforce_db_start_row' => true,
'incremental_sync' => true,
],
// Bandeja compartida de carritos recuperables: misma estructura, pero desde la fila 2000.
@ -202,9 +202,12 @@ function drive_test_extract_codigo_number(?string $codigo): ?int
return (int) $digits;
}
function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $storeKey = 'flower'): array
function drive_test_fetch_orders(int $limit = 10, ?int $startRow = null, string $storeKey = 'flower'): array
{
$storeConfig = drive_test_get_store_config($storeKey);
if ($startRow === null || $startRow <= 0) {
$startRow = (int) ($storeConfig['startRow'] ?? 0);
}
$credentialsPath = __DIR__ . '/../google_credentials.json';
$spreadsheetId = (string) ($storeConfig['spreadsheet_id'] ?? '');
@ -612,7 +615,7 @@ function drive_test_sync_orders_incremental(PDO $pdo, string $storeKey, int $ove
{
$storeKey = mb_strtolower(trim($storeKey));
$storeConfig = drive_test_get_store_config($storeKey);
$baselineStartRow = (int) ($storeConfig['startRow'] ?? 5552);
$baselineStartRow = (int) ($storeConfig['startRow'] ?? 0);
drive_test_ensure_orders_table($pdo);
drive_test_ensure_import_checkpoints_table($pdo);

View File

@ -33,6 +33,23 @@ if ($currentUserId > 0 && !in_array($userRole, ['Administrador', 'admin'], true)
}
$canAccessTestModule = cc_test_is_allowed_module_user($userRole, $currentUserUsername, $currentUserNombreAsesor);
$currentAssessorFilter = cc_test_normalize_user_key($_GET['assessor'] ?? ($_SESSION[cc_test_callcenter_assessor_filter_session_key()] ?? ''));
if (in_array($currentAssessorFilter, cc_test_callcenter_assessor_filter_clear_values(), true)) {
$currentAssessorFilter = '';
}
$shouldPersistAssessor = $currentAssessorFilter !== '';
$callCenterPersistentParams = $shouldPersistAssessor ? ['assessor' => $currentAssessorFilter] : [];
$appendCallCenterParams = static function (string $url) use ($callCenterPersistentParams): string {
if (empty($callCenterPersistentParams)) {
return $url;
}
if (!in_array($url, ['gestiones_callcenter.php', 'call_center_pro.php'], true)) {
return $url;
}
return $url . '?' . http_build_query($callCenterPersistentParams);
};
// Define navigation items for each role
$navItems = [
@ -451,7 +468,7 @@ $navItems = [
$isActive = $currentPage == $sub_item['url'] ? 'active' : '';
?>
<li class="nav-item">
<a href="<?php echo $sub_item['url']; ?>" class="nav-link <?php echo $isActive; ?>"><i class="fas <?php echo $sub_item['icon']; ?>"></i> <?php echo $sub_item['text']; ?></a>
<a href="<?php echo htmlspecialchars($appendCallCenterParams($sub_item['url'])); ?>" class="nav-link <?php echo $isActive; ?>"><i class="fas <?php echo $sub_item['icon']; ?>"></i> <?php echo $sub_item['text']; ?></a>
</li>
<?php
endif;
@ -462,7 +479,7 @@ $navItems = [
$isActive = $currentPage == $item['url'] ? 'active' : '';
?>
<li class="nav-item">
<a href="<?php echo $item['url']; ?>" class="nav-link <?php echo $isActive; ?>"><i class="fas <?php echo $item['icon']; ?>"></i> <?php echo $item['text']; ?></a>
<a href="<?php echo htmlspecialchars($appendCallCenterParams($item['url'])); ?>" class="nav-link <?php echo $isActive; ?>"><i class="fas <?php echo $item['icon']; ?>"></i> <?php echo $item['text']; ?></a>
</li>
<?php endif;
endif;