Autosave: 20260807-043717

This commit is contained in:
Flatlogic Bot 2026-08-07 04:35:31 +00:00
parent 87af7beac5
commit 288a7b0cd7
3 changed files with 83 additions and 17 deletions

View File

@ -350,6 +350,12 @@ $viewCards = $isTuaniRecuperablesStore ? [
'stat_key' => 'pendientes_hoy',
'active_class' => 'bg-dark text-white',
],
'nuevos_hoy' => [
'label' => 'Nuevos hoy',
'heading' => 'Nuevos hoy',
'stat_key' => 'nuevos_hoy',
'active_class' => 'bg-primary-subtle border border-primary',
],
'confirmados' => [
'label' => 'Confirmados',
'heading' => 'Confirmados',
@ -458,7 +464,7 @@ $_SESSION[cc_test_callcenter_store_session_key()] = cc_test_normalize_context_ke
$_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key($view);
$viewStatesNote = $isTuaniRecuperablesStore
? 'Estados disponibles: <strong>Carritos recuperables</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. 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 <strong>TUANI principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja. Las vistas <strong>Carritos recuperables</strong> y <strong>Todos</strong> arrancan en la fila 2000, solo toman pedidos desde esa fila y se ordenan por el número <strong>#D</strong> más alto, lo que mantiene arriba el código más reciente, al más antiguo, en bloques de 200 pedidos.'
? 'Estados disponibles: <strong>Carritos recuperables</strong>, <strong>Nuevos hoy</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. La vista <strong>Nuevos hoy</strong> agrupa los pedidos más recientes agregados desde Drive. 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 <strong>TUANI principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja. Las vistas <strong>Carritos recuperables</strong> y <strong>Todos</strong> arrancan en la fila 2000, solo toman pedidos desde esa fila y se ordenan por el número <strong>#D</strong> más alto, lo que mantiene arriba el código más reciente, al más antiguo, en bloques de 200 pedidos.'
: 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. <strong>Promo Final</strong> se habilita para mover el pedido desde el Día 4; la imagen de sustento se puede cargar desde el Día 3 para dejarlo listo antes del pase. 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 <strong>Últimos 4 días hábiles</strong> para concentrar el seguimiento reciente.';
$errorMessage = null;
@ -714,7 +720,9 @@ try {
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
if ($isAdmin) {
$selectedAssessorFilter = cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, !$isTuaniRecuperablesStore, true);
$selectedAssessorFilter = $isTuaniRecuperablesStore
? ''
: cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, !$isTuaniRecuperablesStore, true);
} else {
unset($_SESSION[cc_test_callcenter_assessor_filter_session_key()]);
$selectedAssessorFilter = '';
@ -734,6 +742,21 @@ try {
$openStates = cc_test_open_states();
$closedStates = cc_test_closed_states();
$recoverablesRecentSourceRowThreshold = null;
if ($isTuaniRecuperablesStore) {
$recoverablesMaxSourceRow = 0;
foreach ($orders as $recoverablesThresholdOrder) {
$recoverablesSourceRow = (int) ($recoverablesThresholdOrder['source_row'] ?? 0);
if ($recoverablesSourceRow > $recoverablesMaxSourceRow) {
$recoverablesMaxSourceRow = $recoverablesSourceRow;
}
}
if ($recoverablesMaxSourceRow > 0) {
$recoverablesRecentSourceRowThreshold = max(0, $recoverablesMaxSourceRow - 4);
}
}
foreach ($orders as &$order) {
$order['estado'] = cc_test_normalize_state((string) ($order['estado'] ?? ''));
$order['total_llamadas'] = (int) ($callCounts[$order['source_key']] ?? 0);
@ -752,7 +775,12 @@ try {
$order['pendiente_logistica_destino'] = cc_test_is_tuani_store_key($storeKey) ? 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;
if ($isTuaniRecuperablesStore) {
$order['es_nuevo_hoy'] = $recoverablesRecentSourceRowThreshold !== null
&& (int) ($order['source_row'] ?? 0) >= $recoverablesRecentSourceRowThreshold;
} else {
$order['es_nuevo_hoy'] = $assignedDate ? $assignedDate->format('Y-m-d') === $todayStart->format('Y-m-d') : false;
}
$order['es_ultimos_3_dias'] = cc_test_is_within_last_days_window($order, $todayStart, 4);
$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);
@ -1371,7 +1399,7 @@ require_once 'layout_header.php';
<?php if ($isTuaniRecuperablesStore): ?>
<span class="badge rounded-pill bg-success-subtle text-success-emphasis border px-3 py-2">Solo pedidos válidos: IDs #D...</span>
<?php else: ?>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', ['store' => 'tuani_recuperables', 'view' => 'todos'])); ?>" class="btn btn-sm btn-outline-success">Abrir Carritos recuperables (#D...)</a>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', ['store' => 'tuani_recuperables', 'view' => 'nuevos_hoy'])); ?>" class="btn btn-sm btn-outline-success">Abrir Carritos recuperables (#D...)</a>
<?php endif; ?>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
@ -1384,7 +1412,7 @@ require_once 'layout_header.php';
<?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('call_center_pro.php', array_merge($callCenterParams, ['view' => $groupKey === 'tuani_recuperables' ? 'todos' : $view, 'store' => $groupKey]))); ?>">
<a class="nav-link <?php echo $activeStoreGroupKey === $groupKey ? 'active' : ''; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', array_merge($callCenterParams, ['view' => $groupKey === 'tuani_recuperables' ? 'nuevos_hoy' : $view, 'store' => $groupKey]))); ?>">
<?php echo htmlspecialchars((string) ($groupConfig['label'] ?? strtoupper((string) $groupKey))); ?>
</a>
</li>
@ -1394,9 +1422,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('call_center_pro.php', array_merge($callCenterParams, ['view' => $activeStoreGroupKey === 'tuani_recuperables' ? 'todos' : $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('call_center_pro.php', array_merge($callCenterParams, ['view' => $activeStoreGroupKey === 'tuani_recuperables' ? 'nuevos_hoy' : $view, 'store' => $activeStoreGroupKey]))); ?>">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('call_center_pro.php', array_merge($callCenterParams, ['view' => $childKey === 'tuani_recuperables' ? 'todos' : $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('call_center_pro.php', array_merge($callCenterParams, ['view' => $childKey === 'tuani_recuperables' ? 'nuevos_hoy' : $view, 'store' => $childKey]))); ?>">
<?php echo htmlspecialchars((string) ($childConfig['label'] ?? strtoupper((string) $childKey))); ?>
</a>
<?php endforeach; ?>
@ -3571,7 +3599,12 @@ syncServerAssessorSelects();
ccCallCenterPanelBindDirtyTracking();
ccCallCenterPanelRestoreScroll();
window.addEventListener('beforeunload', ccCallCenterPanelSaveScroll);
window.addEventListener('pageshow', function () {
window.addEventListener('pageshow', function (event) {
if (event && event.persisted) {
window.location.reload();
return;
}
syncServerAssessorSelects();
ccCallCenterPanelRestoreScroll();
ccCallCenterPanelCheckForUpdates();

View File

@ -247,6 +247,12 @@ $viewCards = $isTuaniRecuperablesStore ? [
'stat_key' => 'pendientes_hoy',
'active_class' => 'bg-dark text-white',
],
'nuevos_hoy' => [
'label' => 'Nuevos hoy',
'heading' => 'Nuevos hoy',
'stat_key' => 'nuevos_hoy',
'active_class' => 'bg-primary-subtle border border-primary',
],
'confirmados' => [
'label' => 'Confirmados',
'heading' => 'Confirmados',
@ -349,7 +355,7 @@ $_SESSION[cc_test_callcenter_store_session_key()] = cc_test_normalize_context_ke
$_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key($view);
$viewStatesNote = $isTuaniRecuperablesStore
? 'Estados disponibles: <strong>Carritos recuperables</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. 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 <strong>TUANI principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja. Las vistas <strong>Carritos recuperables</strong> y <strong>Todos</strong> arrancan en la fila 2000, solo toman pedidos desde esa fila y se ordenan por el número <strong>#D</strong> más alto, lo que mantiene arriba el código más reciente, al más antiguo, en bloques de 200 pedidos.'
? 'Estados disponibles: <strong>Carritos recuperables</strong>, <strong>Nuevos hoy</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. La vista <strong>Nuevos hoy</strong> agrupa los pedidos más recientes agregados desde Drive. 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 <strong>TUANI principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja. Las vistas <strong>Carritos recuperables</strong> y <strong>Todos</strong> arrancan en la fila 2000, solo toman pedidos desde esa fila y se ordenan por el número <strong>#D</strong> más alto, lo que mantiene arriba el código más reciente, al más antiguo, en bloques de 200 pedidos.'
: 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. <strong>Promo Final</strong> se habilita para mover el pedido desde el Día 4; la imagen de sustento se puede cargar desde el Día 3 para dejarlo listo. En las vistas <strong>Todos</strong> y <strong>Últimos 4 días hábiles</strong>, la llamada y la gestión siguen disponibles desde el panel.';
$errorMessage = null;
@ -420,7 +426,9 @@ 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, false, false);
$selectedAssessorFilter = $isTuaniRecuperablesStore
? ''
: cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, false, false);
$selectedAssessorFilterLabel = $selectedAssessorFilter !== ''
? (string) ($assessors[$selectedAssessorFilter]['label'] ?? $selectedAssessorFilter)
: '';
@ -902,6 +910,21 @@ try {
$openStates = cc_test_open_states();
$closedStates = cc_test_closed_states();
$recoverablesRecentSourceRowThreshold = null;
if ($isTuaniRecuperablesStore) {
$recoverablesMaxSourceRow = 0;
foreach ($orders as $recoverablesThresholdOrder) {
$recoverablesSourceRow = (int) ($recoverablesThresholdOrder['source_row'] ?? 0);
if ($recoverablesSourceRow > $recoverablesMaxSourceRow) {
$recoverablesMaxSourceRow = $recoverablesSourceRow;
}
}
if ($recoverablesMaxSourceRow > 0) {
$recoverablesRecentSourceRowThreshold = max(0, $recoverablesMaxSourceRow - 4);
}
}
$advisorOpenCounts = [];
$advisorTotalCounts = [];
foreach ($assessors as $assessor) {
@ -930,7 +953,12 @@ try {
$order['pendiente_logistica_destino'] = cc_test_is_tuani_store_key($storeKey) ? cc_test_pending_logistica_destination($order) : null;
$order['pendiente_logistica'] = $order['pendiente_logistica_destino'] !== null;
$order['es_nuevo_hoy'] = $firstSeenDate ? $firstSeenDate->format('Y-m-d') === $todayStart->format('Y-m-d') : false;
if ($isTuaniRecuperablesStore) {
$order['es_nuevo_hoy'] = $recoverablesRecentSourceRowThreshold !== null
&& (int) ($order['source_row'] ?? 0) >= $recoverablesRecentSourceRowThreshold;
} else {
$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);
@ -1627,7 +1655,7 @@ require_once 'layout_header.php';
<?php if ($isTuaniRecuperablesStore): ?>
<span class="badge rounded-pill bg-success-subtle text-success-emphasis border px-3 py-2">Solo pedidos válidos: IDs #D...</span>
<?php else: ?>
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['store' => 'tuani_recuperables', 'view' => 'todos'])); ?>" class="btn btn-sm btn-outline-success">Abrir Carritos recuperables (#D...)</a>
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', ['store' => 'tuani_recuperables', 'view' => 'nuevos_hoy'])); ?>" class="btn btn-sm btn-outline-success">Abrir Carritos recuperables (#D...)</a>
<?php endif; ?>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
@ -1638,7 +1666,7 @@ require_once 'layout_header.php';
<?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', array_merge(['view' => $groupKey === 'tuani_recuperables' ? 'todos' : $view, 'store' => $groupKey], $persistedAssessorParams))); ?>">
<a class="nav-link <?php echo $activeStoreGroupKey === $groupKey ? 'active' : ''; ?>" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $groupKey === 'tuani_recuperables' ? 'nuevos_hoy' : $view, 'store' => $groupKey], $persistedAssessorParams))); ?>">
<?php echo htmlspecialchars((string) ($groupConfig['label'] ?? strtoupper((string) $groupKey))); ?>
</a>
</li>
@ -1648,9 +1676,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', array_merge(['view' => $activeStoreGroupKey === 'tuani_recuperables' ? 'todos' : $view, 'store' => $activeStoreGroupKey], $persistedAssessorParams))); ?>">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' => $activeStoreGroupKey === 'tuani_recuperables' ? 'nuevos_hoy' : $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', array_merge(['view' => $childKey === 'tuani_recuperables' ? 'todos' : $view, 'store' => $childKey], $persistedAssessorParams))); ?>">
<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' => $childKey === 'tuani_recuperables' ? 'nuevos_hoy' : $view, 'store' => $childKey], $persistedAssessorParams))); ?>">
<?php echo htmlspecialchars((string) ($childConfig['label'] ?? strtoupper((string) $childKey))); ?>
</a>
<?php endforeach; ?>
@ -4093,7 +4121,12 @@ document.querySelectorAll('.cc-callcenter-assign-form').forEach(function (form)
}
});
window.addEventListener('pageshow', function () {
window.addEventListener('pageshow', function (event) {
if (event && event.persisted) {
window.location.reload();
return;
}
syncAllAssessorFormsFromServer();
});
window.addEventListener('load', function () {

View File

@ -275,7 +275,7 @@ function cc_test_callcenter_pro_default_view_key(string $storeKey): string
}
if (cc_test_is_tuani_recoverables_store_key($storeKey)) {
return 'todos';
return 'nuevos_hoy';
}
return 'pendientes_hoy';