Autosave: 20260731-085103

This commit is contained in:
Flatlogic Bot 2026-07-31 08:49:28 +00:00
parent a902ed41a0
commit 2d3ef7a210
5 changed files with 142 additions and 40 deletions

View File

@ -422,7 +422,7 @@ $_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key
$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.'
: '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 desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; cuando haya sustento pendiente en ese nuevo flujo, la bandeja principal queda visible pero bloqueada hasta completarlo. 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 3 días hábiles</strong> para concentrar el seguimiento reciente.';
: '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 desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; cuando haya sustento pendiente en ese nuevo flujo, la tarjeta de Asignados hoy sigue visible y el celular aparece con candado hasta completarlo. 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 3 días hábiles</strong> para concentrar el seguimiento reciente. En las vistas <strong>Todos</strong> y <strong>Últimos 3 días hábiles</strong>, los pedidos del nuevo flujo con sustento pendiente se muestran con candado en el celular hasta completar ese paso; la llamada sigue disponible desde el panel.';
$errorMessage = null;
$noticeMessage = null;
@ -702,6 +702,12 @@ try {
$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['llamada_bloqueada_dia3'] = $followupDayInfo !== null
&& !empty($order['promo_final_pendiente'])
&& (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4) === 3;
$order['llamada_bloqueada_dia3_mensaje'] = $order['llamada_bloqueada_dia3']
? 'Celular oculto hasta completar el sustento de Día 3.'
: '';
$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;
@ -988,9 +994,9 @@ try {
}
$assessorInboxLockedCount = count($assessorInboxLockedOrders);
$assessorInboxLocked = $assessorInboxLockedCount > 0;
$assessorInboxLockedView = $assessorInboxLocked && $view === 'pendientes_hoy';
$assessorInboxLockedView = $assessorInboxLocked && $view === 'nuevos_hoy';
$assessorInboxLockNotice = $assessorInboxLocked
? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' del nuevo flujo con sustento pendiente para Promo Final. La bandeja principal queda bloqueada hasta completar ese paso.'
? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' del nuevo flujo con sustento pendiente para Promo Final. En esos casos, el celular aparece con candado hasta completar ese paso.'
: '';
$visibleOrdersTotalCount = count($visibleOrders);
@ -1354,8 +1360,8 @@ require_once 'layout_header.php';
<div class="card-body">
<div class="small text-uppercase <?php echo htmlspecialchars($labelClass); ?> mb-2"><?php echo htmlspecialchars((string) ($viewCard['label'] ?? $viewKey)); ?></div>
<div class="display-6 fw-bold mb-0"><?php echo (int) ($stats[$viewCard['stat_key']] ?? 0); ?></div>
<?php if ($viewKey === 'pendientes_hoy' && $assessorInboxLocked): ?>
<div class="small fw-semibold text-danger mt-2"><i class="bi bi-lock-fill me-1"></i>Bloqueado</div>
<?php if ($viewKey === 'nuevos_hoy' && $assessorInboxLocked): ?>
<div class="small fw-semibold text-warning mt-2"><i class="bi bi-lock-fill me-1"></i>Con candado en celulares</div>
<?php endif; ?>
</div>
</article>
@ -1417,7 +1423,7 @@ require_once 'layout_header.php';
<?php endif; ?>
<span class="badge bg-light text-dark border"><?php echo (int) $visibleOrdersTotalCount; ?> pedidos únicos en esta bandeja</span>
<?php if ($assessorInboxLockedView): ?>
<span class="badge bg-danger-subtle text-danger-emphasis border">Bloqueado por sustento de Día 3</span>
<span class="badge bg-warning-subtle text-warning-emphasis border">Candado en celulares por sustento de Día 3</span>
<?php endif; ?>
<?php if ($view === 'todos'): ?>
<span class="badge bg-primary-subtle text-primary-emphasis border">Página <?php echo (int) $todosPage; ?> de <?php echo (int) $todosTotalPages; ?> · 200 por página</span>
@ -1427,16 +1433,16 @@ require_once 'layout_header.php';
<div class="card-body p-0">
<?php if ($assessorInboxLockedView): ?>
<div class="p-3 pb-0">
<div class="alert alert-warning border-warning-subtle d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 mb-0">
<div class="alert alert-info border-info-subtle d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 mb-0">
<div>
<div class="fw-semibold mb-1"><i class="bi bi-lock-fill me-1"></i>Bandeja bloqueada hasta completar el sustento</div>
<div class="small mb-0"><?php echo htmlspecialchars($assessorInboxLockNotice); ?> Usa <strong>Últimos 3 días hábiles</strong> para subir la imagen y mover esos pedidos a Promo Final.</div>
<div class="fw-semibold mb-1"><i class="bi bi-lock-fill me-1"></i>Celulares con candado por sustento de Día 3</div>
<div class="small mb-0"><?php echo htmlspecialchars($assessorInboxLockNotice); ?> Usa <strong>Últimos 3 días hábiles</strong> si necesitas completar ese sustento más rápido.</div>
</div>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', array_merge($callCenterParams, ['view' => 'ultimos_3_dias']))); ?>" class="btn btn-outline-warning btn-sm text-nowrap">Ir a Últimos 3 días</a>
<a href="<?php echo htmlspecialchars(cc_test_build_url('call_center_pro.php', array_merge($callCenterParams, ['view' => 'ultimos_3_dias']))); ?>" class="btn btn-outline-info btn-sm text-nowrap">Ir a Últimos 3 días</a>
</div>
</div>
<?php endif; ?>
<div class="table-responsive <?php echo $assessorInboxLockedView ? 'opacity-50 pe-none user-select-none' : ''; ?>">
<div class="table-responsive">
<table class="table table-hover align-top mb-0 cc-callcenter-table">
<thead class="table-light">
<tr>
@ -1524,7 +1530,16 @@ require_once 'layout_header.php';
</td>
<td>
<div class="fw-semibold"><?php echo htmlspecialchars(cc_test_display_value($order['nombre'], 'Sin nombre')); ?></div>
<div class="small text-muted mb-1">Celular: <?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin número')); ?></div>
<div class="small text-muted mb-1">
Celular:
<?php if (!empty($order['llamada_bloqueada_dia3'])): ?>
<span class="badge bg-warning-subtle text-warning-emphasis border ms-1" title="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>" aria-label="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>">
<i class="bi bi-lock-fill me-1"></i>Celular oculto
</span>
<?php else: ?>
<?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin número')); ?>
<?php endif; ?>
</div>
<div class="small text-muted">DNI: <?php echo htmlspecialchars(cc_test_display_value($order['dni'], 'Sin DNI')); ?></div>
</td>
<td>
@ -1641,14 +1656,14 @@ require_once 'layout_header.php';
<button type="submit" class="btn btn-sm btn-primary cc-callcenter-assign-submit cc-callcenter-action-btn"><?php echo htmlspecialchars((string) $buttonLabel); ?></button>
</div>
</form>
<?php endif; ?>
<?php if (!empty($order['telefono_url'])): ?>
<?php endif; ?> <?php if (!empty($order['telefono_url'])): ?>
<button
type="button"
class="btn btn-sm btn-primary cc-callcenter-action-btn"
data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>"
data-modal-id="<?php echo htmlspecialchars($modalId); ?>"
data-phone="<?php echo htmlspecialchars((string) ($order['celular'] ?? '')); ?>"
data-phone-locked="<?php echo !empty($order['llamada_bloqueada_dia3']) ? '1' : '0'; ?>"
data-order-label="<?php echo htmlspecialchars(cc_test_order_label($order)); ?>"
data-client-name="<?php echo htmlspecialchars(cc_test_display_value($order['nombre'], 'Cliente sin nombre')); ?>"
onclick="return registrarLlamada(event, this)">
@ -1688,10 +1703,19 @@ require_once 'layout_header.php';
<button type="button" class="btn btn-primary btn-sm px-3" id="subir-logistica-<?php echo htmlspecialchars($order['source_key']); ?>" data-route-pedido-id="<?php echo (int) ($order['ruta_contraentrega_pedido_id'] ?? 0); ?>" data-rotulado-pedido-id="<?php echo (int) ($order['pedido_rotulado_pedido_id'] ?? 0); ?>" onclick="guardarGestion('<?php echo htmlspecialchars($order['source_key']); ?>', this, { subirLogistica: true })"><?php echo htmlspecialchars($subirLogisticaButtonLabel); ?></button>
<?php endif; ?>
</div>
<div class="small text-muted mt-1"><?php echo htmlspecialchars(cc_test_display_value($order['producto'], 'Sin producto')); ?> · <?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?></div>
<div class="small text-muted mt-1">
<?php echo htmlspecialchars(cc_test_display_value($order['producto'], 'Sin producto')); ?> ·
<?php if (!empty($order['llamada_bloqueada_dia3'])): ?>
<span class="badge bg-warning-subtle text-warning-emphasis border ms-1" title="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>" aria-label="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>">
<i class="bi bi-lock-fill me-1"></i>Celular oculto
</span>
<?php else: ?>
<?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?>
<?php endif; ?>
</div>
<?php if ($followupDayInfo !== null): ?>
<div class="small mt-2 <?php echo htmlspecialchars(!empty($followupDayInfo['notice_text']) ? $followupDayInfo['text_class'] : 'text-muted'); ?><?php echo !empty($followupDayInfo['notice_text']) ? ' fw-semibold' : ''; ?>">
<?php echo htmlspecialchars(!empty($followupDayInfo['notice_text']) ? $followupDayInfo['notice_text'] : ('Seguimiento iniciado el ' . $followupDayInfo['started_at_label'] . '.')); ?>
<?php echo htmlspecialchars(!empty($followupDayInfo['notice_text']) ? $followupDayInfo['notice_text'] : ('Seguimiento asignado el ' . $followupDayInfo['started_at_label'] . '.')); ?>
</div>
<?php endif; ?>
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
@ -1730,11 +1754,19 @@ require_once 'layout_header.php';
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
<div>
<div class="small text-muted mb-1">Llamar / Gestionar</div>
<h4 class="h6 fw-bold mb-2">Número listo para copiar</h4>
<div class="fs-4 fw-semibold lh-sm text-primary" id="airDroidPhoneNumber-<?php echo htmlspecialchars($order['source_key']); ?>"><?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?></div>
<h4 class="h6 fw-bold mb-2"><?php echo !empty($order['llamada_bloqueada_dia3']) ? 'Celular oculto' : 'Número listo para copiar'; ?></h4>
<div class="fs-4 fw-semibold lh-sm text-primary" id="airDroidPhoneNumber-<?php echo htmlspecialchars($order['source_key']); ?>">
<?php if (!empty($order['llamada_bloqueada_dia3'])): ?>
<span class="badge bg-warning-subtle text-warning-emphasis border" title="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>" aria-label="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>">
<i class="bi bi-lock-fill me-1"></i>Celular oculto
</span>
<?php else: ?>
<?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?>
<?php endif; ?>
</div>
</div>
<div class="d-flex flex-column gap-2">
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneToClipboard(this.dataset.phone); return false;">Copiar número</button>
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo !empty($order['llamada_bloqueada_dia3']) ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneToClipboard(this.dataset.phone); return false;"<?php echo !empty($order['llamada_bloqueada_dia3']) ? ' disabled' : ''; ?>><?php echo !empty($order['llamada_bloqueada_dia3']) ? 'Número oculto' : 'Copiar número'; ?></button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="openAirDroidWeb()">Abrir AirDroid Web</button>
</div>
</div>
@ -2737,6 +2769,7 @@ function showAirDroidHelper(details) {
const registered = !!details.registered;
const copied = !!details.copied;
const modalId = details.modalId || '';
const phoneLocked = !!details.phoneLocked;
if (modalId) {
const modalElement = document.getElementById(modalId);
@ -2753,10 +2786,26 @@ function showAirDroidHelper(details) {
if (orderNode) orderNode.textContent = orderLabel;
if (clientNode) clientNode.textContent = clientName;
if (phoneNode) phoneNode.textContent = phone;
if (phoneNode) {
if (phoneLocked) {
phoneNode.innerHTML = '<span class="badge bg-warning-subtle text-warning-emphasis border"><i class="bi bi-lock-fill me-1"></i>Celular oculto</span>';
} else {
phoneNode.textContent = phone;
}
}
if (copyButton) {
copyButton.dataset.phone = phone !== '-' ? phone : '';
if (phoneLocked) {
copyButton.dataset.phone = '';
copyButton.disabled = true;
copyButton.textContent = 'Número oculto';
copyButton.setAttribute('title', 'Disponible cuando se complete el sustento de Día 3');
} else {
copyButton.dataset.phone = phone !== '-' ? phone : '';
copyButton.disabled = false;
copyButton.textContent = 'Copiar número';
copyButton.removeAttribute('title');
}
}
if (openButton) {
@ -2776,6 +2825,7 @@ function registrarLlamada(event, trigger) {
const phone = normalizarNumeroTelefono(trigger?.dataset?.phone || '');
const orderLabel = trigger?.dataset?.orderLabel || '';
const clientName = trigger?.dataset?.clientName || '';
const phoneLocked = trigger?.dataset?.phoneLocked === '1';
if (!sourceKey) {
alert('No se encontró el pedido para registrar la llamada.');
@ -2837,6 +2887,7 @@ function registrarLlamada(event, trigger) {
sourceKey,
modalId,
phone,
phoneLocked,
orderLabel,
clientName,
registered,

View File

@ -320,7 +320,7 @@ $_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key
$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.'
: '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 desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; exige imagen de sustento para mover el pedido.';
: '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 desde el Día 3 para pedidos nuevos desde el 29/07/2026 y desde el Día 4 para pedidos anteriores; exige imagen de sustento para mover el pedido. En las vistas <strong>Todos</strong> y <strong>Últimos 3 días hábiles</strong>, los pedidos del nuevo flujo con sustento pendiente se muestran con candado en el número hasta completar ese paso; la llamada sigue disponible desde el panel.';
$errorMessage = null;
$noticeMessage = null;
@ -887,6 +887,12 @@ try {
$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['llamada_bloqueada_dia3'] = $followupDayInfo !== null
&& !empty($order['promo_final_pendiente'])
&& (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4) === 3;
$order['llamada_bloqueada_dia3_mensaje'] = $order['llamada_bloqueada_dia3']
? 'Celular oculto hasta completar el sustento de Día 3.'
: '';
$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;
@ -2042,14 +2048,14 @@ require_once 'layout_header.php';
</div>
</form>
<?php endif; ?>
<div class="d-flex flex-column gap-1 align-items-stretch cc-callcenter-actions<?php echo in_array($_SESSION['user_role'] ?? '', ['Administrador', 'admin'], true) ? ' mt-1' : ''; ?>">
<?php if (!empty($order['telefono_url'])): ?>
<div class="d-flex flex-column gap-1 align-items-stretch cc-callcenter-actions<?php echo in_array($_SESSION['user_role'] ?? '', ['Administrador', 'admin'], true) ? ' mt-1' : ''; ?>"> <?php if (!empty($order['telefono_url'])): ?>
<button
type="button"
class="btn btn-sm btn-primary cc-callcenter-action-btn"
data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>"
data-modal-id="<?php echo htmlspecialchars($modalId); ?>"
data-phone="<?php echo htmlspecialchars((string) ($order['celular'] ?? '')); ?>"
data-phone-locked="<?php echo !empty($order['llamada_bloqueada_dia3']) ? '1' : '0'; ?>"
data-order-label="<?php echo htmlspecialchars(cc_test_order_label($order)); ?>"
data-client-name="<?php echo htmlspecialchars(cc_test_display_value($order['nombre'], 'Cliente sin nombre')); ?>"
onclick="return registrarLlamada(event, this)">
@ -2080,7 +2086,16 @@ require_once 'layout_header.php';
</td>
<td>
<div class="fw-semibold"><?php echo htmlspecialchars(cc_test_display_value($order['nombre'], 'Sin nombre')); ?></div>
<div class="small text-muted mb-1">Celular: <?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin número')); ?></div>
<div class="small text-muted mb-1">
Celular:
<?php if (!empty($order['llamada_bloqueada_dia3'])): ?>
<span class="badge bg-warning-subtle text-warning-emphasis border ms-1" title="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>" aria-label="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>">
<i class="bi bi-lock-fill me-1"></i>Celular oculto
</span>
<?php else: ?>
<?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin número')); ?>
<?php endif; ?>
</div>
<div class="small text-muted">DNI: <?php echo htmlspecialchars(cc_test_display_value($order['dni'], 'Sin DNI')); ?></div>
</td>
<td>
@ -2160,10 +2175,19 @@ require_once 'layout_header.php';
<button type="button" class="btn btn-primary btn-sm px-3" id="subir-logistica-<?php echo htmlspecialchars($order['source_key']); ?>" data-route-pedido-id="<?php echo (int) ($order['ruta_contraentrega_pedido_id'] ?? 0); ?>" data-rotulado-pedido-id="<?php echo (int) ($order['pedido_rotulado_pedido_id'] ?? 0); ?>" onclick="guardarGestion('<?php echo htmlspecialchars($order['source_key']); ?>', this, { subirLogistica: true })"><?php echo htmlspecialchars($subirLogisticaButtonLabel); ?></button>
<?php endif; ?>
</div>
<div class="small text-muted mt-1"><?php echo htmlspecialchars(cc_test_display_value($order['producto'], 'Sin producto')); ?> · <?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?></div>
<div class="small text-muted mt-1">
<?php echo htmlspecialchars(cc_test_display_value($order['producto'], 'Sin producto')); ?> ·
<?php if (!empty($order['llamada_bloqueada_dia3'])): ?>
<span class="badge bg-warning-subtle text-warning-emphasis border ms-1" title="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>" aria-label="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>">
<i class="bi bi-lock-fill me-1"></i>Celular oculto
</span>
<?php else: ?>
<?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?>
<?php endif; ?>
</div>
<?php if ($followupDayInfo !== null): ?>
<div class="small mt-2 <?php echo htmlspecialchars(!empty($followupDayInfo['notice_text']) ? $followupDayInfo['text_class'] : 'text-muted'); ?><?php echo !empty($followupDayInfo['notice_text']) ? ' fw-semibold' : ''; ?>">
<?php echo htmlspecialchars(!empty($followupDayInfo['notice_text']) ? $followupDayInfo['notice_text'] : ('Seguimiento iniciado el ' . $followupDayInfo['started_at_label'] . '.')); ?>
<?php echo htmlspecialchars(!empty($followupDayInfo['notice_text']) ? $followupDayInfo['notice_text'] : ('Seguimiento asignado el ' . $followupDayInfo['started_at_label'] . '.')); ?>
</div>
<?php endif; ?>
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
@ -2202,11 +2226,19 @@ require_once 'layout_header.php';
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
<div>
<div class="small text-muted mb-1">Llamar / Gestionar</div>
<h4 class="h6 fw-bold mb-2">Número listo para copiar</h4>
<div class="fs-4 fw-semibold lh-sm text-primary" id="airDroidPhoneNumber-<?php echo htmlspecialchars($order['source_key']); ?>"><?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?></div>
<h4 class="h6 fw-bold mb-2"><?php echo !empty($order['llamada_bloqueada_dia3']) ? 'Celular oculto' : 'Número listo para copiar'; ?></h4>
<div class="fs-4 fw-semibold lh-sm text-primary" id="airDroidPhoneNumber-<?php echo htmlspecialchars($order['source_key']); ?>">
<?php if (!empty($order['llamada_bloqueada_dia3'])): ?>
<span class="badge bg-warning-subtle text-warning-emphasis border" title="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>" aria-label="<?php echo htmlspecialchars((string) ($order['llamada_bloqueada_dia3_mensaje'] ?? 'Celular oculto hasta completar el sustento de Día 3.'), ENT_QUOTES); ?>">
<i class="bi bi-lock-fill me-1"></i>Celular oculto
</span>
<?php else: ?>
<?php echo htmlspecialchars(cc_test_display_value($order['celular'], 'Sin celular')); ?>
<?php endif; ?>
</div>
</div>
<div class="d-flex flex-column gap-2">
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneToClipboard(this.dataset.phone); return false;">Copiar número</button>
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo !empty($order['llamada_bloqueada_dia3']) ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneToClipboard(this.dataset.phone); return false;"<?php echo !empty($order['llamada_bloqueada_dia3']) ? ' disabled' : ''; ?>><?php echo !empty($order['llamada_bloqueada_dia3']) ? 'Número oculto' : 'Copiar número'; ?></button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="openAirDroidWeb()">Abrir AirDroid Web</button>
</div>
</div>
@ -3253,6 +3285,7 @@ function showAirDroidHelper(details) {
const registered = !!details.registered;
const copied = !!details.copied;
const modalId = details.modalId || '';
const phoneLocked = !!details.phoneLocked;
if (modalId) {
const modalElement = document.getElementById(modalId);
@ -3269,10 +3302,26 @@ function showAirDroidHelper(details) {
if (orderNode) orderNode.textContent = orderLabel;
if (clientNode) clientNode.textContent = clientName;
if (phoneNode) phoneNode.textContent = phone;
if (phoneNode) {
if (phoneLocked) {
phoneNode.innerHTML = '<span class="badge bg-warning-subtle text-warning-emphasis border"><i class="bi bi-lock-fill me-1"></i>Celular oculto</span>';
} else {
phoneNode.textContent = phone;
}
}
if (copyButton) {
copyButton.dataset.phone = phone !== '-' ? phone : '';
if (phoneLocked) {
copyButton.dataset.phone = '';
copyButton.disabled = true;
copyButton.textContent = 'Número oculto';
copyButton.setAttribute('title', 'Disponible cuando se complete el sustento de Día 3');
} else {
copyButton.dataset.phone = phone !== '-' ? phone : '';
copyButton.disabled = false;
copyButton.textContent = 'Copiar número';
copyButton.removeAttribute('title');
}
}
if (openButton) {
@ -3292,6 +3341,7 @@ function registrarLlamada(event, trigger) {
const phone = normalizarNumeroTelefono(trigger?.dataset?.phone || '');
const orderLabel = trigger?.dataset?.orderLabel || '';
const clientName = trigger?.dataset?.clientName || '';
const phoneLocked = trigger?.dataset?.phoneLocked === '1';
if (!sourceKey) {
alert('No se encontró el pedido para registrar la llamada.');
@ -3353,6 +3403,7 @@ function registrarLlamada(event, trigger) {
sourceKey,
modalId,
phone,
phoneLocked,
orderLabel,
clientName,
registered,

View File

@ -1320,7 +1320,7 @@ function cc_test_followup_parse_datetime(?string $value): ?DateTimeImmutable
function cc_test_followup_started_at(array $order): ?DateTimeImmutable
{
foreach (['drive_imported_at', 'first_seen_at', 'import_id'] as $field) {
foreach (['assigned_at', 'drive_imported_at', 'first_seen_at', 'import_id'] as $field) {
$date = cc_test_followup_parse_datetime($order[$field] ?? null);
if ($date instanceof DateTimeImmutable) {
return $date;

View File

@ -30,10 +30,12 @@ try {
$role = (string) ($_SESSION['user_role'] ?? '');
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
$tracking = null;
if (!$isAdmin) {
cc_test_ensure_tracking_table($pdo);
$stmtTrack = $pdo->prepare('SELECT user_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1');
$stmtTrack = $pdo->prepare('SELECT user_id, estado, assigned_at, promo_final_evidencia_path FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1');
$stmtTrack->execute([$pedido_id]);
$tracking = $stmtTrack->fetch(PDO::FETCH_ASSOC);

View File

@ -866,7 +866,7 @@ try {
$pdo = db();
cc_test_ensure_tracking_table($pdo);
$stmtCurrent = $pdo->prepare('SELECT estado, numero_cuenta_enviado_at, numero_cuenta_sede_id, numero_cuenta_dni, user_id, promo_final_evidencia_path, promo_final_evidencia_subido_at, promo_final_evidencia_subido_por, cancelado_evidencia_path, cancelado_evidencia_subido_at, cancelado_evidencia_subido_por, eliminado_at, eliminado_por, ruta_contraentrega_pedido_id, pedido_rotulado_pedido_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1');
$stmtCurrent = $pdo->prepare('SELECT estado, numero_cuenta_enviado_at, numero_cuenta_sede_id, numero_cuenta_dni, user_id, assigned_at, promo_final_evidencia_path, promo_final_evidencia_subido_at, promo_final_evidencia_subido_por, cancelado_evidencia_path, cancelado_evidencia_subido_at, cancelado_evidencia_subido_por, eliminado_at, eliminado_por, ruta_contraentrega_pedido_id, pedido_rotulado_pedido_id FROM callcenter_test_tracking WHERE source_key = ? LIMIT 1');
$stmtCurrent->execute([$sourceKey]);
$currentTracking = $stmtCurrent->fetch(PDO::FETCH_ASSOC) ?: null;
@ -1071,12 +1071,10 @@ try {
throw new RuntimeException('No encontré el pedido base para moverlo a Promo Final.');
}
$promoFinalDayInfo = cc_test_followup_day_info([
$promoFinalDayInfo = cc_test_followup_day_info(array_merge($sourceOrderForPromo, [
'estado' => $estado,
'drive_imported_at' => $sourceOrderForPromo['drive_imported_at'] ?? null,
'first_seen_at' => $sourceOrderForPromo['first_seen_at'] ?? null,
'import_id' => $sourceOrderForPromo['import_id'] ?? null,
]);
'assigned_at' => $currentTracking['assigned_at'] ?? null,
]));
if (empty($promoFinalDayInfo['is_promo_final'])) {
$promoFinalThresholdDay = (int) ($promoFinalDayInfo['promo_final_threshold_day'] ?? 4);
throw new RuntimeException('Este pedido aún no puede pasar a Promo Final. Recién se habilita desde el Día ' . $promoFinalThresholdDay . '.');