Autosave: 20260807-073833

This commit is contained in:
Flatlogic Bot 2026-08-07 07:36:47 +00:00
parent 9f2ee910f3
commit dd197310a1
8 changed files with 146 additions and 78 deletions

View File

@ -326,6 +326,12 @@ if (!isset($availableStores[$storeKey])) {
}
$storeConfig = $availableStores[$storeKey];
$storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey));
$recoverablesStoreKey = trim((string) ($storeConfig['recoverables_store_key'] ?? ''));
$recoverablesStoreLabel = $recoverablesStoreKey !== '' && isset($availableStores[$recoverablesStoreKey]) ? (string) ($availableStores[$recoverablesStoreKey]['label'] ?? strtoupper($recoverablesStoreKey)) : '';
$recoverablesMainStoreKey = trim((string) ($storeConfig['main_store_key'] ?? ''));
$recoverablesMainStoreLabel = $recoverablesMainStoreKey !== '' && isset($availableStores[$recoverablesMainStoreKey]) ? (string) ($availableStores[$recoverablesMainStoreKey]['label'] ?? strtoupper($recoverablesMainStoreKey)) : $storeLabel;
$recoverablesStoreLabelHtml = htmlspecialchars($storeLabel);
$recoverablesMainStoreLabelHtml = htmlspecialchars($recoverablesMainStoreLabel);
$usesIncrementalSync = !empty($storeConfig['incremental_sync']);
$storeNav = drive_test_store_navigation_state($availableStores, $storeKey);
$storeNavGroups = $storeNav['groups'];
@ -335,7 +341,7 @@ $activeStoreGroupChildren = $storeNav['active_group_children'] ?? [];
$parentStoreLabel = (string) ($storeNav['parent_label'] ?? '');
$isMainTuaniStore = cc_test_is_tuani_main_store_key($storeKey);
$isTuaniRecuperablesStore = $storeKey === 'tuani_recuperables';
$isTuaniRecuperablesStore = cc_test_is_recoverables_store_key($storeKey);
if ($isTuaniRecuperablesStore && trim((string) ($_GET['view'] ?? '')) === '') {
$view = 'todos';
}
@ -347,17 +353,11 @@ $viewCardColumnClass = $isTuaniRecuperablesStore ? 'col-md-6 col-xl-2' : 'col-md
$defaultView = cc_test_callcenter_pro_default_view_key($storeKey);
$viewCards = $isTuaniRecuperablesStore ? [
'pendientes_hoy' => [
'label' => 'Carritos recuperables',
'heading' => 'Carritos recuperables',
'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',
'todos' => [
'label' => 'Todos',
'heading' => 'Todos los pedidos cargados',
'stat_key' => 'total',
'active_class' => 'bg-light border',
],
'confirmados' => [
'label' => 'Confirmados',
@ -383,11 +383,11 @@ $viewCards = $isTuaniRecuperablesStore ? [
'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' => $storeLabel,
'heading' => $storeLabel,
'stat_key' => 'pendientes_hoy',
'active_class' => 'bg-dark text-white',
],
] : [
'nuevos_hoy' => [
@ -467,7 +467,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>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: <strong>' . $recoverablesStoreLabelHtml . '</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. Esta bandeja abre por defecto en <strong>Todos</strong>. La 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>' . $recoverablesMainStoreLabelHtml . ' principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja. Las vistas <strong>' . $recoverablesStoreLabelHtml . '</strong> y <strong>Todos</strong> arrancan en la fila ' . (int) ($storeConfig['startRow'] ?? 0) . ', 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;
@ -922,7 +922,7 @@ try {
$aImport = cc_test_import_time($a);
$bImport = cc_test_import_time($b);
if (cc_test_is_tuani_recoverables_store_key($storeKey) && in_array($view, ['pendientes_hoy', 'todos'], true)) {
if (cc_test_is_recoverables_store_key($storeKey) && in_array($view, ['pendientes_hoy', 'todos'], true)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
@ -1042,7 +1042,7 @@ try {
}
if ($view === 'todos') {
if (cc_test_is_tuani_recoverables_store_key($storeKey)) {
if (cc_test_is_recoverables_store_key($storeKey)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
@ -1179,14 +1179,14 @@ if (!empty($_GET['ajax_refresh'])) {
}
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.';
$pageTitle = $storeLabel . ' | Bandeja compartida';
$pageDescription = 'Bandeja compartida para que cualquier asesora gestione ' . mb_strtolower($storeLabel) . ' 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 vistas de hoy, últimos 4 días hábiles, estados, historial y acciones.';
}
$callCenterSectionLabel = $isTuaniRecuperablesStore ? 'Carritos recuperables' : 'Pedidos Asignados';
$callCenterSectionLabel = $isTuaniRecuperablesStore ? $storeLabel : 'Pedidos Asignados';
require_once 'layout_header.php';
?>
@ -1399,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' => 'nuevos_hoy'])); ?>" 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' => $recoverablesStoreKey, 'view' => 'todos'])); ?>" class="btn btn-sm btn-outline-success">Abrir <?php echo htmlspecialchars($recoverablesStoreLabel !== '' ? $recoverablesStoreLabel : 'Carritos recuperables'); ?> (#D...)</a>
<?php endif; ?>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
@ -1412,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' ? 'nuevos_hoy' : $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' => cc_test_is_recoverables_store_key($groupKey) ? 'todos' : $view, 'store' => $groupKey]))); ?>">
<?php echo htmlspecialchars((string) ($groupConfig['label'] ?? strtoupper((string) $groupKey))); ?>
</a>
</li>
@ -1422,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' ? 'nuevos_hoy' : $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' => cc_test_is_recoverables_store_key($activeStoreGroupKey) ? 'todos' : $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' ? 'nuevos_hoy' : $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' => cc_test_is_recoverables_store_key($childKey) ? 'todos' : $view, 'store' => $childKey]))); ?>">
<?php echo htmlspecialchars((string) ($childConfig['label'] ?? strtoupper((string) $childKey))); ?>
</a>
<?php endforeach; ?>

