Autosave: 20260806-194852
This commit is contained in:
parent
e983920c84
commit
a5e8a92a73
@ -14,7 +14,7 @@ $provinciasPorDepartamentoContraentrega = contraentregaProvinciasPorDepartamento
|
|||||||
$distritosPorProvinciaContraentrega = contraentregaDistritosPorProvincia();
|
$distritosPorProvinciaContraentrega = contraentregaDistritosPorProvincia();
|
||||||
$departamentosContraentrega = array_keys($provinciasPorDepartamentoContraentrega);
|
$departamentosContraentrega = array_keys($provinciasPorDepartamentoContraentrega);
|
||||||
$pageTitle = 'Pedidos Asignados | Bandeja de la asesora';
|
$pageTitle = 'Pedidos Asignados | Bandeja de la asesora';
|
||||||
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con vistas de hoy, últimos 3 días hábiles, estados, historial y acciones.';
|
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con vistas de hoy, últimos 4 días hábiles, estados, historial y acciones.';
|
||||||
|
|
||||||
$kpiPeriodConfig = cc_test_resolve_kpi_period((string) ($_GET['kpi_period'] ?? 'today'), (string) ($_GET['kpi_date'] ?? ''));
|
$kpiPeriodConfig = cc_test_resolve_kpi_period((string) ($_GET['kpi_period'] ?? 'today'), (string) ($_GET['kpi_date'] ?? ''));
|
||||||
$kpiPeriodKey = (string) ($kpiPeriodConfig['key'] ?? 'today');
|
$kpiPeriodKey = (string) ($kpiPeriodConfig['key'] ?? 'today');
|
||||||
@ -386,8 +386,8 @@ $viewCards = $isTuaniRecuperablesStore ? [
|
|||||||
'active_class' => 'bg-primary-subtle border border-primary',
|
'active_class' => 'bg-primary-subtle border border-primary',
|
||||||
],
|
],
|
||||||
'ultimos_3_dias' => [
|
'ultimos_3_dias' => [
|
||||||
'label' => 'Últimos 3 días hábiles',
|
'label' => 'Últimos 4 días hábiles',
|
||||||
'heading' => 'Pedidos asignados de los últimos 3 días hábiles',
|
'heading' => 'Pedidos asignados de los últimos 4 días hábiles',
|
||||||
'stat_key' => 'ultimos_3_dias',
|
'stat_key' => 'ultimos_3_dias',
|
||||||
'active_class' => 'bg-info-subtle border border-info',
|
'active_class' => 'bg-info-subtle border border-info',
|
||||||
],
|
],
|
||||||
@ -456,8 +456,8 @@ $_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);
|
$_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key($view);
|
||||||
|
|
||||||
$viewStatesNote = $isTuaniRecuperablesStore
|
$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: <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: 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 todos los pedidos; desde ese momento el número queda visible para llamar y gestionar, y el sustento solo se usa para mover el pedido a Promo Final. 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 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;
|
$errorMessage = null;
|
||||||
$noticeMessage = null;
|
$noticeMessage = null;
|
||||||
@ -705,7 +705,7 @@ try {
|
|||||||
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
|
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
|
||||||
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
|
$isAdmin = in_array($role, ['Administrador', 'admin'], true);
|
||||||
if ($isAdmin) {
|
if ($isAdmin) {
|
||||||
$selectedAssessorFilter = cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, true, true);
|
$selectedAssessorFilter = cc_test_resolve_callcenter_assessor_filter($assessors, $_GET['assessor'] ?? null, !$isTuaniRecuperablesStore, true);
|
||||||
} else {
|
} else {
|
||||||
unset($_SESSION[cc_test_callcenter_assessor_filter_session_key()]);
|
unset($_SESSION[cc_test_callcenter_assessor_filter_session_key()]);
|
||||||
$selectedAssessorFilter = '';
|
$selectedAssessorFilter = '';
|
||||||
@ -734,6 +734,7 @@ try {
|
|||||||
$order['seguimiento_dia_info'] = $followupDayInfo;
|
$order['seguimiento_dia_info'] = $followupDayInfo;
|
||||||
$order['dias_seguimiento'] = $followupDayInfo['day_number'] ?? null;
|
$order['dias_seguimiento'] = $followupDayInfo['day_number'] ?? null;
|
||||||
$order['promo_final_habilitado'] = !empty($followupDayInfo['is_promo_final']);
|
$order['promo_final_habilitado'] = !empty($followupDayInfo['is_promo_final']);
|
||||||
|
$order['promo_final_evidencia_habilitada'] = !empty($followupDayInfo['can_upload_promo_final_evidence']);
|
||||||
$order['promo_final_evidencia_cargada'] = trim((string) ($order['promo_final_evidencia_path'] ?? '')) !== '';
|
$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['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['promo_final_pendiente'] = $order['promo_final_habilitado'] && !$order['promo_final_evidencia_cargada'];
|
||||||
@ -743,7 +744,7 @@ try {
|
|||||||
$order['pendiente_logistica'] = $order['pendiente_logistica_destino'] !== 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;
|
$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, 3);
|
$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_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['es_cerrado'] = in_array($order['estado'], $closedStates, true);
|
||||||
$order['assessor_key'] = cc_test_find_assessor_key_by_user_id(isset($order['user_id']) ? (int) $order['user_id'] : null, $assessors);
|
$order['assessor_key'] = cc_test_find_assessor_key_by_user_id(isset($order['user_id']) ? (int) $order['user_id'] : null, $assessors);
|
||||||
@ -789,8 +790,6 @@ try {
|
|||||||
|
|
||||||
$pendingPromoFinalCounts = cc_test_followup_pending_promo_final_counts($orders);
|
$pendingPromoFinalCounts = cc_test_followup_pending_promo_final_counts($orders);
|
||||||
|
|
||||||
$orders = cc_test_mark_recoverables_duplicate_orders($pdo, $orders, $storeKey);
|
|
||||||
|
|
||||||
$performanceOrders = $orders;
|
$performanceOrders = $orders;
|
||||||
if ($selectedAssessorFilter !== '') {
|
if ($selectedAssessorFilter !== '') {
|
||||||
$performanceOrders = array_values(array_filter($orders, static function (array $order) use ($selectedAssessorFilter): bool {
|
$performanceOrders = array_values(array_filter($orders, static function (array $order) use ($selectedAssessorFilter): bool {
|
||||||
@ -989,7 +988,9 @@ try {
|
|||||||
$aSourceRow = (int) ($a['source_row'] ?? 0);
|
$aSourceRow = (int) ($a['source_row'] ?? 0);
|
||||||
$bSourceRow = (int) ($b['source_row'] ?? 0);
|
$bSourceRow = (int) ($b['source_row'] ?? 0);
|
||||||
if ($aSourceRow !== $bSourceRow) {
|
if ($aSourceRow !== $bSourceRow) {
|
||||||
return $bSourceRow <=> $aSourceRow;
|
return cc_test_is_tuani_recoverables_store_key($storeKey)
|
||||||
|
? ($aSourceRow <=> $bSourceRow)
|
||||||
|
: ($bSourceRow <=> $aSourceRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
$aId = (int) ($a['id'] ?? 0);
|
$aId = (int) ($a['id'] ?? 0);
|
||||||
@ -1022,7 +1023,7 @@ try {
|
|||||||
$assessorInboxLocked = $assessorInboxLockedCount > 0;
|
$assessorInboxLocked = $assessorInboxLockedCount > 0;
|
||||||
$assessorInboxLockedView = $assessorInboxLocked && $view === 'nuevos_hoy';
|
$assessorInboxLockedView = $assessorInboxLocked && $view === 'nuevos_hoy';
|
||||||
$assessorInboxLockNotice = $assessorInboxLocked
|
$assessorInboxLockNotice = $assessorInboxLocked
|
||||||
? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' pendiente' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' de Promo Final. Mientras existan pendientes, los pedidos de Día 1 mantienen el número oculto; cuando los muevas todos a Promo Final, el celular volverá a mostrarse.'
|
? 'Tienes ' . $assessorInboxLockedCount . ' pedido' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' pendiente' . ($assessorInboxLockedCount === 1 ? '' : 's') . ' de Promo Final. Mientras existan pendientes de sustento, los pedidos de Día 1 mantienen el número oculto; cuando ya no queden pendientes, el celular volverá a mostrarse.'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
$visibleOrdersTotalCount = count($visibleOrders);
|
$visibleOrdersTotalCount = count($visibleOrders);
|
||||||
@ -1033,45 +1034,46 @@ try {
|
|||||||
$todosPageEnd = 0;
|
$todosPageEnd = 0;
|
||||||
$todosPaginationPages = [];
|
$todosPaginationPages = [];
|
||||||
|
|
||||||
if ($view === 'todos') {
|
$requestedPage = (int) ($_GET['page'] ?? 1);
|
||||||
$requestedPage = (int) ($_GET['page'] ?? 1);
|
if ($requestedPage < 1) {
|
||||||
if ($requestedPage < 1) {
|
$requestedPage = 1;
|
||||||
$requestedPage = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$todosTotalPages = max(1, (int) ceil($visibleOrdersTotalCount / $todosPageSize));
|
|
||||||
if ($requestedPage > $todosTotalPages) {
|
|
||||||
$requestedPage = $todosTotalPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
$todosPage = $requestedPage;
|
|
||||||
$todosPageStart = ($todosPage - 1) * $todosPageSize;
|
|
||||||
$visibleOrders = array_slice($visibleOrders, $todosPageStart, $todosPageSize);
|
|
||||||
$todosPageEnd = $todosPageStart + count($visibleOrders);
|
|
||||||
|
|
||||||
if ($todosTotalPages <= 7) {
|
|
||||||
$todosPaginationPages = range(1, $todosTotalPages);
|
|
||||||
} else {
|
|
||||||
$todosPaginationPages[] = 1;
|
|
||||||
$windowStart = max(2, $todosPage - 1);
|
|
||||||
$windowEnd = min($todosTotalPages - 1, $todosPage + 1);
|
|
||||||
|
|
||||||
if ($windowStart > 2) {
|
|
||||||
$todosPaginationPages[] = 'ellipsis';
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($pageNumber = $windowStart; $pageNumber <= $windowEnd; $pageNumber++) {
|
|
||||||
$todosPaginationPages[] = $pageNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($windowEnd < $todosTotalPages - 1) {
|
|
||||||
$todosPaginationPages[] = 'ellipsis';
|
|
||||||
}
|
|
||||||
|
|
||||||
$todosPaginationPages[] = $todosTotalPages;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$todosTotalPages = max(1, (int) ceil($visibleOrdersTotalCount / $todosPageSize));
|
||||||
|
if ($requestedPage > $todosTotalPages) {
|
||||||
|
$requestedPage = $todosTotalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
$todosPage = $requestedPage;
|
||||||
|
$todosPageStart = ($todosPage - 1) * $todosPageSize;
|
||||||
|
$visibleOrders = array_slice($visibleOrders, $todosPageStart, $todosPageSize);
|
||||||
|
$todosPageEnd = $todosPageStart + count($visibleOrders);
|
||||||
|
|
||||||
|
if ($isTuaniRecuperablesStore) {
|
||||||
|
$visibleOrders = cc_test_mark_recoverables_duplicate_orders($pdo, $visibleOrders, $storeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($todosTotalPages <= 7) {
|
||||||
|
$todosPaginationPages = range(1, $todosTotalPages);
|
||||||
|
} else {
|
||||||
|
$todosPaginationPages[] = 1;
|
||||||
|
$windowStart = max(2, $todosPage - 1);
|
||||||
|
$windowEnd = min($todosTotalPages - 1, $todosPage + 1);
|
||||||
|
|
||||||
|
if ($windowStart > 2) {
|
||||||
|
$todosPaginationPages[] = 'ellipsis';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($pageNumber = $windowStart; $pageNumber <= $windowEnd; $pageNumber++) {
|
||||||
|
$todosPaginationPages[] = $pageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($windowEnd < $todosTotalPages - 1) {
|
||||||
|
$todosPaginationPages[] = 'ellipsis';
|
||||||
|
}
|
||||||
|
|
||||||
|
$todosPaginationPages[] = $todosTotalPages;
|
||||||
|
}
|
||||||
} catch (Throwable $exception) {
|
} catch (Throwable $exception) {
|
||||||
$errorMessage = $exception->getMessage();
|
$errorMessage = $exception->getMessage();
|
||||||
}
|
}
|
||||||
@ -1118,7 +1120,7 @@ if ($isTuaniRecuperablesStore) {
|
|||||||
$pageDescription = 'Bandeja compartida para que cualquier asesora gestione carritos recuperables con vistas de seguimiento y observados desde Drive.';
|
$pageDescription = 'Bandeja compartida para que cualquier asesora gestione carritos recuperables con vistas de seguimiento y observados desde Drive.';
|
||||||
} else {
|
} else {
|
||||||
$pageTitle = 'Pedidos Asignados | Bandeja de la asesora';
|
$pageTitle = 'Pedidos Asignados | Bandeja de la asesora';
|
||||||
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con vistas de hoy, últimos 3 días hábiles, estados, historial y acciones.';
|
$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 ? 'Carritos recuperables' : 'Pedidos Asignados';
|
||||||
@ -1495,7 +1497,7 @@ require_once 'layout_header.php';
|
|||||||
<?php if ($assessorInboxLockedView): ?>
|
<?php if ($assessorInboxLockedView): ?>
|
||||||
<span class="badge bg-warning-subtle text-warning-emphasis border">Pedidos con sustento pendiente para Promo Final</span>
|
<span class="badge bg-warning-subtle text-warning-emphasis border">Pedidos con sustento pendiente para Promo Final</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($view === 'todos'): ?>
|
<?php if ($todosTotalPages > 1): ?>
|
||||||
<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>
|
<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>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
@ -1506,9 +1508,9 @@ require_once 'layout_header.php';
|
|||||||
<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 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>
|
||||||
<div class="fw-semibold mb-1"><i class="bi bi-info-circle me-1"></i>Pedidos con sustento pendiente para Promo Final</div>
|
<div class="fw-semibold mb-1"><i class="bi bi-info-circle me-1"></i>Pedidos con sustento pendiente para Promo Final</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 class="small mb-0"><?php echo htmlspecialchars($assessorInboxLockNotice); ?> Usa <strong>Últimos 4 días hábiles</strong> si necesitas completar ese sustento más rápido.</div>
|
||||||
</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-info 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 4 días hábiles</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -1558,7 +1560,7 @@ require_once 'layout_header.php';
|
|||||||
};
|
};
|
||||||
$subirLogisticaButtonLabel = 'Subir pedido';
|
$subirLogisticaButtonLabel = 'Subir pedido';
|
||||||
$subirLogisticaNoteClass = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
$subirLogisticaNoteClass = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
||||||
$subirLogisticaNoteHtml = 'Selecciona <strong>CONFIRMADO CONTRAENTREGA</strong> o <strong>CONFIRMADO ENVIO</strong> para enviarlo a logística.';
|
$subirLogisticaNoteHtml = 'Selecciona <strong>CONFIRMADO CONTRAENTREGA</strong> o <strong>CONFIRMADO ENVIO</strong> y completa <strong>Confirmación de pedido</strong> (producto, cantidad y precio) para enviarlo a logística.';
|
||||||
if ($canShowTuaniLogisticaButton) {
|
if ($canShowTuaniLogisticaButton) {
|
||||||
if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') {
|
if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') {
|
||||||
if (!empty($order['ruta_contraentrega_pedido_id'])) {
|
if (!empty($order['ruta_contraentrega_pedido_id'])) {
|
||||||
@ -1589,7 +1591,7 @@ require_once 'layout_header.php';
|
|||||||
$phoneLocked = false;
|
$phoneLocked = false;
|
||||||
$phoneLockMessage = (string) ($order['llamada_bloqueada_dia3_mensaje'] ?? '');
|
$phoneLockMessage = (string) ($order['llamada_bloqueada_dia3_mensaje'] ?? '');
|
||||||
if ($phoneLocked && $phoneLockMessage === '') {
|
if ($phoneLocked && $phoneLockMessage === '') {
|
||||||
$phoneLockMessage = 'Celular oculto mientras completas los pendientes del Día 3.';
|
$phoneLockMessage = 'Celular oculto mientras completas los pendientes del Día 4.';
|
||||||
}
|
}
|
||||||
$phoneHiddenByDay1 = cc_test_followup_day1_phone_hidden($order, $pendingPromoFinalCounts);
|
$phoneHiddenByDay1 = cc_test_followup_day1_phone_hidden($order, $pendingPromoFinalCounts);
|
||||||
$phoneRowHiddenLabel = 'Número oculto';
|
$phoneRowHiddenLabel = 'Número oculto';
|
||||||
@ -1750,7 +1752,7 @@ require_once 'layout_header.php';
|
|||||||
data-phone="<?php echo $phoneLocked ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>"
|
data-phone="<?php echo $phoneLocked ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>"
|
||||||
data-order-label="<?php echo htmlspecialchars(cc_test_order_label($order)); ?>"
|
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')); ?>"
|
data-client-name="<?php echo htmlspecialchars(cc_test_display_value($order['nombre'], 'Cliente sin nombre')); ?>"
|
||||||
onclick="return registrarLlamada(event, this)"<?php echo $phoneLocked ? ' disabled aria-disabled="true" title="Llamada bloqueada hasta completar el sustento de Día 3"' : ''; ?>>
|
onclick="return registrarLlamada(event, this)"<?php echo $phoneLocked ? ' disabled aria-disabled="true" title="Llamada bloqueada hasta completar el sustento de Día 4"' : ''; ?>>
|
||||||
<i class="bi bi-telephone-outbound"></i> <?php echo $phoneLocked ? 'Llamada bloqueada' : 'Llamar / Gestionar'; ?>
|
<i class="bi bi-telephone-outbound"></i> <?php echo $phoneLocked ? 'Llamada bloqueada' : 'Llamar / Gestionar'; ?>
|
||||||
</button>
|
</button>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@ -1809,7 +1811,10 @@ require_once 'layout_header.php';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
||||||
<?php $promoFinalMoveAvailable = !empty($order['promo_final_habilitado']) && in_array($order['estado'], cc_test_followup_tracking_states(), true); ?>
|
<?php $promoFinalMoveAvailable = !empty($order['promo_final_habilitado']) && in_array($order['estado'], cc_test_followup_tracking_states(), true); ?>
|
||||||
<?php if ($promoFinalMoveAvailable || $promoFinalProofPath !== ''): ?>
|
<?php $promoFinalEvidenceAvailable = !empty($order['promo_final_evidencia_habilitada']); ?>
|
||||||
|
<?php $promoFinalThresholdDay = (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4); ?>
|
||||||
|
<?php $promoFinalEvidenceDay = (int) ($followupDayInfo['promo_final_evidence_day'] ?? max(1, $promoFinalThresholdDay - 1)); ?>
|
||||||
|
<?php if ($promoFinalMoveAvailable || $promoFinalEvidenceAvailable || $promoFinalProofPath !== ''): ?>
|
||||||
<div
|
<div
|
||||||
class="mt-2 js-promo-final-controls"
|
class="mt-2 js-promo-final-controls"
|
||||||
id="promo-final-controls-<?php echo htmlspecialchars($order['source_key']); ?>"
|
id="promo-final-controls-<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||||
@ -1818,16 +1823,18 @@ require_once 'layout_header.php';
|
|||||||
data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>"
|
data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>"
|
||||||
>
|
>
|
||||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||||
<button type="button" class="btn btn-outline-danger btn-sm px-3 <?php echo ($promoFinalMoveAvailable && $promoFinalProofPath === '') ? '' : 'd-none'; ?>" id="mover-promo-final-<?php echo htmlspecialchars($order['source_key']); ?>" onclick="guardarGestion('<?php echo htmlspecialchars($order['source_key']); ?>', this, { moverPromoFinal: true })">Mover a Promo Final</button>
|
<button type="button" class="btn btn-outline-danger btn-sm px-3 <?php echo $promoFinalMoveAvailable ? '' : 'd-none'; ?>" id="mover-promo-final-<?php echo htmlspecialchars($order['source_key']); ?>" onclick="guardarGestion('<?php echo htmlspecialchars($order['source_key']); ?>', this, { moverPromoFinal: true })">Mover a Promo Final</button>
|
||||||
<?php if ($promoFinalProofPath !== ''): ?>
|
<?php if ($promoFinalProofPath !== ''): ?>
|
||||||
<a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" class="btn btn-outline-success btn-sm" target="_blank" rel="noopener">Ver sustento Promo Final</a>
|
<a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" class="btn btn-outline-success btn-sm" target="_blank" rel="noopener">Ver sustento Promo Final</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="small mt-2 <?php echo $promoFinalProofPath !== '' ? 'text-success fw-semibold' : 'text-danger-emphasis'; ?>" id="mover-promo-final-note-<?php echo htmlspecialchars($order['source_key']); ?>">
|
<div class="small mt-2 <?php echo $promoFinalProofPath !== '' ? 'text-success fw-semibold' : 'text-danger-emphasis'; ?>" id="mover-promo-final-note-<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||||
<?php if ($promoFinalProofPath !== ''): ?>
|
<?php if ($promoFinalProofPath !== ''): ?>
|
||||||
Promo Final con sustento cargado<?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
Sustento de Promo Final cargado<?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
||||||
|
<?php elseif ((int) ($followupDayInfo['day_number'] ?? 0) < $promoFinalThresholdDay): ?>
|
||||||
|
Ya puedes cargar la imagen desde el Día <?php echo $promoFinalEvidenceDay; ?> para dejarlo listo; el movimiento a Promo Final sigue desde el Día <?php echo $promoFinalThresholdDay; ?>.
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
Debes subir una imagen de sustento y luego usar este botón para moverlo a Promo Final.
|
Ya puedes moverlo a Promo Final; si aún no cargaste la imagen, súbela antes de usar el botón.
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1958,6 +1965,7 @@ require_once 'layout_header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-text mt-3">Antes de usar <strong>Subir pedido</strong>, completa producto, cantidad y precio en esta sección.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
@ -2033,12 +2041,14 @@ require_once 'layout_header.php';
|
|||||||
</div>
|
</div>
|
||||||
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
||||||
<?php $canceladoProofPath = trim((string) ($order['cancelado_evidencia_path'] ?? '')); ?>
|
<?php $canceladoProofPath = trim((string) ($order['cancelado_evidencia_path'] ?? '')); ?>
|
||||||
<?php $mostrarPromoFinalProof = !empty($order['promo_final_habilitado']) || $promoFinalProofPath !== ''; ?>
|
<?php $promoFinalThresholdDay = (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4); ?>
|
||||||
|
<?php $promoFinalEvidenceDay = (int) ($followupDayInfo['promo_final_evidence_day'] ?? max(1, $promoFinalThresholdDay - 1)); ?>
|
||||||
|
<?php $mostrarPromoFinalProof = !empty($order['promo_final_evidencia_habilitada']) || $promoFinalProofPath !== ''; ?>
|
||||||
<?php $mostrarCanceladoProof = $order['estado'] === 'CANCELADO' || $canceladoProofPath !== ''; ?>
|
<?php $mostrarCanceladoProof = $order['estado'] === 'CANCELADO' || $canceladoProofPath !== ''; ?>
|
||||||
<div class="col-12 js-promo-final-proof-group <?php echo $mostrarPromoFinalProof ? '' : 'd-none'; ?>" data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>" data-promo-final-candidate="<?php echo !empty($order['promo_final_habilitado']) ? '1' : '0'; ?>" data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>">
|
<div class="col-12 js-promo-final-proof-group <?php echo $mostrarPromoFinalProof ? '' : 'd-none'; ?>" data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>" data-promo-final-candidate="<?php echo !empty($order['promo_final_habilitado']) ? '1' : '0'; ?>" data-promo-final-evidence-ready="<?php echo !empty($order['promo_final_evidencia_habilitada']) ? '1' : '0'; ?>" data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>">
|
||||||
<label for="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Imagen de sustento para Promo Final</label>
|
<label for="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Imagen de sustento para Promo Final</label>
|
||||||
<input type="file" class="form-control" id="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" accept=".jpg,.jpeg,.png,.webp">
|
<input type="file" class="form-control" id="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" accept=".jpg,.jpeg,.png,.webp">
|
||||||
<div class="form-text">Obligatoria al mover el pedido a <strong>Promo Final</strong> desde el Día <?php echo (int) ($followupDayInfo['promo_final_threshold_day'] ?? 3); ?>. La supervisora podrá revisarla.</div>
|
<div class="form-text">Puedes cargarla desde el Día <?php echo $promoFinalEvidenceDay; ?>. Para mover el pedido a <strong>Promo Final</strong> seguirá siendo obligatoria desde el Día <?php echo $promoFinalThresholdDay; ?>. La supervisora podrá revisarla.</div>
|
||||||
<?php if ($promoFinalProofPath !== ''): ?>
|
<?php if ($promoFinalProofPath !== ''): ?>
|
||||||
<div class="small text-success mt-2">
|
<div class="small text-success mt-2">
|
||||||
Sustento actual: <a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" target="_blank" rel="noopener">ver imagen</a><?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
Sustento actual: <a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" target="_blank" rel="noopener">ver imagen</a><?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
||||||
@ -2203,7 +2213,7 @@ require_once 'layout_header.php';
|
|||||||
<div class="modal-footer d-flex justify-content-between flex-wrap gap-2">
|
<div class="modal-footer d-flex justify-content-between flex-wrap gap-2">
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
<?php if ($canShowTuaniLogisticaButton): ?>
|
<?php if ($canShowTuaniLogisticaButton): ?>
|
||||||
<strong>Guardar gestión</strong> solo guarda el historial. Para enviarlo a logística, usa el <strong>botón del encabezado</strong> junto al nombre del cliente.
|
<strong>Guardar gestión</strong> solo guarda el historial. Para enviarlo a logística, completa <strong>Confirmación de pedido</strong> (producto, cantidad y precio) y usa el <strong>botón del encabezado</strong> junto al nombre del cliente.
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
Los cambios se guardan en la base local del módulo de prueba.
|
Los cambios se guardan en la base local del módulo de prueba.
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -2224,7 +2234,7 @@ require_once 'layout_header.php';
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($view === 'todos'): ?>
|
<?php if ($todosTotalPages > 1): ?>
|
||||||
<div class="border-top bg-light-subtle px-3 py-3 d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-2">
|
<div class="border-top bg-light-subtle px-3 py-3 d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-2">
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
<?php if ($visibleOrdersTotalCount > 0): ?>
|
<?php if ($visibleOrdersTotalCount > 0): ?>
|
||||||
@ -2752,23 +2762,26 @@ function updateLogisticaButton(sourceKey) {
|
|||||||
button.textContent = 'Subir pedido';
|
button.textContent = 'Subir pedido';
|
||||||
if (note) {
|
if (note) {
|
||||||
note.className = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
note.className = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
||||||
note.textContent = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO para enviarlo a logística.';
|
note.textContent = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO y completa Confirmación de pedido (producto, cantidad y precio) para enviarlo a logística.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PROMO_FINAL_FOLLOWUP_STATES = ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO', 'SE ENVIO NUMERO DE CUENTA'];
|
||||||
|
|
||||||
function updatePromoFinalControls(sourceKey) {
|
function updatePromoFinalControls(sourceKey) {
|
||||||
const estado = document.getElementById('estado-' + sourceKey)?.value || '';
|
const estado = document.getElementById('estado-' + sourceKey)?.value || '';
|
||||||
const followupStates = ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO', 'SE ENVIO NUMERO DE CUENTA'];
|
const followupStates = PROMO_FINAL_FOLLOWUP_STATES;
|
||||||
|
|
||||||
document.querySelectorAll('.js-promo-final-controls[data-source-key="' + sourceKey + '"]').forEach(block => {
|
document.querySelectorAll('.js-promo-final-controls[data-source-key="' + sourceKey + '"]').forEach(block => {
|
||||||
const isCandidate = block.dataset.promoFinalCandidate === '1';
|
const isCandidate = block.dataset.promoFinalCandidate === '1';
|
||||||
const hasExisting = block.dataset.hasExisting === '1';
|
const hasExisting = block.dataset.hasExisting === '1';
|
||||||
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
||||||
block.classList.toggle('d-none', !hasExisting && !canUsePromoFinal);
|
const canUploadPromoFinalEvidence = block.dataset.promoFinalEvidenceReady === '1' && followupStates.includes(estado);
|
||||||
|
block.classList.toggle('d-none', !hasExisting && !canUsePromoFinal && !canUploadPromoFinalEvidence);
|
||||||
|
|
||||||
const moveButton = block.querySelector('button[id^="mover-promo-final-"]');
|
const moveButton = block.querySelector('button[id^="mover-promo-final-"]');
|
||||||
if (moveButton) {
|
if (moveButton) {
|
||||||
moveButton.classList.toggle('d-none', !canUsePromoFinal || hasExisting);
|
moveButton.classList.toggle('d-none', !canUsePromoFinal);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2776,7 +2789,8 @@ function updatePromoFinalControls(sourceKey) {
|
|||||||
const isCandidate = group.dataset.promoFinalCandidate === '1';
|
const isCandidate = group.dataset.promoFinalCandidate === '1';
|
||||||
const hasExisting = group.dataset.hasExisting === '1';
|
const hasExisting = group.dataset.hasExisting === '1';
|
||||||
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
||||||
group.classList.toggle('d-none', !hasExisting && !canUsePromoFinal);
|
const canUploadPromoFinalEvidence = group.dataset.promoFinalEvidenceReady === '1' && followupStates.includes(estado);
|
||||||
|
group.classList.toggle('d-none', !hasExisting && !canUsePromoFinal && !canUploadPromoFinalEvidence);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2969,6 +2983,33 @@ function guardarGestion(sourceKey, trigger, options = {}) {
|
|||||||
const cancelProofGroup = document.querySelector('.js-cancelado-proof-group[data-source-key="' + sourceKey + '"]');
|
const cancelProofGroup = document.querySelector('.js-cancelado-proof-group[data-source-key="' + sourceKey + '"]');
|
||||||
const hasExistingPromoProof = promoProofGroup?.dataset.hasExisting === '1';
|
const hasExistingPromoProof = promoProofGroup?.dataset.hasExisting === '1';
|
||||||
const hasExistingCancelProof = cancelProofGroup?.dataset.hasExisting === '1';
|
const hasExistingCancelProof = cancelProofGroup?.dataset.hasExisting === '1';
|
||||||
|
const followupStates = PROMO_FINAL_FOLLOWUP_STATES;
|
||||||
|
const canUploadPromoFinalEvidence = promoProofGroup?.dataset.promoFinalEvidenceReady === '1' && followupStates.includes(estado);
|
||||||
|
|
||||||
|
if (subirLogistica) {
|
||||||
|
const confirmacionProducto = document.getElementById('confirmacion_producto-' + sourceKey);
|
||||||
|
const confirmacionCantidad = document.getElementById('confirmacion_cantidad-' + sourceKey);
|
||||||
|
const confirmacionPrecio = document.getElementById('confirmacion_precio-' + sourceKey);
|
||||||
|
const confirmacionFields = [
|
||||||
|
confirmacionProducto,
|
||||||
|
confirmacionCantidad,
|
||||||
|
confirmacionPrecio
|
||||||
|
];
|
||||||
|
const missingField = confirmacionFields.find(field => !(field?.value || '').trim());
|
||||||
|
if (missingField) {
|
||||||
|
alert('Completa producto, cantidad y precio en Confirmación de pedido antes de subir el pedido.');
|
||||||
|
missingField.focus();
|
||||||
|
if (typeof missingField.select === 'function') {
|
||||||
|
missingField.select();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!moverPromoFinal && promoProofInput?.files?.[0] && !canUploadPromoFinalEvidence) {
|
||||||
|
alert('La imagen de sustento solo se puede cargar desde el Día 3.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (moverPromoFinal && !hasExistingPromoProof && (!promoProofInput?.files || promoProofInput.files.length === 0)) {
|
if (moverPromoFinal && !hasExistingPromoProof && (!promoProofInput?.files || promoProofInput.files.length === 0)) {
|
||||||
alert('Debes subir una imagen de sustento antes de mover el pedido a Promo Final.');
|
alert('Debes subir una imagen de sustento antes de mover el pedido a Promo Final.');
|
||||||
@ -3017,7 +3058,7 @@ function guardarGestion(sourceKey, trigger, options = {}) {
|
|||||||
body.append(key, value);
|
body.append(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moverPromoFinal && promoProofInput?.files?.[0]) {
|
if ((moverPromoFinal || canUploadPromoFinalEvidence) && promoProofInput?.files?.[0]) {
|
||||||
body.append('promo_final_evidencia', promoProofInput.files[0]);
|
body.append('promo_final_evidencia', promoProofInput.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3038,7 +3079,7 @@ function guardarGestion(sourceKey, trigger, options = {}) {
|
|||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
throw new Error(data.message || 'No se pudo guardar la gestión');
|
throw new Error(data.message || 'No se pudo guardar la gestión');
|
||||||
}
|
}
|
||||||
if (subirLogistica || moverPromoFinal) {
|
if (subirLogistica || moverPromoFinal || (canUploadPromoFinalEvidence && promoProofInput?.files?.[0])) {
|
||||||
alert(data.message || 'Gestión actualizada correctamente.');
|
alert(data.message || 'Gestión actualizada correctamente.');
|
||||||
}
|
}
|
||||||
ccCallCenterPanelNavigate();
|
ccCallCenterPanelNavigate();
|
||||||
|
|||||||
@ -347,8 +347,8 @@ $_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);
|
$_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key($view);
|
||||||
|
|
||||||
$viewStatesNote = $isTuaniRecuperablesStore
|
$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: <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: 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 todos los pedidos; desde ese momento el número se mantiene visible para llamar y gestionar, y el sustento solo se usa para mover el pedido a Promo Final. En las vistas <strong>Todos</strong> y <strong>Últimos 3 días hábiles</strong>, la llamada y la gestión siguen disponibles desde el panel.';
|
: '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;
|
$errorMessage = null;
|
||||||
$noticeMessage = null;
|
$noticeMessage = null;
|
||||||
@ -912,6 +912,7 @@ try {
|
|||||||
$order['seguimiento_dia_info'] = $followupDayInfo;
|
$order['seguimiento_dia_info'] = $followupDayInfo;
|
||||||
$order['dias_seguimiento'] = $followupDayInfo['day_number'] ?? null;
|
$order['dias_seguimiento'] = $followupDayInfo['day_number'] ?? null;
|
||||||
$order['promo_final_habilitado'] = !empty($followupDayInfo['is_promo_final']);
|
$order['promo_final_habilitado'] = !empty($followupDayInfo['is_promo_final']);
|
||||||
|
$order['promo_final_evidencia_habilitada'] = !empty($followupDayInfo['can_upload_promo_final_evidence']);
|
||||||
$order['promo_final_evidencia_cargada'] = trim((string) ($order['promo_final_evidencia_path'] ?? '')) !== '';
|
$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['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['promo_final_pendiente'] = $order['promo_final_habilitado'] && !$order['promo_final_evidencia_cargada'];
|
||||||
@ -973,8 +974,6 @@ try {
|
|||||||
|
|
||||||
$pendingPromoFinalCounts = cc_test_followup_pending_promo_final_counts($orders);
|
$pendingPromoFinalCounts = cc_test_followup_pending_promo_final_counts($orders);
|
||||||
|
|
||||||
$orders = cc_test_mark_recoverables_duplicate_orders($pdo, $orders, $storeKey);
|
|
||||||
|
|
||||||
$performanceOrders = $selectedAssessorFilter !== ''
|
$performanceOrders = $selectedAssessorFilter !== ''
|
||||||
? array_values(array_filter($orders, static function (array $order) use ($selectedAssessorFilter): bool {
|
? array_values(array_filter($orders, static function (array $order) use ($selectedAssessorFilter): bool {
|
||||||
return ($order['matches_assessor_filter'] ?? false) === true;
|
return ($order['matches_assessor_filter'] ?? false) === true;
|
||||||
@ -1174,7 +1173,9 @@ try {
|
|||||||
$aSourceRow = (int) ($a['source_row'] ?? 0);
|
$aSourceRow = (int) ($a['source_row'] ?? 0);
|
||||||
$bSourceRow = (int) ($b['source_row'] ?? 0);
|
$bSourceRow = (int) ($b['source_row'] ?? 0);
|
||||||
if ($aSourceRow !== $bSourceRow) {
|
if ($aSourceRow !== $bSourceRow) {
|
||||||
return $bSourceRow <=> $aSourceRow;
|
return cc_test_is_tuani_recoverables_store_key($storeKey)
|
||||||
|
? ($aSourceRow <=> $bSourceRow)
|
||||||
|
: ($bSourceRow <=> $aSourceRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
$aId = (int) ($a['id'] ?? 0);
|
$aId = (int) ($a['id'] ?? 0);
|
||||||
@ -1201,45 +1202,46 @@ try {
|
|||||||
$todosPageEnd = 0;
|
$todosPageEnd = 0;
|
||||||
$todosPaginationPages = [];
|
$todosPaginationPages = [];
|
||||||
|
|
||||||
if ($view === 'todos') {
|
$requestedPage = (int) ($_GET['page'] ?? 1);
|
||||||
$requestedPage = (int) ($_GET['page'] ?? 1);
|
if ($requestedPage < 1) {
|
||||||
if ($requestedPage < 1) {
|
$requestedPage = 1;
|
||||||
$requestedPage = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$todosTotalPages = max(1, (int) ceil($visibleOrdersTotalCount / $todosPageSize));
|
|
||||||
if ($requestedPage > $todosTotalPages) {
|
|
||||||
$requestedPage = $todosTotalPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
$todosPage = $requestedPage;
|
|
||||||
$todosPageStart = ($todosPage - 1) * $todosPageSize;
|
|
||||||
$visibleOrders = array_slice($visibleOrders, $todosPageStart, $todosPageSize);
|
|
||||||
$todosPageEnd = $todosPageStart + count($visibleOrders);
|
|
||||||
|
|
||||||
if ($todosTotalPages <= 7) {
|
|
||||||
$todosPaginationPages = range(1, $todosTotalPages);
|
|
||||||
} else {
|
|
||||||
$todosPaginationPages[] = 1;
|
|
||||||
$windowStart = max(2, $todosPage - 1);
|
|
||||||
$windowEnd = min($todosTotalPages - 1, $todosPage + 1);
|
|
||||||
|
|
||||||
if ($windowStart > 2) {
|
|
||||||
$todosPaginationPages[] = 'ellipsis';
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($pageNumber = $windowStart; $pageNumber <= $windowEnd; $pageNumber++) {
|
|
||||||
$todosPaginationPages[] = $pageNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($windowEnd < $todosTotalPages - 1) {
|
|
||||||
$todosPaginationPages[] = 'ellipsis';
|
|
||||||
}
|
|
||||||
|
|
||||||
$todosPaginationPages[] = $todosTotalPages;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$todosTotalPages = max(1, (int) ceil($visibleOrdersTotalCount / $todosPageSize));
|
||||||
|
if ($requestedPage > $todosTotalPages) {
|
||||||
|
$requestedPage = $todosTotalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
$todosPage = $requestedPage;
|
||||||
|
$todosPageStart = ($todosPage - 1) * $todosPageSize;
|
||||||
|
$visibleOrders = array_slice($visibleOrders, $todosPageStart, $todosPageSize);
|
||||||
|
$todosPageEnd = $todosPageStart + count($visibleOrders);
|
||||||
|
|
||||||
|
if ($isTuaniRecuperablesStore) {
|
||||||
|
$visibleOrders = cc_test_mark_recoverables_duplicate_orders($pdo, $visibleOrders, $storeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($todosTotalPages <= 7) {
|
||||||
|
$todosPaginationPages = range(1, $todosTotalPages);
|
||||||
|
} else {
|
||||||
|
$todosPaginationPages[] = 1;
|
||||||
|
$windowStart = max(2, $todosPage - 1);
|
||||||
|
$windowEnd = min($todosTotalPages - 1, $todosPage + 1);
|
||||||
|
|
||||||
|
if ($windowStart > 2) {
|
||||||
|
$todosPaginationPages[] = 'ellipsis';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($pageNumber = $windowStart; $pageNumber <= $windowEnd; $pageNumber++) {
|
||||||
|
$todosPaginationPages[] = $pageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($windowEnd < $todosTotalPages - 1) {
|
||||||
|
$todosPaginationPages[] = 'ellipsis';
|
||||||
|
}
|
||||||
|
|
||||||
|
$todosPaginationPages[] = $todosTotalPages;
|
||||||
|
}
|
||||||
} catch (Throwable $exception) {
|
} catch (Throwable $exception) {
|
||||||
$errorMessage = $exception->getMessage();
|
$errorMessage = $exception->getMessage();
|
||||||
}
|
}
|
||||||
@ -1908,7 +1910,7 @@ require_once 'layout_header.php';
|
|||||||
<p class="text-muted small mb-1"><?php echo $viewStatesNote; ?></p>
|
<p class="text-muted small mb-1"><?php echo $viewStatesNote; ?></p>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-light text-dark border"><?php echo (int) $visibleOrdersTotalCount; ?> pedidos únicos en esta bandeja</span>
|
<span class="badge bg-light text-dark border"><?php echo (int) $visibleOrdersTotalCount; ?> pedidos únicos en esta bandeja</span>
|
||||||
<?php if ($view === 'todos'): ?>
|
<?php if ($todosTotalPages > 1): ?>
|
||||||
<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>
|
<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>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
@ -1979,7 +1981,7 @@ require_once 'layout_header.php';
|
|||||||
};
|
};
|
||||||
$subirLogisticaButtonLabel = 'Subir pedido';
|
$subirLogisticaButtonLabel = 'Subir pedido';
|
||||||
$subirLogisticaNoteClass = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
$subirLogisticaNoteClass = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
||||||
$subirLogisticaNoteHtml = 'Selecciona <strong>CONFIRMADO CONTRAENTREGA</strong> o <strong>CONFIRMADO ENVIO</strong> para enviarlo a logística.';
|
$subirLogisticaNoteHtml = 'Selecciona <strong>CONFIRMADO CONTRAENTREGA</strong> o <strong>CONFIRMADO ENVIO</strong> y completa <strong>Confirmación de pedido</strong> (producto, cantidad y precio) para enviarlo a logística.';
|
||||||
if ($canShowTuaniLogisticaButton) {
|
if ($canShowTuaniLogisticaButton) {
|
||||||
if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') {
|
if ($order['estado'] === 'CONFIRMADO CONTRAENTREGA') {
|
||||||
if (!empty($order['ruta_contraentrega_pedido_id'])) {
|
if (!empty($order['ruta_contraentrega_pedido_id'])) {
|
||||||
@ -2105,7 +2107,7 @@ require_once 'layout_header.php';
|
|||||||
$phoneLocked = false;
|
$phoneLocked = false;
|
||||||
$phoneLockMessage = (string) ($order['llamada_bloqueada_dia3_mensaje'] ?? '');
|
$phoneLockMessage = (string) ($order['llamada_bloqueada_dia3_mensaje'] ?? '');
|
||||||
if ($phoneLocked && $phoneLockMessage === '') {
|
if ($phoneLocked && $phoneLockMessage === '') {
|
||||||
$phoneLockMessage = 'Celular oculto mientras completas los pendientes del Día 3.';
|
$phoneLockMessage = 'Celular oculto mientras completas los pendientes del Día 4.';
|
||||||
}
|
}
|
||||||
$phoneRowHiddenLabel = 'Número oculto';
|
$phoneRowHiddenLabel = 'Número oculto';
|
||||||
$phoneRowHiddenTitle = 'Número oculto hasta completar Promo Final';
|
$phoneRowHiddenTitle = 'Número oculto hasta completar Promo Final';
|
||||||
@ -2121,7 +2123,7 @@ require_once 'layout_header.php';
|
|||||||
data-phone-hidden-day1="<?php echo $phoneHiddenByDay1 ? '1' : '0'; ?>"
|
data-phone-hidden-day1="<?php echo $phoneHiddenByDay1 ? '1' : '0'; ?>"
|
||||||
data-order-label="<?php echo htmlspecialchars(cc_test_order_label($order)); ?>"
|
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')); ?>"
|
data-client-name="<?php echo htmlspecialchars(cc_test_display_value($order['nombre'], 'Cliente sin nombre')); ?>"
|
||||||
onclick="return registrarLlamada(event, this)"<?php echo $phoneLocked ? ' disabled aria-disabled="true" title="Llamada bloqueada hasta completar el sustento de Día 3"' : ''; ?>>
|
onclick="return registrarLlamada(event, this)"<?php echo $phoneLocked ? ' disabled aria-disabled="true" title="Llamada bloqueada hasta completar el sustento de Día 4"' : ''; ?>>
|
||||||
<i class="bi bi-telephone-outbound"></i> <?php echo $phoneLocked ? 'Llamada bloqueada' : 'Llamar / Gestionar'; ?>
|
<i class="bi bi-telephone-outbound"></i> <?php echo $phoneLocked ? 'Llamada bloqueada' : 'Llamar / Gestionar'; ?>
|
||||||
</button>
|
</button>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@ -2267,7 +2269,10 @@ require_once 'layout_header.php';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
||||||
<?php $promoFinalMoveAvailable = !empty($order['promo_final_habilitado']) && in_array($order['estado'], cc_test_followup_tracking_states(), true); ?>
|
<?php $promoFinalMoveAvailable = !empty($order['promo_final_habilitado']) && in_array($order['estado'], cc_test_followup_tracking_states(), true); ?>
|
||||||
<?php if ($promoFinalMoveAvailable || $promoFinalProofPath !== ''): ?>
|
<?php $promoFinalEvidenceAvailable = !empty($order['promo_final_evidencia_habilitada']); ?>
|
||||||
|
<?php $promoFinalThresholdDay = (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4); ?>
|
||||||
|
<?php $promoFinalEvidenceDay = (int) ($followupDayInfo['promo_final_evidence_day'] ?? max(1, $promoFinalThresholdDay - 1)); ?>
|
||||||
|
<?php if ($promoFinalMoveAvailable || $promoFinalEvidenceAvailable || $promoFinalProofPath !== ''): ?>
|
||||||
<div
|
<div
|
||||||
class="mt-2 js-promo-final-controls"
|
class="mt-2 js-promo-final-controls"
|
||||||
id="promo-final-controls-<?php echo htmlspecialchars($order['source_key']); ?>"
|
id="promo-final-controls-<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||||
@ -2276,16 +2281,18 @@ require_once 'layout_header.php';
|
|||||||
data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>"
|
data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>"
|
||||||
>
|
>
|
||||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||||
<button type="button" class="btn btn-outline-danger btn-sm px-3 <?php echo ($promoFinalMoveAvailable && $promoFinalProofPath === '') ? '' : 'd-none'; ?>" id="mover-promo-final-<?php echo htmlspecialchars($order['source_key']); ?>" onclick="guardarGestion('<?php echo htmlspecialchars($order['source_key']); ?>', this, { moverPromoFinal: true })">Mover a Promo Final</button>
|
<button type="button" class="btn btn-outline-danger btn-sm px-3 <?php echo $promoFinalMoveAvailable ? '' : 'd-none'; ?>" id="mover-promo-final-<?php echo htmlspecialchars($order['source_key']); ?>" onclick="guardarGestion('<?php echo htmlspecialchars($order['source_key']); ?>', this, { moverPromoFinal: true })">Mover a Promo Final</button>
|
||||||
<?php if ($promoFinalProofPath !== ''): ?>
|
<?php if ($promoFinalProofPath !== ''): ?>
|
||||||
<a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" class="btn btn-outline-success btn-sm" target="_blank" rel="noopener">Ver sustento Promo Final</a>
|
<a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" class="btn btn-outline-success btn-sm" target="_blank" rel="noopener">Ver sustento Promo Final</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="small mt-2 <?php echo $promoFinalProofPath !== '' ? 'text-success fw-semibold' : 'text-danger-emphasis'; ?>" id="mover-promo-final-note-<?php echo htmlspecialchars($order['source_key']); ?>">
|
<div class="small mt-2 <?php echo $promoFinalProofPath !== '' ? 'text-success fw-semibold' : 'text-danger-emphasis'; ?>" id="mover-promo-final-note-<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||||
<?php if ($promoFinalProofPath !== ''): ?>
|
<?php if ($promoFinalProofPath !== ''): ?>
|
||||||
Promo Final con sustento cargado<?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
Sustento de Promo Final cargado<?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
||||||
|
<?php elseif ((int) ($followupDayInfo['day_number'] ?? 0) < $promoFinalThresholdDay): ?>
|
||||||
|
Ya puedes cargar la imagen desde el Día <?php echo $promoFinalEvidenceDay; ?> para dejarlo listo; el movimiento a Promo Final sigue desde el Día <?php echo $promoFinalThresholdDay; ?>.
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
Debes subir una imagen de sustento y luego usar este botón para moverlo a Promo Final.
|
Ya puedes moverlo a Promo Final; si aún no cargaste la imagen, súbela antes de usar el botón.
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2418,6 +2425,7 @@ require_once 'layout_header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-text mt-3">Antes de usar <strong>Subir pedido</strong>, completa producto, cantidad y precio en esta sección.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
@ -2496,12 +2504,14 @@ require_once 'layout_header.php';
|
|||||||
</div>
|
</div>
|
||||||
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
<?php $promoFinalProofPath = trim((string) ($order['promo_final_evidencia_path'] ?? '')); ?>
|
||||||
<?php $canceladoProofPath = trim((string) ($order['cancelado_evidencia_path'] ?? '')); ?>
|
<?php $canceladoProofPath = trim((string) ($order['cancelado_evidencia_path'] ?? '')); ?>
|
||||||
<?php $mostrarPromoFinalProof = !empty($order['promo_final_habilitado']) || $promoFinalProofPath !== ''; ?>
|
<?php $promoFinalThresholdDay = (int) ($followupDayInfo['promo_final_threshold_day'] ?? 4); ?>
|
||||||
|
<?php $promoFinalEvidenceDay = (int) ($followupDayInfo['promo_final_evidence_day'] ?? max(1, $promoFinalThresholdDay - 1)); ?>
|
||||||
|
<?php $mostrarPromoFinalProof = !empty($order['promo_final_evidencia_habilitada']) || $promoFinalProofPath !== ''; ?>
|
||||||
<?php $mostrarCanceladoProof = $order['estado'] === 'CANCELADO' || $canceladoProofPath !== ''; ?>
|
<?php $mostrarCanceladoProof = $order['estado'] === 'CANCELADO' || $canceladoProofPath !== ''; ?>
|
||||||
<div class="col-12 js-promo-final-proof-group <?php echo $mostrarPromoFinalProof ? '' : 'd-none'; ?>" data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>" data-promo-final-candidate="<?php echo !empty($order['promo_final_habilitado']) ? '1' : '0'; ?>" data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>">
|
<div class="col-12 js-promo-final-proof-group <?php echo $mostrarPromoFinalProof ? '' : 'd-none'; ?>" data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>" data-promo-final-candidate="<?php echo !empty($order['promo_final_habilitado']) ? '1' : '0'; ?>" data-promo-final-evidence-ready="<?php echo !empty($order['promo_final_evidencia_habilitada']) ? '1' : '0'; ?>" data-has-existing="<?php echo $promoFinalProofPath !== '' ? '1' : '0'; ?>">
|
||||||
<label for="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Imagen de sustento para Promo Final</label>
|
<label for="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Imagen de sustento para Promo Final</label>
|
||||||
<input type="file" class="form-control" id="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" accept=".jpg,.jpeg,.png,.webp">
|
<input type="file" class="form-control" id="promo_final_evidencia-<?php echo htmlspecialchars($order['source_key']); ?>" accept=".jpg,.jpeg,.png,.webp">
|
||||||
<div class="form-text">Obligatoria al mover el pedido a <strong>Promo Final</strong> desde el Día <?php echo (int) ($followupDayInfo['promo_final_threshold_day'] ?? 3); ?>. La supervisora podrá revisarla.</div>
|
<div class="form-text">Puedes cargarla desde el Día <?php echo $promoFinalEvidenceDay; ?>. Para mover el pedido a <strong>Promo Final</strong> seguirá siendo obligatoria desde el Día <?php echo $promoFinalThresholdDay; ?>. La supervisora podrá revisarla.</div>
|
||||||
<?php if ($promoFinalProofPath !== ''): ?>
|
<?php if ($promoFinalProofPath !== ''): ?>
|
||||||
<div class="small text-success mt-2">
|
<div class="small text-success mt-2">
|
||||||
Sustento actual: <a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" target="_blank" rel="noopener">ver imagen</a><?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
Sustento actual: <a href="<?php echo htmlspecialchars($promoFinalProofPath); ?>" target="_blank" rel="noopener">ver imagen</a><?php if (!empty($order['promo_final_evidencia_subido_at'])): ?> · <?php echo htmlspecialchars(cc_test_format_datetime($order['promo_final_evidencia_subido_at'] ?? null, '')); ?><?php endif; ?>.
|
||||||
@ -2661,7 +2671,7 @@ require_once 'layout_header.php';
|
|||||||
<div class="modal-footer d-flex justify-content-between flex-wrap gap-2">
|
<div class="modal-footer d-flex justify-content-between flex-wrap gap-2">
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
<?php if ($canShowTuaniLogisticaButton): ?>
|
<?php if ($canShowTuaniLogisticaButton): ?>
|
||||||
<strong>Guardar gestión</strong> solo guarda el historial. Para enviarlo a logística, usa el <strong>botón del encabezado</strong> junto al nombre del cliente.
|
<strong>Guardar gestión</strong> solo guarda el historial. Para enviarlo a logística, completa <strong>Confirmación de pedido</strong> (producto, cantidad y precio) y usa el <strong>botón del encabezado</strong> junto al nombre del cliente.
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
Los cambios se guardan en la base local del módulo de prueba.
|
Los cambios se guardan en la base local del módulo de prueba.
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -2687,7 +2697,7 @@ require_once 'layout_header.php';
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($view === 'todos'): ?>
|
<?php if ($todosTotalPages > 1): ?>
|
||||||
<div class="border-top bg-light-subtle px-3 py-3 d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-2">
|
<div class="border-top bg-light-subtle px-3 py-3 d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-2">
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
<?php if ($visibleOrdersTotalCount > 0): ?>
|
<?php if ($visibleOrdersTotalCount > 0): ?>
|
||||||
@ -2703,7 +2713,7 @@ require_once 'layout_header.php';
|
|||||||
<?php if ($todosPage <= 1): ?>
|
<?php if ($todosPage <= 1): ?>
|
||||||
<span class="page-link">«</span>
|
<span class="page-link">«</span>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a class="page-link" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => 'todos', 'store' => $storeKey, 'page' => $todosPage - 1], $persistedAssessorParams))); ?>" aria-label="Anterior">«</a>
|
<a class="page-link" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $view, 'store' => $storeKey, 'page' => $todosPage - 1], $persistedAssessorParams))); ?>" aria-label="Anterior">«</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</li>
|
</li>
|
||||||
<?php foreach ($todosPaginationPages as $pageEntry): ?>
|
<?php foreach ($todosPaginationPages as $pageEntry): ?>
|
||||||
@ -2714,7 +2724,7 @@ require_once 'layout_header.php';
|
|||||||
<?php if ((int) $pageEntry === $todosPage): ?>
|
<?php if ((int) $pageEntry === $todosPage): ?>
|
||||||
<span class="page-link" aria-current="page"><?php echo (int) $pageEntry; ?></span>
|
<span class="page-link" aria-current="page"><?php echo (int) $pageEntry; ?></span>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a class="page-link" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => 'todos', 'store' => $storeKey, 'page' => (int) $pageEntry], $persistedAssessorParams))); ?>"><?php echo (int) $pageEntry; ?></a>
|
<a class="page-link" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $view, 'store' => $storeKey, 'page' => (int) $pageEntry], $persistedAssessorParams))); ?>"><?php echo (int) $pageEntry; ?></a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</li>
|
</li>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -2723,7 +2733,7 @@ require_once 'layout_header.php';
|
|||||||
<?php if ($todosPage >= $todosTotalPages): ?>
|
<?php if ($todosPage >= $todosTotalPages): ?>
|
||||||
<span class="page-link">»</span>
|
<span class="page-link">»</span>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a class="page-link" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => 'todos', 'store' => $storeKey, 'page' => $todosPage + 1], $persistedAssessorParams))); ?>" aria-label="Siguiente">»</a>
|
<a class="page-link" href="<?php echo htmlspecialchars(cc_test_build_url('gestiones_callcenter.php', array_merge(['view' => $view, 'store' => $storeKey, 'page' => $todosPage + 1], $persistedAssessorParams))); ?>" aria-label="Siguiente">»</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -3250,23 +3260,26 @@ function updateLogisticaButton(sourceKey) {
|
|||||||
button.textContent = 'Subir pedido';
|
button.textContent = 'Subir pedido';
|
||||||
if (note) {
|
if (note) {
|
||||||
note.className = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
note.className = 'alert alert-secondary py-2 px-3 mt-2 mb-0';
|
||||||
note.textContent = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO para enviarlo a logística.';
|
note.textContent = 'Selecciona CONFIRMADO CONTRAENTREGA o CONFIRMADO ENVIO y completa Confirmación de pedido (producto, cantidad y precio) para enviarlo a logística.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PROMO_FINAL_FOLLOWUP_STATES = ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO', 'SE ENVIO NUMERO DE CUENTA'];
|
||||||
|
|
||||||
function updatePromoFinalControls(sourceKey) {
|
function updatePromoFinalControls(sourceKey) {
|
||||||
const estado = document.getElementById('estado-' + sourceKey)?.value || '';
|
const estado = document.getElementById('estado-' + sourceKey)?.value || '';
|
||||||
const followupStates = ['POR LLAMAR', 'DEVOLVER LLAMADA', 'OBSERVADO', 'SE ENVIO NUMERO DE CUENTA'];
|
const followupStates = PROMO_FINAL_FOLLOWUP_STATES;
|
||||||
|
|
||||||
document.querySelectorAll('.js-promo-final-controls[data-source-key="' + sourceKey + '"]').forEach(block => {
|
document.querySelectorAll('.js-promo-final-controls[data-source-key="' + sourceKey + '"]').forEach(block => {
|
||||||
const isCandidate = block.dataset.promoFinalCandidate === '1';
|
const isCandidate = block.dataset.promoFinalCandidate === '1';
|
||||||
const hasExisting = block.dataset.hasExisting === '1';
|
const hasExisting = block.dataset.hasExisting === '1';
|
||||||
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
||||||
block.classList.toggle('d-none', !hasExisting && !canUsePromoFinal);
|
const canUploadPromoFinalEvidence = block.dataset.promoFinalEvidenceReady === '1' && followupStates.includes(estado);
|
||||||
|
block.classList.toggle('d-none', !hasExisting && !canUsePromoFinal && !canUploadPromoFinalEvidence);
|
||||||
|
|
||||||
const moveButton = block.querySelector('button[id^="mover-promo-final-"]');
|
const moveButton = block.querySelector('button[id^="mover-promo-final-"]');
|
||||||
if (moveButton) {
|
if (moveButton) {
|
||||||
moveButton.classList.toggle('d-none', !canUsePromoFinal || hasExisting);
|
moveButton.classList.toggle('d-none', !canUsePromoFinal);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3274,7 +3287,8 @@ function updatePromoFinalControls(sourceKey) {
|
|||||||
const isCandidate = group.dataset.promoFinalCandidate === '1';
|
const isCandidate = group.dataset.promoFinalCandidate === '1';
|
||||||
const hasExisting = group.dataset.hasExisting === '1';
|
const hasExisting = group.dataset.hasExisting === '1';
|
||||||
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
const canUsePromoFinal = isCandidate && followupStates.includes(estado);
|
||||||
group.classList.toggle('d-none', !hasExisting && !canUsePromoFinal);
|
const canUploadPromoFinalEvidence = group.dataset.promoFinalEvidenceReady === '1' && followupStates.includes(estado);
|
||||||
|
group.classList.toggle('d-none', !hasExisting && !canUsePromoFinal && !canUploadPromoFinalEvidence);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3464,6 +3478,33 @@ function guardarGestion(sourceKey, trigger, options = {}) {
|
|||||||
const cancelProofGroup = document.querySelector('.js-cancelado-proof-group[data-source-key="' + sourceKey + '"]');
|
const cancelProofGroup = document.querySelector('.js-cancelado-proof-group[data-source-key="' + sourceKey + '"]');
|
||||||
const hasExistingPromoProof = promoProofGroup?.dataset.hasExisting === '1';
|
const hasExistingPromoProof = promoProofGroup?.dataset.hasExisting === '1';
|
||||||
const hasExistingCancelProof = cancelProofGroup?.dataset.hasExisting === '1';
|
const hasExistingCancelProof = cancelProofGroup?.dataset.hasExisting === '1';
|
||||||
|
const followupStates = PROMO_FINAL_FOLLOWUP_STATES;
|
||||||
|
const canUploadPromoFinalEvidence = promoProofGroup?.dataset.promoFinalEvidenceReady === '1' && followupStates.includes(estado);
|
||||||
|
|
||||||
|
if (subirLogistica) {
|
||||||
|
const confirmacionProducto = document.getElementById('confirmacion_producto-' + sourceKey);
|
||||||
|
const confirmacionCantidad = document.getElementById('confirmacion_cantidad-' + sourceKey);
|
||||||
|
const confirmacionPrecio = document.getElementById('confirmacion_precio-' + sourceKey);
|
||||||
|
const confirmacionFields = [
|
||||||
|
confirmacionProducto,
|
||||||
|
confirmacionCantidad,
|
||||||
|
confirmacionPrecio
|
||||||
|
];
|
||||||
|
const missingField = confirmacionFields.find(field => !(field?.value || '').trim());
|
||||||
|
if (missingField) {
|
||||||
|
alert('Completa producto, cantidad y precio en Confirmación de pedido antes de subir el pedido.');
|
||||||
|
missingField.focus();
|
||||||
|
if (typeof missingField.select === 'function') {
|
||||||
|
missingField.select();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!moverPromoFinal && promoProofInput?.files?.[0] && !canUploadPromoFinalEvidence) {
|
||||||
|
alert('La imagen de sustento solo se puede cargar desde el Día 3.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (moverPromoFinal && !hasExistingPromoProof && (!promoProofInput?.files || promoProofInput.files.length === 0)) {
|
if (moverPromoFinal && !hasExistingPromoProof && (!promoProofInput?.files || promoProofInput.files.length === 0)) {
|
||||||
alert('Debes subir una imagen de sustento antes de mover el pedido a Promo Final.');
|
alert('Debes subir una imagen de sustento antes de mover el pedido a Promo Final.');
|
||||||
@ -3512,7 +3553,7 @@ function guardarGestion(sourceKey, trigger, options = {}) {
|
|||||||
body.append(key, value);
|
body.append(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moverPromoFinal && promoProofInput?.files?.[0]) {
|
if ((moverPromoFinal || canUploadPromoFinalEvidence) && promoProofInput?.files?.[0]) {
|
||||||
body.append('promo_final_evidencia', promoProofInput.files[0]);
|
body.append('promo_final_evidencia', promoProofInput.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3533,7 +3574,7 @@ function guardarGestion(sourceKey, trigger, options = {}) {
|
|||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
throw new Error(data.message || 'No se pudo guardar la gestión');
|
throw new Error(data.message || 'No se pudo guardar la gestión');
|
||||||
}
|
}
|
||||||
if (subirLogistica || moverPromoFinal) {
|
if (subirLogistica || moverPromoFinal || (canUploadPromoFinalEvidence && promoProofInput?.files?.[0])) {
|
||||||
alert(data.message || 'Gestión actualizada correctamente.');
|
alert(data.message || 'Gestión actualizada correctamente.');
|
||||||
}
|
}
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
|||||||
@ -361,7 +361,7 @@ 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 = 'otra_tienda'): array
|
||||||
{
|
{
|
||||||
$storeKey = trim(mb_strtolower($storeKey));
|
$storeKey = trim(mb_strtolower($storeKey));
|
||||||
if ($storeKey !== 'tuani_recuperables') {
|
if ($storeKey !== 'tuani_recuperables' || empty($orders)) {
|
||||||
return $orders;
|
return $orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,20 +370,57 @@ if (!function_exists('cc_test_mark_recoverables_duplicate_orders')) {
|
|||||||
return $orders;
|
return $orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mainPhoneIndex = cc_test_fetch_store_phone_index($pdo, $mainStoreKey);
|
$targetPhoneMap = [];
|
||||||
foreach ($orders as &$order) {
|
foreach ($orders as $index => $order) {
|
||||||
$isDuplicate = false;
|
$orders[$index]['pedido_repetido_en_tienda'] = false;
|
||||||
foreach (cc_test_phone_match_keys((string) ($order['celular'] ?? '')) as $phoneKey) {
|
$orders[$index]['pedido_repetido_en_tienda_label'] = '';
|
||||||
if (isset($mainPhoneIndex[$phoneKey])) {
|
|
||||||
$isDuplicate = true;
|
$phoneKeys = cc_test_phone_match_keys((string) ($order['celular'] ?? ''));
|
||||||
break;
|
if (empty($phoneKeys)) {
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$order['pedido_repetido_en_tienda'] = $isDuplicate;
|
foreach ($phoneKeys as $phoneKey) {
|
||||||
$order['pedido_repetido_en_tienda_label'] = $isDuplicate ? 'Pedido repetido en tienda' : '';
|
$targetPhoneMap[$phoneKey][] = $index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($targetPhoneMap)) {
|
||||||
|
return $orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
drive_test_ensure_orders_table($pdo);
|
||||||
|
$stmt = $pdo->prepare("SELECT celular FROM callcenter_test_orders WHERE store_key = ? AND celular IS NOT NULL AND celular <> ''");
|
||||||
|
$stmt->execute([$mainStoreKey]);
|
||||||
|
|
||||||
|
$remainingTargets = count($targetPhoneMap);
|
||||||
|
while (($phoneValue = $stmt->fetchColumn()) !== false) {
|
||||||
|
$phoneKeys = cc_test_phone_match_keys((string) $phoneValue);
|
||||||
|
if (empty($phoneKeys)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($phoneKeys as $phoneKey) {
|
||||||
|
if (!isset($targetPhoneMap[$phoneKey])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($targetPhoneMap[$phoneKey] as $orderIndex) {
|
||||||
|
$orders[$orderIndex]['pedido_repetido_en_tienda'] = true;
|
||||||
|
$orders[$orderIndex]['pedido_repetido_en_tienda_label'] = 'Pedido repetido en tienda';
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($targetPhoneMap[$phoneKey]);
|
||||||
|
$remainingTargets--;
|
||||||
|
if ($remainingTargets <= 0) {
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
error_log('cc_test_mark_recoverables_duplicate_orders: ' . $exception->getMessage());
|
||||||
}
|
}
|
||||||
unset($order);
|
|
||||||
|
|
||||||
return $orders;
|
return $orders;
|
||||||
}
|
}
|
||||||
@ -1368,7 +1405,12 @@ function cc_test_followup_is_from_promo_final_cutoff(array $order): bool
|
|||||||
|
|
||||||
function cc_test_followup_promo_final_threshold_day(array $order): int
|
function cc_test_followup_promo_final_threshold_day(array $order): int
|
||||||
{
|
{
|
||||||
return 3;
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cc_test_followup_promo_final_evidence_day(array $order): int
|
||||||
|
{
|
||||||
|
return max(1, cc_test_followup_promo_final_threshold_day($order) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cc_test_followup_day_info(array $order): ?array
|
function cc_test_followup_day_info(array $order): ?array
|
||||||
@ -1392,6 +1434,7 @@ function cc_test_followup_day_info(array $order): ?array
|
|||||||
|
|
||||||
$dayNumber = $daysElapsed + 1;
|
$dayNumber = $daysElapsed + 1;
|
||||||
$promoFinalThresholdDay = cc_test_followup_promo_final_threshold_day($order);
|
$promoFinalThresholdDay = cc_test_followup_promo_final_threshold_day($order);
|
||||||
|
$promoFinalEvidenceDay = cc_test_followup_promo_final_evidence_day($order);
|
||||||
$badgeClass = 'bg-primary-subtle text-primary-emphasis border';
|
$badgeClass = 'bg-primary-subtle text-primary-emphasis border';
|
||||||
$textClass = 'text-primary-emphasis';
|
$textClass = 'text-primary-emphasis';
|
||||||
$description = 'Seguimiento dentro de la ventana óptima.';
|
$description = 'Seguimiento dentro de la ventana óptima.';
|
||||||
@ -1408,7 +1451,7 @@ function cc_test_followup_day_info(array $order): ?array
|
|||||||
$badgeClass = 'bg-warning-subtle text-warning-emphasis border';
|
$badgeClass = 'bg-warning-subtle text-warning-emphasis border';
|
||||||
$textClass = 'text-warning-emphasis';
|
$textClass = 'text-warning-emphasis';
|
||||||
$description = 'Último día de seguimiento antes de Promo Final.';
|
$description = 'Último día de seguimiento antes de Promo Final.';
|
||||||
$noticeText = 'Último día de seguimiento. Mañana debe pasar a Promo Final con imagen de sustento.';
|
$noticeText = 'Último día de seguimiento. Ya puedes cargar la imagen de sustento; mañana se podrá mover a Promo Final.';
|
||||||
$rowNoticeClass = 'text-warning-emphasis';
|
$rowNoticeClass = 'text-warning-emphasis';
|
||||||
} elseif ($dayNumber === 2) {
|
} elseif ($dayNumber === 2) {
|
||||||
$badgeClass = 'bg-info-subtle text-info-emphasis border';
|
$badgeClass = 'bg-info-subtle text-info-emphasis border';
|
||||||
@ -1427,6 +1470,8 @@ function cc_test_followup_day_info(array $order): ?array
|
|||||||
'row_notice_class' => $rowNoticeClass,
|
'row_notice_class' => $rowNoticeClass,
|
||||||
'started_at_label' => $startedAt->format('d/m/Y'),
|
'started_at_label' => $startedAt->format('d/m/Y'),
|
||||||
'promo_final_threshold_day' => $promoFinalThresholdDay,
|
'promo_final_threshold_day' => $promoFinalThresholdDay,
|
||||||
|
'promo_final_evidence_day' => $promoFinalEvidenceDay,
|
||||||
|
'can_upload_promo_final_evidence' => $dayNumber >= $promoFinalEvidenceDay,
|
||||||
'is_promo_final' => $dayNumber >= $promoFinalThresholdDay,
|
'is_promo_final' => $dayNumber >= $promoFinalThresholdDay,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -238,36 +238,12 @@ function cc_test_prepare_route_order_payload(array $sourceOrder, array $formData
|
|||||||
throw new RuntimeException('Ingresa coordenadas válidas con el formato -12.082029, -77.069024.');
|
throw new RuntimeException('Ingresa coordenadas válidas con el formato -12.082029, -77.069024.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$productoBase = trim((string) ($formData['producto'] ?? ''));
|
|
||||||
if ($productoBase === '') {
|
|
||||||
$productoBase = trim((string) ($sourceOrder['producto'] ?? ''));
|
|
||||||
}
|
|
||||||
$cantidadBase = trim((string) ($formData['cantidad'] ?? ''));
|
|
||||||
if ($cantidadBase === '') {
|
|
||||||
$cantidadBase = trim((string) ($sourceOrder['cantidad'] ?? ''));
|
|
||||||
}
|
|
||||||
$precioBase = trim((string) ($formData['precio'] ?? ''));
|
|
||||||
if ($precioBase === '') {
|
|
||||||
$precioBase = trim((string) ($sourceOrder['precio'] ?? ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
$productoPrincipal = trim((string) ($formData['confirmacion_producto'] ?? ''));
|
$productoPrincipal = trim((string) ($formData['confirmacion_producto'] ?? ''));
|
||||||
if ($productoPrincipal === '') {
|
|
||||||
$productoPrincipal = $productoBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cantidadPrincipalRaw = trim((string) ($formData['confirmacion_cantidad'] ?? ''));
|
$cantidadPrincipalRaw = trim((string) ($formData['confirmacion_cantidad'] ?? ''));
|
||||||
if ($cantidadPrincipalRaw === '') {
|
|
||||||
$cantidadPrincipalRaw = $cantidadBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
$precioPrincipalRaw = trim((string) ($formData['confirmacion_precio'] ?? ''));
|
$precioPrincipalRaw = trim((string) ($formData['confirmacion_precio'] ?? ''));
|
||||||
if ($precioPrincipalRaw === '') {
|
|
||||||
$precioPrincipalRaw = $precioBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($productoPrincipal === '') {
|
if ($productoPrincipal === '' || $cantidadPrincipalRaw === '' || $precioPrincipalRaw === '') {
|
||||||
throw new RuntimeException('El pedido no tiene producto principal para subir a Ruta Contraentrega.');
|
throw new RuntimeException('Para subir el pedido a Ruta Contraentrega completa producto, cantidad y precio en Confirmación de pedido.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cantidadPrincipal = cc_test_parse_quantity($cantidadPrincipalRaw);
|
$cantidadPrincipal = cc_test_parse_quantity($cantidadPrincipalRaw);
|
||||||
@ -552,36 +528,12 @@ function cc_test_prepare_rotulado_order_payload(array $sourceOrder, array $formD
|
|||||||
throw new RuntimeException('Debes ingresar un monto de adelanto válido antes de subir el pedido a Pedidos Rotulados.');
|
throw new RuntimeException('Debes ingresar un monto de adelanto válido antes de subir el pedido a Pedidos Rotulados.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$productoBase = trim((string) ($formData['producto'] ?? ''));
|
|
||||||
if ($productoBase === '') {
|
|
||||||
$productoBase = trim((string) ($sourceOrder['producto'] ?? ''));
|
|
||||||
}
|
|
||||||
$cantidadBase = trim((string) ($formData['cantidad'] ?? ''));
|
|
||||||
if ($cantidadBase === '') {
|
|
||||||
$cantidadBase = trim((string) ($sourceOrder['cantidad'] ?? ''));
|
|
||||||
}
|
|
||||||
$precioBase = trim((string) ($formData['precio'] ?? ''));
|
|
||||||
if ($precioBase === '') {
|
|
||||||
$precioBase = trim((string) ($sourceOrder['precio'] ?? ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
$productoPrincipal = trim((string) ($formData['confirmacion_producto'] ?? ''));
|
$productoPrincipal = trim((string) ($formData['confirmacion_producto'] ?? ''));
|
||||||
if ($productoPrincipal === '') {
|
|
||||||
$productoPrincipal = $productoBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cantidadPrincipalRaw = trim((string) ($formData['confirmacion_cantidad'] ?? ''));
|
$cantidadPrincipalRaw = trim((string) ($formData['confirmacion_cantidad'] ?? ''));
|
||||||
if ($cantidadPrincipalRaw === '') {
|
|
||||||
$cantidadPrincipalRaw = $cantidadBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
$precioPrincipalRaw = trim((string) ($formData['confirmacion_precio'] ?? ''));
|
$precioPrincipalRaw = trim((string) ($formData['confirmacion_precio'] ?? ''));
|
||||||
if ($precioPrincipalRaw === '') {
|
|
||||||
$precioPrincipalRaw = $precioBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($productoPrincipal === '') {
|
if ($productoPrincipal === '' || $cantidadPrincipalRaw === '' || $precioPrincipalRaw === '') {
|
||||||
throw new RuntimeException('El pedido no tiene producto principal para subir a Pedidos Rotulados.');
|
throw new RuntimeException('Para subir el pedido a Pedidos Rotulados completa producto, cantidad y precio en Confirmación de pedido.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cantidadPrincipal = cc_test_parse_quantity($cantidadPrincipalRaw);
|
$cantidadPrincipal = cc_test_parse_quantity($cantidadPrincipalRaw);
|
||||||
@ -902,8 +854,9 @@ try {
|
|||||||
if (!$isAdmin) {
|
if (!$isAdmin) {
|
||||||
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
|
$currentUserId = (int) ($_SESSION['user_id'] ?? 0);
|
||||||
$trackingUserId = $currentTracking ? (int) ($currentTracking['user_id'] ?? 0) : null;
|
$trackingUserId = $currentTracking ? (int) ($currentTracking['user_id'] ?? 0) : null;
|
||||||
$recoverablesAutoassignStates = ['SE ENVIO NUMERO DE CUENTA', 'OBSERVADO'];
|
// En la bandeja de carritos recuperables, cualquier pedido sin asignar
|
||||||
$canClaimRecoverable = $isRecoverablesQueue && $trackingUserId === null && in_array($estado, $recoverablesAutoassignStates, true);
|
// debe poder ser tomado por la asesora, sin importar el estado inicial.
|
||||||
|
$canClaimRecoverable = $isRecoverablesQueue && $trackingUserId === null;
|
||||||
if ($currentTracking === null) {
|
if ($currentTracking === null) {
|
||||||
if (!$canClaimRecoverable) {
|
if (!$canClaimRecoverable) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
@ -994,6 +947,19 @@ try {
|
|||||||
throw new RuntimeException('Este pedido fue eliminado del panel por un administrador.');
|
throw new RuntimeException('Este pedido fue eliminado del panel por un administrador.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($moverAPromoFinal || $promoFinalEvidenceFile !== null) {
|
||||||
|
$promoFinalSourceOrder = cc_test_fetch_source_order($pdo, $sourceKey);
|
||||||
|
if (!$promoFinalSourceOrder) {
|
||||||
|
throw new RuntimeException('No encontré el pedido base para gestionar el sustento de Promo Final.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$promoFinalDayInfo = cc_test_followup_day_info(array_merge($promoFinalSourceOrder, [
|
||||||
|
'estado' => $estado,
|
||||||
|
'assigned_at' => $currentTracking['assigned_at'] ?? null,
|
||||||
|
]));
|
||||||
|
$promoFinalEvidenceCanUpload = !empty($promoFinalDayInfo['can_upload_promo_final_evidence']);
|
||||||
|
}
|
||||||
|
|
||||||
$userIdToSet = $isAdmin
|
$userIdToSet = $isAdmin
|
||||||
? ($currentTracking ? ((int) ($currentTracking['user_id'] ?? 0) > 0 ? (int) $currentTracking['user_id'] : null) : null)
|
? ($currentTracking ? ((int) ($currentTracking['user_id'] ?? 0) > 0 ? (int) $currentTracking['user_id'] : null) : null)
|
||||||
: (int) $_SESSION['user_id'];
|
: (int) $_SESSION['user_id'];
|
||||||
@ -1066,17 +1032,12 @@ try {
|
|||||||
throw new RuntimeException('Solo puedes mover a Promo Final pedidos que sigan en seguimiento.');
|
throw new RuntimeException('Solo puedes mover a Promo Final pedidos que sigan en seguimiento.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$sourceOrderForPromo = cc_test_fetch_source_order($pdo, $sourceKey);
|
if (!$promoFinalSourceOrder) {
|
||||||
if (!$sourceOrderForPromo) {
|
throw new RuntimeException('No encontré el pedido base para gestionar el sustento de Promo Final.');
|
||||||
throw new RuntimeException('No encontré el pedido base para moverlo a Promo Final.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$promoFinalDayInfo = cc_test_followup_day_info(array_merge($sourceOrderForPromo, [
|
|
||||||
'estado' => $estado,
|
|
||||||
'assigned_at' => $currentTracking['assigned_at'] ?? null,
|
|
||||||
]));
|
|
||||||
if (empty($promoFinalDayInfo['is_promo_final'])) {
|
if (empty($promoFinalDayInfo['is_promo_final'])) {
|
||||||
$promoFinalThresholdDay = (int) ($promoFinalDayInfo['promo_final_threshold_day'] ?? 3);
|
$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 . '.');
|
throw new RuntimeException('Este pedido aún no puede pasar a Promo Final. Recién se habilita desde el Día ' . $promoFinalThresholdDay . '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1085,12 +1046,17 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($promoFinalEvidenceFile !== null && !$moverAPromoFinal && !$promoFinalEvidenceCanUpload) {
|
||||||
|
$promoFinalEvidenceThresholdDay = (int) ($promoFinalDayInfo['promo_final_evidence_day'] ?? max(1, (int) ($promoFinalDayInfo['promo_final_threshold_day'] ?? 4) - 1));
|
||||||
|
throw new RuntimeException('La imagen de sustento solo se puede cargar desde el Día ' . $promoFinalEvidenceThresholdDay . '.');
|
||||||
|
}
|
||||||
|
|
||||||
if ($estado === 'CANCELADO' && $canceladoEvidenceFile === null && $canceladoEvidencePath === null) {
|
if ($estado === 'CANCELADO' && $canceladoEvidenceFile === null && $canceladoEvidencePath === null) {
|
||||||
throw new RuntimeException('Debes subir una imagen de sustento para guardar el pedido en estado Cancelado.');
|
throw new RuntimeException('Debes subir una imagen de sustento para guardar el pedido en estado Cancelado.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$actorUserId = (int) ($_SESSION['user_id'] ?? 0);
|
$actorUserId = (int) ($_SESSION['user_id'] ?? 0);
|
||||||
if ($moverAPromoFinal && $promoFinalEvidenceFile !== null) {
|
if ($promoFinalEvidenceFile !== null && ($moverAPromoFinal || $promoFinalEvidenceCanUpload)) {
|
||||||
$storedPromoFinalEvidence = cc_test_store_tracking_evidence_image($promoFinalEvidenceFile, 'promo_final', $sourceKey, $actorUserId);
|
$storedPromoFinalEvidence = cc_test_store_tracking_evidence_image($promoFinalEvidenceFile, 'promo_final', $sourceKey, $actorUserId);
|
||||||
$createdFiles[] = $storedPromoFinalEvidence['absolute_path'];
|
$createdFiles[] = $storedPromoFinalEvidence['absolute_path'];
|
||||||
$promoFinalEvidencePath = $storedPromoFinalEvidence['relative_path'];
|
$promoFinalEvidencePath = $storedPromoFinalEvidence['relative_path'];
|
||||||
@ -1380,6 +1346,9 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$message = 'Gestión actualizada correctamente.';
|
$message = 'Gestión actualizada correctamente.';
|
||||||
|
if ($promoFinalEvidenceFile !== null && !$moverAPromoFinal && $promoFinalEvidenceCanUpload) {
|
||||||
|
$message = 'Sustento de Promo Final cargado correctamente.';
|
||||||
|
}
|
||||||
if ($moverAPromoFinal && $promoFinalEvidencePath !== null) {
|
if ($moverAPromoFinal && $promoFinalEvidencePath !== null) {
|
||||||
$message = 'Pedido movido a Promo Final correctamente.';
|
$message = 'Pedido movido a Promo Final correctamente.';
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user