Autosave: 20260807-012847

This commit is contained in:
Flatlogic Bot 2026-08-07 01:27:01 +00:00
parent 4471779478
commit 87af7beac5
4 changed files with 136 additions and 14 deletions

View File

@ -317,11 +317,10 @@ function cc_test_panel_orders_signature(array $orders): string
return sha1(json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
$view = trim((string) ($_GET['view'] ?? ''));
$view = cc_test_resolve_callcenter_context_key($_GET['view'] ?? '', cc_test_callcenter_view_session_key());
$storeKey = trim((string) ($_GET['store'] ?? 'flower'));
$storeKey = mb_strtolower($storeKey);
$availableStores = drive_test_available_stores();
$storeKey = cc_test_resolve_callcenter_context_key($_GET['store'] ?? '', cc_test_callcenter_store_session_key(), 'flower');
if (!isset($availableStores[$storeKey])) {
$storeKey = 'flower';
}
@ -337,6 +336,9 @@ $parentStoreLabel = (string) ($storeNav['parent_label'] ?? '');
$isMainTuaniStore = cc_test_is_tuani_main_store_key($storeKey);
$isTuaniRecuperablesStore = $storeKey === 'tuani_recuperables';
if ($isTuaniRecuperablesStore && trim((string) ($_GET['view'] ?? '')) === '') {
$view = 'todos';
}
$canShowTuaniLogisticaButton = $isMainTuaniStore || $isTuaniRecuperablesStore;
$viewCardColumnClass = $isTuaniRecuperablesStore ? 'col-md-6 col-xl-2' : 'col-md-6 col-xl-2';
$defaultView = cc_test_callcenter_pro_default_view_key($storeKey);
@ -456,7 +458,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. La vista <strong>Todos</strong> arranca en la fila 2000 y se pagina en bloques de 200 pedidos.'
? '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: 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;
@ -475,6 +477,7 @@ $recoverablesImportCount = 0;
$recoverablesFirstSourceRow = null;
$recoverablesLastSourceRow = null;
$startRow = (int) ($storeConfig['startRow'] ?? 0);
$recoverablesMinimumSourceRow = $isTuaniRecuperablesStore ? (int) ($storeConfig['startRow'] ?? 0) : 0;
$catalogoProductos = [];
$stats = [
'total' => 0,
@ -562,6 +565,9 @@ try {
$orders = array_values(array_filter($orders, static function (array $order): bool {
return empty($order['eliminado']);
}));
if ($isTuaniRecuperablesStore) {
$orders = cc_test_filter_recoverables_orders($orders, $recoverablesMinimumSourceRow);
}
$postedAction = trim((string) ($_POST['action'] ?? ''));
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
@ -868,6 +874,10 @@ 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)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
if ($view === 'nuevos_hoy') {
$aOrderNumberValue = cc_test_order_priority_value($a);
$bOrderNumberValue = cc_test_order_priority_value($b);
@ -984,6 +994,10 @@ try {
}
if ($view === 'todos') {
if (cc_test_is_tuani_recoverables_store_key($storeKey)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
if ($aImport !== $bImport) {
return $bImport <=> $aImport;
}
@ -991,9 +1005,7 @@ try {
$aSourceRow = (int) ($a['source_row'] ?? 0);
$bSourceRow = (int) ($b['source_row'] ?? 0);
if ($aSourceRow !== $bSourceRow) {
return cc_test_is_tuani_recoverables_store_key($storeKey)
? ($aSourceRow <=> $bSourceRow)
: ($bSourceRow <=> $aSourceRow);
return $bSourceRow <=> $aSourceRow;
}
$aId = (int) ($a['id'] ?? 0);
@ -1354,6 +1366,14 @@ require_once 'layout_header.php';
<div class="d-flex flex-column flex-xl-row justify-content-between align-items-xl-center gap-3">
<div>
<h1 class="h2 fw-bold mb-1"><i class="bi bi-headset text-primary"></i> <?php echo htmlspecialchars($callCenterSectionLabel); ?></h1>
<div class="d-flex flex-wrap gap-2 mt-2">
<span class="badge rounded-pill bg-primary-subtle text-primary-emphasis border px-3 py-2">Tienda activa: <?php echo htmlspecialchars($storeLabel); ?></span>
<?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>
<?php endif; ?>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
<?php if ($isAdmin): ?>
<a href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', $callCenterDatabaseParams)); ?>" class="btn btn-sm btn-outline-primary">Base de Datos Pedidos</a>

View File

@ -214,11 +214,10 @@ function cc_test_followup_semaforo(array $order): ?array
}
$defaultView = 'todos';
$view = trim((string) ($_GET['view'] ?? $defaultView));
$view = cc_test_resolve_callcenter_context_key($_GET['view'] ?? '', cc_test_callcenter_view_session_key());
$storeKey = trim((string) ($_GET['store'] ?? 'flower'));
$storeKey = mb_strtolower($storeKey);
$availableStores = drive_test_available_stores();
$storeKey = cc_test_resolve_callcenter_context_key($_GET['store'] ?? '', cc_test_callcenter_store_session_key(), 'flower');
if (!isset($availableStores[$storeKey])) {
$storeKey = 'flower';
}
@ -234,6 +233,9 @@ $parentStoreLabel = (string) ($storeNav['parent_label'] ?? '');
$isMainTuaniStore = cc_test_is_tuani_main_store_key($storeKey);
$isTuaniRecuperablesStore = $storeKey === 'tuani_recuperables';
if ($isTuaniRecuperablesStore && trim((string) ($_GET['view'] ?? '')) === '') {
$view = 'todos';
}
$canShowTuaniLogisticaButton = $isMainTuaniStore || $isTuaniRecuperablesStore;
$viewCardColumnClass = $isTuaniRecuperablesStore ? 'col-md-6 col-xl-2' : 'col-md-6 col-xl-2';
$callCenterDefaultView = ($isMainTuaniStore || $isTuaniRecuperablesStore) ? cc_test_callcenter_pro_default_view_key($storeKey) : $view;
@ -347,7 +349,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. La vista <strong>Todos</strong> arranca en la fila 2000 y se pagina en bloques de 200 pedidos.'
? '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: 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;
@ -399,6 +401,7 @@ $advisorOpenCounts = [];
$advisorTotalCounts = [];
$recommendedAssessorKey = null;
$startRow = (int) ($storeConfig['startRow'] ?? 0);
$recoverablesMinimumSourceRow = $isTuaniRecuperablesStore ? (int) ($storeConfig['startRow'] ?? 0) : 0;
$catalogoProductos = [];
$stats = [
'total' => 0,
@ -488,6 +491,9 @@ try {
$orders = array_values(array_filter($orders, static function (array $order): bool {
return empty($order['eliminado']);
}));
if ($isTuaniRecuperablesStore) {
$orders = cc_test_filter_recoverables_orders($orders, $recoverablesMinimumSourceRow);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'assign_assessor') {
$sourceKey = trim((string) ($_POST['source_key'] ?? ''));
@ -1095,6 +1101,10 @@ 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)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
if ($view === 'promo_final') {
$aDays = (int) ($a['dias_seguimiento'] ?? 0);
$bDays = (int) ($b['dias_seguimiento'] ?? 0);
@ -1169,6 +1179,10 @@ try {
}
if ($view === 'todos') {
if (cc_test_is_tuani_recoverables_store_key($storeKey)) {
return cc_test_compare_recoverables_orders_desc($a, $b);
}
if ($aImport !== $bImport) {
return $bImport <=> $aImport;
}
@ -1176,9 +1190,7 @@ try {
$aSourceRow = (int) ($a['source_row'] ?? 0);
$bSourceRow = (int) ($b['source_row'] ?? 0);
if ($aSourceRow !== $bSourceRow) {
return cc_test_is_tuani_recoverables_store_key($storeKey)
? ($aSourceRow <=> $bSourceRow)
: ($bSourceRow <=> $aSourceRow);
return $bSourceRow <=> $aSourceRow;
}
$aId = (int) ($a['id'] ?? 0);
@ -1610,6 +1622,14 @@ require_once 'layout_header.php';
<div class="d-flex flex-column flex-xl-row justify-content-between align-items-xl-center gap-3">
<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-2">
<span class="badge rounded-pill bg-primary-subtle text-primary-emphasis border px-3 py-2">Tienda activa: <?php echo htmlspecialchars($storeLabel); ?></span>
<?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>
<?php endif; ?>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
<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' => $callCenterDefaultView, 'store' => $storeKey], $persistedAssessorParams))); ?>" class="btn btn-sm btn-outline-dark"><?php echo htmlspecialchars($callCenterSectionLabel); ?></a>

View File

@ -1065,6 +1065,88 @@ function cc_test_normalize_context_key(?string $value): string
return mb_strtolower($value);
}
function cc_test_resolve_callcenter_context_key(?string $incomingValue, string $sessionKey, string $fallback = ''): string
{
$incomingValue = cc_test_normalize_context_key($incomingValue ?? '');
if ($incomingValue !== '') {
return $incomingValue;
}
$sessionValue = cc_test_normalize_context_key($_SESSION[$sessionKey] ?? '');
if ($sessionValue !== '') {
return $sessionValue;
}
return cc_test_normalize_context_key($fallback);
}
function cc_test_is_recoverables_order_code(?string $codigo): bool
{
$codigo = trim((string) $codigo);
if ($codigo === '') {
return false;
}
$codigo = ltrim($codigo, '#');
return preg_match('/^D\d+$/i', $codigo) === 1;
}
function cc_test_recoverables_order_sort_value(array $order): int
{
$codigo = trim((string) ($order['codigo'] ?? ''));
if ($codigo !== '' && preg_match('/^#?D(\d+)$/i', $codigo, $matches) === 1) {
return (int) ($matches[1] ?? 0);
}
$sourceRow = (int) ($order['source_row'] ?? 0);
if ($sourceRow > 0) {
return $sourceRow;
}
return (int) ($order['id'] ?? 0);
}
function cc_test_filter_recoverables_orders(array $orders, int $minimumSourceRow = 0): array
{
$minimumSourceRow = max(0, $minimumSourceRow);
return array_values(array_filter($orders, static function (array $order) use ($minimumSourceRow): bool {
if (!cc_test_is_recoverables_order_code($order['codigo'] ?? '')) {
return false;
}
$sourceRow = (int) ($order['source_row'] ?? 0);
if ($minimumSourceRow > 0 && $sourceRow < $minimumSourceRow) {
return false;
}
return true;
}));
}
function cc_test_compare_recoverables_orders_desc(array $a, array $b): int
{
$aValue = cc_test_recoverables_order_sort_value($a);
$bValue = cc_test_recoverables_order_sort_value($b);
if ($aValue !== $bValue) {
return $bValue <=> $aValue;
}
$aSourceRow = (int) ($a['source_row'] ?? 0);
$bSourceRow = (int) ($b['source_row'] ?? 0);
if ($aSourceRow !== $bSourceRow) {
return $bSourceRow <=> $aSourceRow;
}
$aId = (int) ($a['id'] ?? 0);
$bId = (int) ($b['id'] ?? 0);
if ($aId !== $bId) {
return $bId <=> $aId;
}
return strcmp((string) ($a['source_key'] ?? ''), (string) ($b['source_key'] ?? ''));
}
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();