View File

@ -223,6 +223,12 @@ if (!isset($availableStores[$storeKey])) {
}
$storeConfig = $availableStores[$storeKey];
$storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey));
$recoverablesStoreKey = trim((string) ($storeConfig['recoverables_store_key'] ?? ''));
$recoverablesStoreLabel = $recoverablesStoreKey !== '' && isset($availableStores[$recoverablesStoreKey]) ? (string) ($availableStores[$recoverablesStoreKey]['label'] ?? strtoupper($recoverablesStoreKey)) : '';
$recoverablesMainStoreKey = trim((string) ($storeConfig['main_store_key'] ?? ''));
$recoverablesMainStoreLabel = $recoverablesMainStoreKey !== '' && isset($availableStores[$recoverablesMainStoreKey]) ? (string) ($availableStores[$recoverablesMainStoreKey]['label'] ?? strtoupper($recoverablesMainStoreKey)) : $storeLabel;
$recoverablesStoreLabelHtml = htmlspecialchars($storeLabel);
$recoverablesMainStoreLabelHtml = htmlspecialchars($recoverablesMainStoreLabel);
$usesIncrementalSync = !empty($storeConfig['incremental_sync']);
$storeNav = drive_test_store_navigation_state($availableStores, $storeKey);
$storeNavGroups = $storeNav['groups'];
@ -232,7 +238,7 @@ $activeStoreGroupChildren = $storeNav['active_group_children'] ?? [];
$parentStoreLabel = (string) ($storeNav['parent_label'] ?? '');
$isMainTuaniStore = cc_test_is_tuani_main_store_key($storeKey);
$isTuaniRecuperablesStore = $storeKey === 'tuani_recuperables';
$isTuaniRecuperablesStore = cc_test_is_recoverables_store_key($storeKey);
if ($isTuaniRecuperablesStore && trim((string) ($_GET['view'] ?? '')) === '') {
$view = 'todos';
}
@ -244,17 +250,11 @@ $viewCardColumnClass = $isTuaniRecuperablesStore ? 'col-md-6 col-xl-2' : 'col-md
$callCenterDefaultView = ($isMainTuaniStore || $isTuaniRecuperablesStore) ? cc_test_callcenter_pro_default_view_key($storeKey) : $view;
$viewCards = $isTuaniRecuperablesStore ? [
'pendientes_hoy' => [
'label' => 'Carritos recuperables',
'heading' => 'Carritos recuperables',
'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',
'todos' => [
'label' => 'Todos',
'heading' => 'Todos los pedidos cargados',
'stat_key' => 'total',
'active_class' => 'bg-light border',
],
'confirmados' => [
'label' => 'Confirmados',
@ -280,11 +280,11 @@ $viewCards = $isTuaniRecuperablesStore ? [
'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' => $storeLabel,
'heading' => $storeLabel,
'stat_key' => 'pendientes_hoy',
'active_class' => 'bg-dark text-white',
],
] : [
'pendientes_hoy' => [
@ -358,7 +358,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>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: <strong>' . $recoverablesStoreLabelHtml . '</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. Esta bandeja abre por defecto en <strong>Todos</strong>. La 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>' . $recoverablesMainStoreLabelHtml . ' principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja. Las vistas <strong>' . $recoverablesStoreLabelHtml . '</strong> y <strong>Todos</strong> arrancan en la fila ' . (int) ($storeConfig['startRow'] ?? 0) . ', 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;
@ -1149,7 +1149,7 @@ try {
$aImport = cc_test_import_time($a);
$bImport = cc_test_import_time($b);
if (cc_test_is_tuani_recoverables_store_key($storeKey) && in_array($view, ['pendientes_hoy', 'todos'], true)) {
if (cc_test_is_recoverables_store_key($storeKey) && in_array($view, ['pendientes_hoy', 'todos'], true)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
@ -1227,7 +1227,7 @@ try {
}
if ($view === 'todos') {
if (cc_test_is_tuani_recoverables_store_key($storeKey)) {
if (cc_test_is_recoverables_store_key($storeKey)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
@ -1310,14 +1310,14 @@ try {
}
if ($isTuaniRecuperablesStore) {
$pageTitle = 'Carritos recuperables | Base de Datos Pedidos';
$pageDescription = 'Bandeja compartida para que cualquier asesora revise carritos recuperables con vistas de seguimiento y observados cargados desde Drive.';
$pageTitle = $storeLabel . ' | Base de Datos Pedidos';
$pageDescription = 'Bandeja compartida para que cualquier asesora revise ' . mb_strtolower($storeLabel) . ' con vistas de seguimiento y observados cargados desde Drive.';
} else {
$pageTitle = 'Base de Datos Pedidos | Asignación';
$pageDescription = 'Bandejas de asignación y gestión de estados para el Call Center, cargadas desde Drive.';
}
$callCenterSectionLabel = $isTuaniRecuperablesStore ? 'Carritos recuperables' : 'Pedidos Asignados';
$callCenterSectionLabel = $isTuaniRecuperablesStore ? $storeLabel : 'Pedidos Asignados';
require_once 'layout_header.php';
?>
@ -1655,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' => 'nuevos_hoy'])); ?>" 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' => $recoverablesStoreKey, 'view' => 'todos'])); ?>" class="btn btn-sm btn-outline-success">Abrir <?php echo htmlspecialchars($recoverablesStoreLabel !== '' ? $recoverablesStoreLabel : 'Carritos recuperables'); ?> (#D...)</a>
<?php endif; ?>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
@ -1666,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' ? 'nuevos_hoy' : $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' => cc_test_is_recoverables_store_key($groupKey) ? 'todos' : $view, 'store' => $groupKey], $persistedAssessorParams))); ?>">
<?php echo htmlspecialchars((string) ($groupConfig['label'] ?? strtoupper((string) $groupKey))); ?>
</a>
</li>
@ -1676,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' ? 'nuevos_hoy' : $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' => cc_test_is_recoverables_store_key($activeStoreGroupKey) ? 'todos' : $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' ? 'nuevos_hoy' : $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' => cc_test_is_recoverables_store_key($childKey) ? 'todos' : $view, 'store' => $childKey], $persistedAssessorParams))); ?>">
<?php echo htmlspecialchars((string) ($childConfig['label'] ?? strtoupper((string) $childKey))); ?>
</a>
<?php endforeach; ?>

View File

@ -253,7 +253,7 @@ function cc_test_default_assessor_keys(): array
function cc_test_is_tuani_store_key(string $storeKey): bool
{
$storeKey = trim(mb_strtolower($storeKey));
return in_array($storeKey, ['flower', 'otra_tienda', 'tuani_recuperables'], true);
return in_array($storeKey, ['flower', 'otra_tienda', 'flower_recuperables', 'tuani_recuperables'], true);
}
function cc_test_is_tuani_main_store_key(string $storeKey): bool
@ -262,10 +262,15 @@ function cc_test_is_tuani_main_store_key(string $storeKey): bool
return in_array($storeKey, ['flower', 'otra_tienda'], true);
}
function cc_test_is_tuani_recoverables_store_key(string $storeKey): bool
function cc_test_is_recoverables_store_key(string $storeKey): bool
{
$storeKey = trim(mb_strtolower($storeKey));
return $storeKey === 'tuani_recuperables';
return in_array($storeKey, ['flower_recuperables', 'tuani_recuperables'], true);
}
function cc_test_is_tuani_recoverables_store_key(string $storeKey): bool
{
return cc_test_is_recoverables_store_key($storeKey);
}
function cc_test_callcenter_pro_default_view_key(string $storeKey): string
@ -274,8 +279,8 @@ function cc_test_callcenter_pro_default_view_key(string $storeKey): string
return 'ultimos_3_dias';
}
if (cc_test_is_tuani_recoverables_store_key($storeKey)) {
return 'nuevos_hoy';
if (cc_test_is_recoverables_store_key($storeKey)) {
return 'todos';
}
return 'pendientes_hoy';
@ -358,14 +363,18 @@ if (!function_exists('cc_test_fetch_store_phone_index')) {
}
if (!function_exists('cc_test_mark_recoverables_duplicate_orders')) {
function cc_test_mark_recoverables_duplicate_orders(PDO $pdo, array $orders, string $storeKey, string $mainStoreKey = 'otra_tienda'): array
function cc_test_mark_recoverables_duplicate_orders(PDO $pdo, array $orders, string $storeKey, string $mainStoreKey = ''): array
{
$storeKey = trim(mb_strtolower($storeKey));
if ($storeKey !== 'tuani_recuperables' || empty($orders)) {
if (!cc_test_is_recoverables_store_key($storeKey) || empty($orders)) {
return $orders;
}
$mainStoreKey = trim(mb_strtolower($mainStoreKey));
if ($mainStoreKey === '') {
$mainStoreKey = $storeKey === 'flower_recuperables' ? 'flower' : 'otra_tienda';
}
if ($mainStoreKey === '') {
return $orders;
}
@ -1484,6 +1493,30 @@ function cc_test_followup_tracking_states(): array
return ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO', 'SE ENVIO NUMERO DE CUENTA'];
}
function cc_test_recoverable_claimable_states(): array
{
return cc_test_followup_tracking_states();
}
function cc_test_is_recoverable_claimable_state(string $estado): bool
{
$estado = cc_test_normalize_state($estado);
if ($estado === '') {
return true;
}
return in_array($estado, cc_test_recoverable_claimable_states(), true);
}
function cc_test_can_claim_recoverable_order(?array $tracking): bool
{
if ($tracking === null) {
return true;
}
return cc_test_is_recoverable_claimable_state((string) ($tracking['estado'] ?? ''));
}
function cc_test_followup_parse_datetime(?string $value): ?DateTimeImmutable
{
$value = trim((string) $value);

View File

@ -26,6 +26,23 @@ function drive_test_available_stores(): array
'delivery_sync_start_row' => 8687,
'delivery_sync_column' => 'W',
'min_codigo_number' => 37431,
'recoverables_store_key' => 'flower_recuperables',
'enforce_db_start_row' => true,
'incremental_sync' => true,
],
// Nueva bandeja recuperable de Flower desde la pestaña compartida.
'flower_recuperables' => [
'label' => 'Carritos recuperables Flower',
'sheet_title' => 'easysell_abandoneds',
'spreadsheet_id' => '1SSmQuR9quxeQbMKNMDkRe8-n1gU7WuEfsFaJ3WKFO-c',
'startRow' => 1503,
'status_sync_start_row' => 1503,
'status_sync_column' => 'V',
'advisor_sync_start_row' => 1503,
'advisor_sync_column' => 'X',
'delivery_sync_start_row' => 1503,
'delivery_sync_column' => 'W',
'main_store_key' => 'flower',
'enforce_db_start_row' => true,
'incremental_sync' => true,
],
@ -36,6 +53,7 @@ function drive_test_available_stores(): array
'spreadsheet_id' => '1QYKeBJIIqYm6yW6Ka-5gKdxhUHwXOAc0No7RjnimxTw',
'sheet_gid' => 1523126328,
'startRow' => 6804,
'recoverables_store_key' => 'tuani_recuperables',
'enforce_db_start_row' => true,
'incremental_sync' => true,
'status_sync_start_row' => 7207,
@ -45,9 +63,9 @@ function drive_test_available_stores(): array
'delivery_sync_start_row' => 7291,
'delivery_sync_column' => 'W',
],
// Bandeja compartida de carritos recuperables/abandonados: misma estructura, pero desde la fila 2000.
// Bandeja compartida de carritos recuperables Tuani/abandonados: misma estructura, pero desde la fila 2000.
'tuani_recuperables' => [
'label' => 'Carritos recuperables',
'label' => 'Carritos recuperables Tuani',
'sheet_title' => 'easysell_abandoneds',
'spreadsheet_id' => '1QYKeBJIIqYm6yW6Ka-5gKdxhUHwXOAc0No7RjnimxTw',
'sheet_gid' => 965530888,
@ -58,6 +76,7 @@ function drive_test_available_stores(): array
'advisor_sync_column' => 'X',
'delivery_sync_start_row' => 2000,
'delivery_sync_column' => 'W',
'main_store_key' => 'otra_tienda',
'enforce_db_start_row' => true,
'incremental_sync' => true,
],
@ -348,6 +367,7 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
}
$storeConfig = drive_test_get_store_config($storeKey);
$sheetTitle = trim((string) ($storeConfig['sheet_title'] ?? ''));
$spreadsheetId = trim((string) ($storeConfig['spreadsheet_id'] ?? ''));
if ($spreadsheetId === '') {
throw new RuntimeException('Configuración de Drive para la tienda no válida.');
@ -366,7 +386,7 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
$client->addScope(Google\Service\Sheets::SPREADSHEETS);
$service = new Google\Service\Sheets($client);
$statusRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $statusColumn . $sourceRow);
$statusRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $statusColumn . $sourceRow, $sheetTitle);
$estadoNormalizado = cc_test_normalize_state(trim($estado));
$deliveryDate = trim((string) $deliveryDate);
@ -389,7 +409,7 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
$deliveryStartRow = drive_test_delivery_sync_start_row($storeKey);
if ($deliveryStartRow !== null && $sourceRow >= $deliveryStartRow) {
$deliveryColumn = drive_test_delivery_sync_column($storeKey);
$deliveryRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $deliveryColumn . $sourceRow);
$deliveryRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $deliveryColumn . $sourceRow, $sheetTitle);
$updates[] = new Google\Service\Sheets\ValueRange([
'range' => $deliveryRange,
'values' => [[ $deliveryLabel ]],
@ -400,7 +420,7 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
$advisorStartRow = drive_test_advisor_sync_start_row($storeKey);
if ($advisorName !== '' && $advisorStartRow !== null && $sourceRow >= $advisorStartRow) {
$advisorColumn = drive_test_advisor_sync_column($storeKey);
$advisorRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $advisorColumn . $sourceRow);
$advisorRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $advisorColumn . $sourceRow, $sheetTitle);
$updates[] = new Google\Service\Sheets\ValueRange([
'range' => $advisorRange,
'values' => [[ $advisorName ]],
@ -428,9 +448,15 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
];
}
function drive_test_resolve_sheet_a1_range($service, string $spreadsheetId, ?int $sheetGid, string $rangeA1): string
function drive_test_resolve_sheet_a1_range($service, string $spreadsheetId, ?int $sheetGid, string $rangeA1, ?string $sheetTitle = null): string
{
$sheetTitle = trim((string) $sheetTitle);
if ($sheetGid === null) {
if ($sheetTitle !== '') {
$escapedTitle = str_replace("'", "''", $sheetTitle);
return "'{$escapedTitle}'!{$rangeA1}";
}
return $rangeA1;
}
@ -502,6 +528,7 @@ function drive_test_extract_codigo_number(?string $codigo): ?int
function drive_test_fetch_orders(int $limit = 10, ?int $startRow = null, string $storeKey = 'flower'): array
{
$storeConfig = drive_test_get_store_config($storeKey);
$sheetTitle = trim((string) ($storeConfig['sheet_title'] ?? ''));
if ($startRow === null || $startRow <= 0) {
$startRow = (int) ($storeConfig['startRow'] ?? 0);
}
@ -525,7 +552,7 @@ function drive_test_fetch_orders(int $limit = 10, ?int $startRow = null, string
$service = new Google\Service\Sheets($client);
$range = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $rangeA1);
$range = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $rangeA1, $sheetTitle);
$response = $service->spreadsheets_values->get($spreadsheetId, $range);
$values = $response->getValues();

View File

@ -46,6 +46,9 @@ if (in_array($currentAssessorFilter, cc_test_callcenter_assessor_filter_clear_va
}
$currentCallCenterStore = cc_test_normalize_context_key($_GET['store'] ?? ($_SESSION[cc_test_callcenter_store_session_key()] ?? ''));
$currentCallCenterView = cc_test_normalize_context_key($_GET['view'] ?? ($_SESSION[cc_test_callcenter_view_session_key()] ?? ''));
if (cc_test_is_tuani_recoverables_store_key($currentCallCenterStore) && $currentCallCenterView === 'nuevos_hoy') {
$currentCallCenterView = 'todos';
}
$shouldPersistAssessor = $currentAssessorFilter !== '';
$callCenterPersistentParams = [];
if ($shouldPersistAssessor) {

View File

@ -50,8 +50,9 @@ try {
$storeKey = '';
}
$isRecoverablesQueue = $storeKey === 'tuani_recuperables';
$canClaimRecoverable = $isRecoverablesQueue && $trackingUserId === null;
$isRecoverablesQueue = cc_test_is_recoverables_store_key($storeKey);
$canClaimRecoverable = $isRecoverablesQueue && cc_test_can_claim_recoverable_order($tracking);
$needsRecoverableClaim = $canClaimRecoverable && ($tracking === null || $trackingUserId !== $asesor_id);
if ($tracking === null) {
if (!$canClaimRecoverable) {
@ -65,7 +66,7 @@ try {
exit;
}
if ($canClaimRecoverable) {
if ($needsRecoverableClaim) {
cc_test_upsert_assignee($pdo, $pedido_id, $asesor_id);
}
}

View File

@ -57,7 +57,7 @@ try {
$service = new Google\Service\Sheets($client);
$range = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $rangeA1);
$range = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $rangeA1, $sheetTitle);
$response = $service->spreadsheets_values->get($spreadsheetId, $range);
$values = $response->getValues();

View File

@ -824,7 +824,7 @@ try {
$sourceOrderForAuth = cc_test_fetch_source_order($pdo, $sourceKey);
$sourceStoreKeyForAuth = trim((string) ($sourceOrderForAuth['store_key'] ?? ''));
$isRecoverablesQueue = $sourceStoreKeyForAuth === 'tuani_recuperables';
$isRecoverablesQueue = cc_test_is_recoverables_store_key($sourceStoreKeyForAuth);
if (!array_key_exists('numero_cuenta_sede_id', $_POST)) {
$numeroCuentaSedeIdCurrent = trim((string) ($currentTracking['numero_cuenta_sede_id'] ?? ''));
@ -851,12 +851,12 @@ try {
$role = (string) ($_SESSION['user_role'] ?? '');
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
$trackingUserId = $currentTracking ? (int) ($currentTracking['user_id'] ?? 0) : null;
// En carritos recuperables, los estados de seguimiento siguen siendo retomables por otra asesora.
// Solo los confirmados o cerrados quedan bloqueados para nuevas tomas.
$canClaimRecoverable = $isRecoverablesQueue && cc_test_can_claim_recoverable_order($currentTracking);
if (!$isAdmin) {
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
$trackingUserId = $currentTracking ? (int) ($currentTracking['user_id'] ?? 0) : null;
// En la bandeja de carritos recuperables, cualquier pedido sin asignar
// debe poder ser tomado por la asesora, sin importar el estado inicial.
$canClaimRecoverable = $isRecoverablesQueue && $trackingUserId === null;
if ($currentTracking === null) {
if (!$canClaimRecoverable) {
http_response_code(403);
@ -1101,6 +1101,10 @@ try {
$pdo->beginTransaction();
if (!$isAdmin && $canClaimRecoverable) {
cc_test_upsert_assignee($pdo, $sourceKey, $currentUserId);
}
$stmt = $pdo->prepare(
'INSERT INTO callcenter_test_tracking (
source_key,