Autosave: 20260715-044358
This commit is contained in:
parent
589f83f35c
commit
bb9b9b3670
@ -168,6 +168,8 @@ if (!isset($availableStores[$storeKey])) {
|
||||
$storeKey = 'flower';
|
||||
}
|
||||
$storeConfig = $availableStores[$storeKey];
|
||||
$storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey));
|
||||
$usesIncrementalSync = !empty($storeConfig['incremental_sync']);
|
||||
|
||||
$errorMessage = null;
|
||||
$noticeMessage = null;
|
||||
@ -177,8 +179,7 @@ $visibleOrders = [];
|
||||
$modalsHtml = [];
|
||||
$totalRows = 0;
|
||||
$agregadosCount = 0;
|
||||
$tuaniLastProcessedRow = null;
|
||||
$loadLimit = $storeKey === 'otra_tienda' ? 0 : 100;
|
||||
$lastProcessedRow = null;
|
||||
$startRow = (int) ($storeConfig['startRow'] ?? 5552);
|
||||
$catalogoProductos = [];
|
||||
$stats = [
|
||||
@ -205,10 +206,10 @@ try {
|
||||
$catalogoProductos = [];
|
||||
}
|
||||
|
||||
if ($storeKey === 'otra_tienda') {
|
||||
if ($usesIncrementalSync) {
|
||||
$syncInfo = drive_test_sync_orders_incremental($pdo, $storeKey, 0);
|
||||
$totalRows = (int) ($syncInfo['drive_rows_total'] ?? 0);
|
||||
$tuaniLastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0);
|
||||
$lastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0);
|
||||
$startRow = (int) ($syncInfo['next_start_row'] ?? $startRow);
|
||||
|
||||
$orders = drive_test_fetch_orders_from_db($pdo, $storeKey);
|
||||
@ -220,7 +221,7 @@ try {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$preview = drive_test_fetch_orders($loadLimit, $startRow, $storeKey);
|
||||
$preview = drive_test_fetch_orders(100, $startRow, $storeKey);
|
||||
$totalRows = (int) ($preview['total_rows'] ?? 0);
|
||||
$orders = $preview['orders'] ?? [];
|
||||
|
||||
@ -398,6 +399,18 @@ try {
|
||||
return $aPendienteLogistica ? -1 : 1;
|
||||
}
|
||||
|
||||
$aAssigned = cc_test_parse_datetime($a['assigned_at'] ?? null);
|
||||
$bAssigned = cc_test_parse_datetime($b['assigned_at'] ?? null);
|
||||
if ($aAssigned && $bAssigned && $aAssigned != $bAssigned) {
|
||||
return $bAssigned <=> $aAssigned;
|
||||
}
|
||||
if ($aAssigned && !$bAssigned) {
|
||||
return -1;
|
||||
}
|
||||
if (!$aAssigned && $bAssigned) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$aDue = cc_test_parse_datetime($a["proxima_llamada_at"] ?? null);
|
||||
$bDue = cc_test_parse_datetime($b["proxima_llamada_at"] ?? null);
|
||||
if ($aDue && $bDue && $aDue != $bDue) {
|
||||
@ -455,7 +468,7 @@ require_once 'layout_header.php';
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2">Drive detectado: <?php echo (int) $totalRows; ?> filas</span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2">Agregados: <?php echo (int) $agregadosCount; ?></span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2"><?php if ($storeKey === 'otra_tienda' && $tuaniLastProcessedRow !== null): ?>TUANI: distribución desde fila <?php echo (int) ($storeConfig['startRow'] ?? $startRow); ?> · último procesado <?php echo (int) $tuaniLastProcessedRow; ?><?php else: ?>Extracción: desde fila <?php echo (int) $startRow; ?><?php endif; ?></span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2"><?php if ($usesIncrementalSync && $lastProcessedRow !== null): ?><?php echo htmlspecialchars(mb_strtoupper($storeLabel)); ?>: distribución desde fila <?php echo (int) ($storeConfig['startRow'] ?? $startRow); ?> · último procesado <?php echo (int) $lastProcessedRow; ?><?php else: ?>Extracción: desde fila <?php echo (int) $startRow; ?><?php endif; ?></span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2">Auto actualización: cada 10 min</span>
|
||||
<a href="test_importar_drive.php?store=<?php echo htmlspecialchars($storeKey); ?>" class="btn btn-outline-primary btn-sm">Ver vista previa Drive</a>
|
||||
</div>
|
||||
|
||||
@ -274,9 +274,15 @@ try {
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$user_role = $_SESSION['user_role'] ?? 'Asesor';
|
||||
$is_admin = in_array($user_role, ['Administrador', 'admin'], true);
|
||||
|
||||
$search_query = trim($_GET['q'] ?? '');
|
||||
|
||||
if (!$is_admin) {
|
||||
http_response_code(403);
|
||||
exit('Acceso denegado.');
|
||||
}
|
||||
|
||||
// Exportar solo los pedidos con fecha de entrega "mañana"
|
||||
$date_condition = "DATE(p.fecha_entrega) = DATE_ADD(CURDATE(), INTERVAL 1 DAY)";
|
||||
|
||||
|
||||
@ -169,6 +169,8 @@ if (!isset($availableStores[$storeKey])) {
|
||||
$storeKey = 'flower';
|
||||
}
|
||||
$storeConfig = $availableStores[$storeKey];
|
||||
$storeLabel = (string) ($storeConfig['label'] ?? strtoupper($storeKey));
|
||||
$usesIncrementalSync = !empty($storeConfig['incremental_sync']);
|
||||
|
||||
$errorMessage = null;
|
||||
$noticeMessage = null;
|
||||
@ -179,8 +181,7 @@ $visibleOrders = [];
|
||||
$modalsHtml = [];
|
||||
$totalRows = 0;
|
||||
$agregadosCount = 0;
|
||||
$tuaniLastProcessedRow = null;
|
||||
$loadLimit = $storeKey === 'otra_tienda' ? 0 : 100;
|
||||
$lastProcessedRow = null;
|
||||
|
||||
$cupoObjetivoPorAsesora = 10;
|
||||
$advisorOpenCounts = [];
|
||||
@ -233,10 +234,10 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
if ($storeKey === 'otra_tienda') {
|
||||
if ($usesIncrementalSync) {
|
||||
$syncInfo = drive_test_sync_orders_incremental($pdo, $storeKey, 0);
|
||||
$totalRows = (int) ($syncInfo['drive_rows_total'] ?? 0);
|
||||
$tuaniLastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0);
|
||||
$lastProcessedRow = (int) ($syncInfo['last_processed_row'] ?? 0);
|
||||
$startRow = (int) ($syncInfo['next_start_row'] ?? $startRow);
|
||||
|
||||
$orders = drive_test_fetch_orders_from_db($pdo, $storeKey);
|
||||
@ -248,7 +249,7 @@ try {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$preview = drive_test_fetch_orders($loadLimit, $startRow, $storeKey);
|
||||
$preview = drive_test_fetch_orders(100, $startRow, $storeKey);
|
||||
$totalRows = (int) ($preview['total_rows'] ?? 0);
|
||||
$orders = $preview['orders'] ?? [];
|
||||
|
||||
@ -311,6 +312,108 @@ try {
|
||||
}));
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'bulk_assign_assessor') {
|
||||
if (empty($assessors)) {
|
||||
throw new RuntimeException('No hay asesoras configuradas en el sistema.');
|
||||
}
|
||||
|
||||
$targetValue = trim((string) ($_POST['target_assessor'] ?? ''));
|
||||
$targetKey = trim(mb_strtoupper($targetValue));
|
||||
$targetKey = preg_replace('/\s+/', ' ', $targetKey) ?? $targetKey;
|
||||
$selectedAssessor = $targetKey !== '' ? ($assessors[$targetKey] ?? null) : null;
|
||||
if (!$selectedAssessor) {
|
||||
throw new RuntimeException('Selecciona la asesora para la asignación rápida.');
|
||||
}
|
||||
|
||||
$targetUserId = (int) ($selectedAssessor['id'] ?? 0);
|
||||
if ($targetUserId <= 0) {
|
||||
throw new RuntimeException('La asesora seleccionada no es válida.');
|
||||
}
|
||||
|
||||
$availableSourceKeys = array_fill_keys(array_column($orders, 'source_key'), true);
|
||||
$sourceKeys = [];
|
||||
foreach ((array) ($_POST['source_keys'] ?? []) as $sourceKeyRaw) {
|
||||
$sourceKey = trim((string) $sourceKeyRaw);
|
||||
if ($sourceKey === '' || !preg_match('/^[a-f0-9]{40}$/', $sourceKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($availableSourceKeys[$sourceKey])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sourceKeys[$sourceKey] = $sourceKey;
|
||||
}
|
||||
|
||||
if (empty($sourceKeys)) {
|
||||
throw new RuntimeException('Selecciona al menos un pedido válido para la asignación rápida.');
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($sourceKeys), '?'));
|
||||
$stmtCurrent = $pdo->prepare("SELECT source_key, user_id FROM callcenter_test_tracking WHERE source_key IN ($placeholders)");
|
||||
$stmtCurrent->execute(array_values($sourceKeys));
|
||||
|
||||
$currentAssignments = [];
|
||||
while ($row = $stmtCurrent->fetch(PDO::FETCH_ASSOC)) {
|
||||
$currentAssignments[(string) ($row['source_key'] ?? '')] = isset($row['user_id']) && (int) $row['user_id'] > 0
|
||||
? (int) $row['user_id']
|
||||
: null;
|
||||
}
|
||||
|
||||
$assignedCount = 0;
|
||||
$sameAssessorCount = 0;
|
||||
$lockedCount = 0;
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
foreach ($sourceKeys as $sourceKey) {
|
||||
$currentUserId = $currentAssignments[$sourceKey] ?? null;
|
||||
|
||||
if ($currentUserId !== null && (int) $currentUserId !== $targetUserId) {
|
||||
$lockedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($currentUserId !== null && (int) $currentUserId === $targetUserId) {
|
||||
$sameAssessorCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
cc_test_upsert_assignee($pdo, $sourceKey, $targetUserId);
|
||||
$assignedCount++;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $exception) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$messageParts = [];
|
||||
if ($assignedCount > 0) {
|
||||
$messageParts[] = 'Asignación rápida lista: ' . $assignedCount . ' pedido' . ($assignedCount === 1 ? '' : 's') . ' asignado' . ($assignedCount === 1 ? '' : 's') . ' a ' . $selectedAssessor['label'] . '.';
|
||||
}
|
||||
if ($sameAssessorCount > 0) {
|
||||
$messageParts[] = $sameAssessorCount . ' ya estaba' . ($sameAssessorCount === 1 ? '' : 'n') . ' con esa misma asesora.';
|
||||
}
|
||||
if ($lockedCount > 0) {
|
||||
$messageParts[] = $lockedCount . ' ya estaba' . ($lockedCount === 1 ? '' : 'n') . ' asignado' . ($lockedCount === 1 ? '' : 's') . ' a otra asesora y se dejó igual.';
|
||||
}
|
||||
|
||||
$noticeMessage = !empty($messageParts)
|
||||
? implode(' ', $messageParts)
|
||||
: 'No hubo cambios en la asignación rápida.';
|
||||
|
||||
$tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key'));
|
||||
$orders = drive_test_merge_tracking($orders, $tracking);
|
||||
$orders = array_values(array_filter($orders, static function (array $order): bool {
|
||||
return empty($order['eliminado']);
|
||||
}));
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'auto_assign_cupo') {
|
||||
$openStates = cc_test_open_states();
|
||||
|
||||
@ -671,6 +774,70 @@ try {
|
||||
require_once 'layout_header.php';
|
||||
?>
|
||||
|
||||
<style>
|
||||
.cc-quick-select-btn {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.cc-callcenter-table tbody td {
|
||||
padding-top: .5rem;
|
||||
padding-bottom: .5rem;
|
||||
}
|
||||
|
||||
.cc-callcenter-table .small {
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.cc-callcenter-row.is-selected td:first-child {
|
||||
box-shadow: inset 4px 0 0 #0d6efd;
|
||||
}
|
||||
|
||||
.cc-callcenter-assign-form {
|
||||
padding: .5rem !important;
|
||||
}
|
||||
|
||||
.cc-callcenter-assign-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
gap: .35rem;
|
||||
}
|
||||
|
||||
.cc-callcenter-assign-row .form-select {
|
||||
min-width: 11rem;
|
||||
flex: 1 1 11rem;
|
||||
}
|
||||
|
||||
.cc-callcenter-assign-submit {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cc-callcenter-actions {
|
||||
gap: .35rem !important;
|
||||
}
|
||||
|
||||
.cc-callcenter-action-btn {
|
||||
padding: .2rem .55rem;
|
||||
line-height: 1.1;
|
||||
font-size: .78rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.cc-callcenter-table tbody td {
|
||||
padding-top: .42rem;
|
||||
padding-bottom: .42rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<main class="container-fluid py-4">
|
||||
<section class="mb-4">
|
||||
<div class="d-flex flex-column flex-xl-row justify-content-between align-items-xl-center gap-3">
|
||||
@ -692,7 +859,7 @@ require_once 'layout_header.php';
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2">Drive detectado: <?php echo (int) $totalRows; ?> filas</span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2">Agregados: <?php echo (int) $agregadosCount; ?></span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2"><?php if ($storeKey === 'otra_tienda' && $tuaniLastProcessedRow !== null): ?>TUANI: distribución desde fila <?php echo (int) ($storeConfig['startRow'] ?? $startRow); ?> · último procesado <?php echo (int) $tuaniLastProcessedRow; ?><?php else: ?>Extracción: desde fila <?php echo (int) $startRow; ?><?php endif; ?></span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2"><?php if ($usesIncrementalSync && $lastProcessedRow !== null): ?><?php echo htmlspecialchars(mb_strtoupper($storeLabel)); ?>: distribución desde fila <?php echo (int) ($storeConfig['startRow'] ?? $startRow); ?> · último procesado <?php echo (int) $lastProcessedRow; ?><?php else: ?>Extracción: desde fila <?php echo (int) $startRow; ?><?php endif; ?></span>
|
||||
<span class="badge rounded-pill text-bg-light border px-3 py-2">Auto actualización: cada 10 min</span>
|
||||
<a href="test_importar_drive.php?store=<?php echo htmlspecialchars($storeKey); ?>" class="btn btn-outline-primary btn-sm">Ver vista previa Drive</a>
|
||||
</div>
|
||||
@ -882,22 +1049,45 @@ require_once 'layout_header.php';
|
||||
<span class="badge bg-light text-dark border"><?php echo count($visibleOrders); ?> pedidos en esta bandeja</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="border-bottom bg-light-subtle px-3 py-3">
|
||||
<form method="post" id="bulkAssignForm" class="d-flex flex-column flex-xl-row align-items-xl-center gap-2 gap-xl-3">
|
||||
<input type="hidden" name="action" value="bulk_assign_assessor">
|
||||
<div id="bulkAssignSourceKeys"></div>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<span class="badge text-bg-primary rounded-pill px-3 py-2"><span id="bulkSelectedCount">0</span> seleccionados</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="toggleSelectAllOrders"<?php echo empty($visibleOrders) ? ' disabled' : ''; ?>>Marcar visibles</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="clearSelectedOrders" disabled>Limpiar</button>
|
||||
</div>
|
||||
<div class="small text-muted">Usa el botón de la izquierda de cada pedido y asigna varios de una sola vez.</div>
|
||||
<div class="d-flex flex-column flex-sm-row gap-2 ms-xl-auto">
|
||||
<select name="target_assessor" class="form-select form-select-sm" style="min-width: 220px;"<?php echo empty($assessors) ? ' disabled' : ''; ?>>
|
||||
<option value="">Elegir asesora</option>
|
||||
<?php foreach ($assessors as $assessorKey => $assessor): ?>
|
||||
<option value="<?php echo htmlspecialchars($assessorKey); ?>"><?php echo htmlspecialchars($assessor['label']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-sm btn-primary" id="bulkAssignSubmit" disabled>Asignar seleccionados</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="small text-muted mt-2">Si un pedido ya está tomado por otra asesora, el sistema lo deja igual y te avisa.</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<table class="table table-hover align-middle mb-0 cc-callcenter-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="text-center" style="width: 64px;">Sel.</th>
|
||||
<th class="text-start" style="min-width: 16rem;">Asignación / acciones</th>
|
||||
<th>N° Pedido</th>
|
||||
<th>Cliente</th>
|
||||
<th>Ubicación editable</th>
|
||||
<th>Pedido</th>
|
||||
<th>Gestión</th>
|
||||
<th class="text-center">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($visibleOrders)): ?>
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-5 text-muted">
|
||||
<td colspan="7" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-inbox fs-2 d-block mb-2"></i>
|
||||
No hay pedidos en esta bandeja por ahora.
|
||||
</td>
|
||||
@ -950,6 +1140,123 @@ require_once 'layout_header.php';
|
||||
ob_start();
|
||||
?>
|
||||
<tr class="cc-callcenter-row" data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>" style="<?php echo htmlspecialchars(cc_test_row_background_style((string) ($order['estado'] ?? 'POR LLAMAR'))); ?>">
|
||||
<td class="text-center">
|
||||
<div class="d-flex justify-content-center">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-primary cc-quick-select-btn js-quick-select-btn"
|
||||
data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||
aria-pressed="false"
|
||||
aria-label="Seleccionar pedido <?php echo htmlspecialchars(cc_test_order_label($order)); ?>"
|
||||
title="Seleccionar este pedido para asignación rápida"
|
||||
>
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
<input type="checkbox" class="d-none js-bulk-order-checkbox" value="<?php echo htmlspecialchars($order['source_key']); ?>" data-source-key="<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle" style="min-width: 16rem;">
|
||||
<?php if (in_array($_SESSION['user_role'] ?? '', ['Administrador', 'admin'], true)): ?>
|
||||
<?php
|
||||
$orderUserId = $order['user_id'] ?? null;
|
||||
$orderUserIdNorm = $orderUserId !== null ? ((int) $orderUserId > 0 ? (int) $orderUserId : null) : null;
|
||||
$unassigned = $orderUserIdNorm === null;
|
||||
$assignedAssessorKey = null;
|
||||
if (!$unassigned) {
|
||||
foreach ($assessors as $assessorKey => $assessor) {
|
||||
if ((int) ($assessor['id'] ?? 0) === (int) $orderUserIdNorm) {
|
||||
$assignedAssessorKey = (string) $assessorKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$assignFormClass = 'border rounded-3 p-1 text-start shadow-sm cc-callcenter-assign-form';
|
||||
if ($unassigned) {
|
||||
$assignFormClass .= ' bg-light';
|
||||
} elseif ($assignedAssessorKey === 'KARINA') {
|
||||
$assignFormClass .= ' bg-primary-subtle border-primary';
|
||||
} elseif ($assignedAssessorKey === 'ESTEFANYA') {
|
||||
$assignFormClass .= ' bg-success-subtle border-success';
|
||||
} elseif ($assignedAssessorKey === 'CARMEN') {
|
||||
$assignFormClass .= ' bg-warning-subtle border-warning';
|
||||
} else {
|
||||
$assignFormClass .= ' bg-secondary-subtle border-secondary';
|
||||
}
|
||||
|
||||
if ($unassigned) {
|
||||
$selectedAssessorKeyForSelect = !empty($recommendedAssessorKey) ? (string) $recommendedAssessorKey : '';
|
||||
} else {
|
||||
$selectedAssessorKeyForSelect = $assignedAssessorKey !== null ? (string) $assignedAssessorKey : '';
|
||||
}
|
||||
|
||||
$buttonLabel = $unassigned
|
||||
? 'Asignar pedido'
|
||||
: ($assignedAssessorKey !== null ? 'Guardar asignación' : 'Desasignar');
|
||||
?>
|
||||
|
||||
<form method="post" class="<?php echo $assignFormClass; ?>">
|
||||
<input type="hidden" name="action" value="assign_assessor">
|
||||
<input type="hidden" name="source_key" value="<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center gap-2 mb-1">
|
||||
<div class="small text-uppercase text-muted fw-semibold">Asignar a asesora</div>
|
||||
<?php if ($unassigned): ?>
|
||||
<span class="badge bg-light text-dark border">Sin asignar</span>
|
||||
<?php elseif ($assignedAssessorKey === 'KARINA'): ?>
|
||||
<span class="badge bg-primary-subtle text-primary-emphasis border">KARINA</span>
|
||||
<?php elseif ($assignedAssessorKey === 'ESTEFANYA'): ?>
|
||||
<span class="badge bg-success-subtle text-success-emphasis border">ESTEFANYA</span>
|
||||
<?php elseif ($assignedAssessorKey === 'CARMEN'): ?>
|
||||
<span class="badge bg-warning-subtle text-warning-emphasis border">CARMEN</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary-subtle text-secondary-emphasis border">Asignado</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="cc-callcenter-assign-row">
|
||||
<select name="target_assessor" class="form-select form-select-sm">
|
||||
<option value=""<?php echo ($selectedAssessorKeyForSelect === '') ? ' selected' : ''; ?>>Sin asignar</option>
|
||||
|
||||
<?php if ($unassigned): ?>
|
||||
<?php foreach ($assessors as $assessorKey => $assessor): ?>
|
||||
<option value="<?php echo htmlspecialchars($assessorKey); ?>"<?php echo ($selectedAssessorKeyForSelect === $assessorKey) ? ' selected' : ''; ?>><?php echo htmlspecialchars($assessor['label']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php elseif ($assignedAssessorKey !== null): ?>
|
||||
<option value="<?php echo htmlspecialchars((string) $assignedAssessorKey); ?>" selected><?php echo htmlspecialchars((string) ($assessors[$assignedAssessorKey]['label'] ?? $assignedAssessorKey)); ?></option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
|
||||
<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; ?>
|
||||
<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-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)">
|
||||
<i class="bi bi-telephone-outbound"></i> Llamar / Gestionar
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="button" class="btn btn-sm btn-primary cc-callcenter-action-btn text-nowrap" disabled>Sin teléfono</button>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($order['whatsapp_url'])): ?>
|
||||
<a href="<?php echo htmlspecialchars($order['whatsapp_url']); ?>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-success cc-callcenter-action-btn">
|
||||
<i class="bi bi-whatsapp"></i> WhatsApp
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="btn btn-sm btn-outline-dark cc-callcenter-action-btn" data-bs-toggle="modal" data-bs-target="#<?php echo htmlspecialchars($modalId); ?>">
|
||||
<i class="bi bi-sliders"></i> Gestionar
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($followupDayInfo !== null): ?>
|
||||
<div class="small text-uppercase fw-semibold <?php echo htmlspecialchars($followupDayInfo['text_class']); ?> mb-1"><?php echo htmlspecialchars($followupDayInfo['display_label']); ?></div>
|
||||
@ -1015,106 +1322,6 @@ require_once 'layout_header.php';
|
||||
<div class="small text-muted">Última gestión: <?php echo htmlspecialchars(cc_test_format_datetime($order['ultima_gestion_at'] ?? ($order['seguimiento_actualizado'] ?? null), 'Aún no gestionado')); ?></div>
|
||||
<div class="small text-muted mt-1">Nota: <?php echo htmlspecialchars(cc_test_display_value($order['nota_seguimiento'], 'Sin nota interna')); ?></div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex flex-column gap-2 align-items-stretch">
|
||||
<?php if (in_array($_SESSION['user_role'] ?? '', ['Administrador', 'admin'], true)): ?>
|
||||
<?php
|
||||
$orderUserId = $order['user_id'] ?? null;
|
||||
$orderUserIdNorm = $orderUserId !== null ? ((int) $orderUserId > 0 ? (int) $orderUserId : null) : null;
|
||||
$unassigned = $orderUserIdNorm === null;
|
||||
$assignedAssessorKey = null;
|
||||
if (!$unassigned) {
|
||||
foreach ($assessors as $assessorKey => $assessor) {
|
||||
if ((int) ($assessor['id'] ?? 0) === (int) $orderUserIdNorm) {
|
||||
$assignedAssessorKey = (string) $assessorKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$assignFormClass = 'border rounded-3 p-2 text-start shadow-sm';
|
||||
if ($unassigned) {
|
||||
$assignFormClass .= ' bg-light';
|
||||
} elseif ($assignedAssessorKey === 'KARINA') {
|
||||
$assignFormClass .= ' bg-primary-subtle border-primary';
|
||||
} elseif ($assignedAssessorKey === 'ESTEFANYA') {
|
||||
$assignFormClass .= ' bg-success-subtle border-success';
|
||||
} elseif ($assignedAssessorKey === 'CARMEN') {
|
||||
$assignFormClass .= ' bg-warning-subtle border-warning';
|
||||
} else {
|
||||
$assignFormClass .= ' bg-secondary-subtle border-secondary';
|
||||
}
|
||||
|
||||
if ($unassigned) {
|
||||
$selectedAssessorKeyForSelect = !empty($recommendedAssessorKey) ? (string) $recommendedAssessorKey : '';
|
||||
} else {
|
||||
$selectedAssessorKeyForSelect = $assignedAssessorKey !== null ? (string) $assignedAssessorKey : '';
|
||||
}
|
||||
|
||||
$buttonLabel = $unassigned
|
||||
? 'Asignar pedido'
|
||||
: ($assignedAssessorKey !== null ? 'Guardar asignación' : 'Desasignar');
|
||||
?>
|
||||
|
||||
<form method="post" class="<?php echo $assignFormClass; ?>">
|
||||
<input type="hidden" name="action" value="assign_assessor">
|
||||
<input type="hidden" name="source_key" value="<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center gap-2 mb-1">
|
||||
<div class="small text-uppercase text-muted fw-semibold">Asignar a asesora</div>
|
||||
<?php if ($unassigned): ?>
|
||||
<span class="badge bg-light text-dark border">Sin asignar</span>
|
||||
<?php elseif ($assignedAssessorKey === 'KARINA'): ?>
|
||||
<span class="badge bg-primary-subtle text-primary-emphasis border">KARINA</span>
|
||||
<?php elseif ($assignedAssessorKey === 'ESTEFANYA'): ?>
|
||||
<span class="badge bg-success-subtle text-success-emphasis border">ESTEFANYA</span>
|
||||
<?php elseif ($assignedAssessorKey === 'CARMEN'): ?>
|
||||
<span class="badge bg-warning-subtle text-warning-emphasis border">CARMEN</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary-subtle text-secondary-emphasis border">Asignado</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<select name="target_assessor" class="form-select form-select-sm mb-2">
|
||||
<option value=""<?php echo ($selectedAssessorKeyForSelect === '') ? ' selected' : ''; ?>>Sin asignar</option>
|
||||
|
||||
<?php if ($unassigned): ?>
|
||||
<?php foreach ($assessors as $assessorKey => $assessor): ?>
|
||||
<option value="<?php echo htmlspecialchars($assessorKey); ?>"<?php echo ($selectedAssessorKeyForSelect === $assessorKey) ? ' selected' : ''; ?>><?php echo htmlspecialchars($assessor['label']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
<?php elseif ($assignedAssessorKey !== null): ?>
|
||||
<option value="<?php echo htmlspecialchars((string) $assignedAssessorKey); ?>" selected><?php echo htmlspecialchars((string) ($assessors[$assignedAssessorKey]['label'] ?? $assignedAssessorKey)); ?></option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
|
||||
<button type="submit" class="btn btn-sm btn-primary w-100"><?php echo htmlspecialchars((string) $buttonLabel); ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($order['telefono_url'])): ?>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
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-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)">
|
||||
<i class="bi bi-telephone-outbound"></i> Llamar / Gestionar
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="button" class="btn btn-sm btn-primary" disabled>Sin teléfono</button>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($order['whatsapp_url'])): ?>
|
||||
<a href="<?php echo htmlspecialchars($order['whatsapp_url']); ?>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-success">
|
||||
<i class="bi bi-whatsapp"></i> WhatsApp
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="btn btn-sm btn-outline-dark" data-bs-toggle="modal" data-bs-target="#<?php echo htmlspecialchars($modalId); ?>">
|
||||
<i class="bi bi-sliders"></i> Gestionar
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $rowHtml = ob_get_clean(); echo $rowHtml; ?>
|
||||
|
||||
@ -2238,6 +2445,142 @@ if (airDroidCopyButton) {
|
||||
});
|
||||
}
|
||||
|
||||
const bulkAssignForm = document.getElementById('bulkAssignForm');
|
||||
const bulkAssignSourceKeys = document.getElementById('bulkAssignSourceKeys');
|
||||
const bulkSelectedCount = document.getElementById('bulkSelectedCount');
|
||||
const bulkAssignSubmit = document.getElementById('bulkAssignSubmit');
|
||||
const toggleSelectAllOrders = document.getElementById('toggleSelectAllOrders');
|
||||
const clearSelectedOrders = document.getElementById('clearSelectedOrders');
|
||||
const bulkOrderCheckboxes = Array.from(document.querySelectorAll('.js-bulk-order-checkbox'));
|
||||
const quickSelectButtons = Array.from(document.querySelectorAll('.js-quick-select-btn'));
|
||||
const bulkAssessorSelect = bulkAssignForm ? bulkAssignForm.querySelector('select[name="target_assessor"]') : null;
|
||||
|
||||
function getSelectedBulkOrderCheckboxes() {
|
||||
return bulkOrderCheckboxes.filter(function (checkbox) {
|
||||
return checkbox.checked;
|
||||
});
|
||||
}
|
||||
|
||||
function setQuickSelectButtonState(button, selected) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.classList.toggle('btn-primary', selected);
|
||||
button.classList.toggle('btn-outline-primary', !selected);
|
||||
button.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
||||
button.setAttribute('title', selected ? 'Quitar de la selección rápida' : 'Seleccionar este pedido para asignación rápida');
|
||||
button.innerHTML = selected ? '<i class="bi bi-check-lg"></i>' : '<i class="bi bi-plus-lg"></i>';
|
||||
}
|
||||
|
||||
function syncBulkAssignSelectionUi() {
|
||||
const selectedCheckboxes = getSelectedBulkOrderCheckboxes();
|
||||
|
||||
if (bulkSelectedCount) {
|
||||
bulkSelectedCount.textContent = String(selectedCheckboxes.length);
|
||||
}
|
||||
|
||||
if (bulkAssignSourceKeys) {
|
||||
bulkAssignSourceKeys.innerHTML = '';
|
||||
selectedCheckboxes.forEach(function (checkbox) {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'source_keys[]';
|
||||
input.value = checkbox.value;
|
||||
bulkAssignSourceKeys.appendChild(input);
|
||||
});
|
||||
}
|
||||
|
||||
bulkOrderCheckboxes.forEach(function (checkbox) {
|
||||
const row = checkbox.closest('tr');
|
||||
const button = checkbox.parentElement ? checkbox.parentElement.querySelector('.js-quick-select-btn') : null;
|
||||
if (row) {
|
||||
row.classList.toggle('is-selected', checkbox.checked);
|
||||
}
|
||||
setQuickSelectButtonState(button, checkbox.checked);
|
||||
});
|
||||
|
||||
const anySelected = selectedCheckboxes.length > 0;
|
||||
const canSubmit = anySelected && !!bulkAssessorSelect && !bulkAssessorSelect.disabled && bulkAssessorSelect.value !== '';
|
||||
if (bulkAssignSubmit) {
|
||||
bulkAssignSubmit.disabled = !canSubmit;
|
||||
}
|
||||
if (clearSelectedOrders) {
|
||||
clearSelectedOrders.disabled = !anySelected;
|
||||
}
|
||||
if (toggleSelectAllOrders) {
|
||||
const allSelected = bulkOrderCheckboxes.length > 0 && selectedCheckboxes.length === bulkOrderCheckboxes.length;
|
||||
toggleSelectAllOrders.textContent = allSelected ? 'Desmarcar visibles' : 'Marcar visibles';
|
||||
toggleSelectAllOrders.disabled = bulkOrderCheckboxes.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
quickSelectButtons.forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
const sourceKey = this.dataset.sourceKey || '';
|
||||
const checkbox = document.querySelector('.js-bulk-order-checkbox[data-source-key="' + sourceKey + '"]');
|
||||
if (!checkbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkbox.checked = !checkbox.checked;
|
||||
syncBulkAssignSelectionUi();
|
||||
});
|
||||
});
|
||||
|
||||
bulkOrderCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.addEventListener('change', syncBulkAssignSelectionUi);
|
||||
});
|
||||
|
||||
if (bulkAssessorSelect) {
|
||||
bulkAssessorSelect.addEventListener('change', syncBulkAssignSelectionUi);
|
||||
}
|
||||
|
||||
if (toggleSelectAllOrders) {
|
||||
toggleSelectAllOrders.addEventListener('click', function () {
|
||||
const shouldSelectAll = bulkOrderCheckboxes.some(function (checkbox) {
|
||||
return !checkbox.checked;
|
||||
});
|
||||
|
||||
bulkOrderCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.checked = shouldSelectAll;
|
||||
});
|
||||
|
||||
syncBulkAssignSelectionUi();
|
||||
});
|
||||
}
|
||||
|
||||
if (clearSelectedOrders) {
|
||||
clearSelectedOrders.addEventListener('click', function () {
|
||||
bulkOrderCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.checked = false;
|
||||
});
|
||||
|
||||
syncBulkAssignSelectionUi();
|
||||
});
|
||||
}
|
||||
|
||||
if (bulkAssignForm) {
|
||||
bulkAssignForm.addEventListener('submit', function (event) {
|
||||
const selectedCheckboxes = getSelectedBulkOrderCheckboxes();
|
||||
const assessorSelect = this.querySelector('select[name="target_assessor"]');
|
||||
|
||||
if (selectedCheckboxes.length === 0) {
|
||||
event.preventDefault();
|
||||
alert('Primero selecciona al menos un pedido.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!assessorSelect || !assessorSelect.value) {
|
||||
event.preventDefault();
|
||||
alert('Elige la asesora para hacer la asignación rápida.');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
syncBulkAssignSelectionUi();
|
||||
}
|
||||
|
||||
window.setInterval(function () {
|
||||
window.location.reload();
|
||||
}, 600000);
|
||||
|
||||
@ -30,6 +30,7 @@ function cc_test_ensure_tracking_table(PDO $pdo): void
|
||||
`estado` VARCHAR(40) NOT NULL DEFAULT 'POR LLAMAR',
|
||||
`nota_seguimiento` TEXT NULL,
|
||||
`user_id` INT NULL,
|
||||
`assigned_at` DATETIME NULL,
|
||||
`direccion` TEXT NULL,
|
||||
`referencia` TEXT NULL,
|
||||
`agencia` VARCHAR(80) NULL,
|
||||
@ -78,6 +79,7 @@ function cc_test_ensure_tracking_table(PDO $pdo): void
|
||||
KEY `idx_callcenter_test_tracking_proxima` (`proxima_llamada_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
|
||||
|
||||
cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'assigned_at', 'DATETIME NULL AFTER `user_id`');
|
||||
cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'direccion', 'TEXT NULL AFTER `user_id`');
|
||||
cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'referencia', 'TEXT NULL AFTER `direccion`');
|
||||
cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'agencia', 'VARCHAR(80) NULL AFTER `referencia`');
|
||||
@ -119,6 +121,11 @@ function cc_test_ensure_tracking_table(PDO $pdo): void
|
||||
cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'pedido_rotulado_subido_por', 'INT NULL AFTER `pedido_rotulado_subido_at`');
|
||||
cc_test_ensure_column($pdo, 'callcenter_test_tracking', 'ultima_gestion_at', 'DATETIME NULL AFTER `ruta_contraentrega_subido_por`');
|
||||
|
||||
$stmtAssignedBackfill = $pdo->query("SELECT id FROM callcenter_test_tracking WHERE assigned_at IS NULL AND user_id IS NOT NULL LIMIT 1");
|
||||
if ($stmtAssignedBackfill && $stmtAssignedBackfill->fetchColumn()) {
|
||||
$pdo->exec("UPDATE callcenter_test_tracking SET assigned_at = created_at, updated_at = updated_at WHERE assigned_at IS NULL AND user_id IS NOT NULL");
|
||||
}
|
||||
|
||||
$checked = true;
|
||||
}
|
||||
|
||||
@ -215,18 +222,27 @@ function cc_test_upsert_assignee(PDO $pdo, string $sourceKey, ?int $userId): voi
|
||||
{
|
||||
cc_test_ensure_tracking_table($pdo);
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO callcenter_test_tracking (source_key, user_id, ultima_gestion_at)
|
||||
VALUES (:source_key, :user_id, CURRENT_TIMESTAMP)
|
||||
$assignedAt = $userId === null ? null : (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO callcenter_test_tracking (source_key, user_id, assigned_at, ultima_gestion_at)
|
||||
VALUES (:source_key, :user_id, :assigned_at, CURRENT_TIMESTAMP)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
user_id = VALUES(user_id),
|
||||
assigned_at = CASE
|
||||
WHEN VALUES(user_id) IS NULL THEN NULL
|
||||
WHEN assigned_at IS NULL OR user_id IS NULL OR user_id <> VALUES(user_id) THEN VALUES(assigned_at)
|
||||
ELSE assigned_at
|
||||
END,
|
||||
ultima_gestion_at = VALUES(ultima_gestion_at),
|
||||
updated_at = CURRENT_TIMESTAMP");
|
||||
|
||||
$stmt->bindValue(':source_key', $sourceKey, PDO::PARAM_STR);
|
||||
if ($userId === null) {
|
||||
$stmt->bindValue(':user_id', null, PDO::PARAM_NULL);
|
||||
$stmt->bindValue(':assigned_at', null, PDO::PARAM_NULL);
|
||||
} else {
|
||||
$stmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':assigned_at', $assignedAt, PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
@ -18,8 +18,10 @@ function drive_test_available_stores(): array
|
||||
'label' => 'Flower',
|
||||
'spreadsheet_id' => '1SSmQuR9quxeQbMKNMDkRe8-n1gU7WuEfsFaJ3WKFO-c',
|
||||
'sheet_gid' => null,
|
||||
'startRow' => 5552,
|
||||
'enforce_db_start_row' => false,
|
||||
'startRow' => 7796, // Pedido #37431
|
||||
'min_codigo_number' => 37431,
|
||||
'enforce_db_start_row' => true,
|
||||
'incremental_sync' => true,
|
||||
],
|
||||
// Segunda tienda (nuevo link que pasaste).
|
||||
'otra_tienda' => [
|
||||
@ -28,6 +30,7 @@ function drive_test_available_stores(): array
|
||||
'sheet_gid' => 1523126328,
|
||||
'startRow' => 6804,
|
||||
'enforce_db_start_row' => true,
|
||||
'incremental_sync' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
@ -94,6 +97,16 @@ function drive_test_get_cell(array $row, array $indexes, array $aliases, string
|
||||
return $default;
|
||||
}
|
||||
|
||||
function drive_test_extract_codigo_number(?string $codigo): ?int
|
||||
{
|
||||
$digits = preg_replace('/\D+/', '', trim((string) $codigo));
|
||||
if ($digits === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $digits;
|
||||
}
|
||||
|
||||
function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $storeKey = 'flower'): array
|
||||
{
|
||||
$storeConfig = drive_test_get_store_config($storeKey);
|
||||
@ -157,6 +170,7 @@ function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $
|
||||
$previewRows = array_reverse($previewRows, true);
|
||||
|
||||
$orders = [];
|
||||
$minCodigoNumber = isset($storeConfig['min_codigo_number']) ? (int) ($storeConfig['min_codigo_number'] ?? 0) : 0;
|
||||
|
||||
foreach ($previewRows as $rowIndex => $row) {
|
||||
$codigo = trim((string) ($row[0] ?? ''));
|
||||
@ -177,6 +191,11 @@ function drive_test_fetch_orders(int $limit = 10, int $startRow = 5552, string $
|
||||
$referencia = drive_test_get_cell($row, $headerIndexes, ['REFERENCIA']);
|
||||
$distrito = drive_test_get_cell($row, $headerIndexes, ['DISTRITO']);
|
||||
$sourceRow = (int) $rowIndex + 2;
|
||||
$codigoNumber = drive_test_extract_codigo_number($codigo);
|
||||
|
||||
if ($minCodigoNumber > 0 && ($codigoNumber === null || $codigoNumber < $minCodigoNumber)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Importante: para 'flower' mantenemos EXACTAMENTE el algoritmo anterior
|
||||
// para no romper el historial/tracking ya existente.
|
||||
@ -229,7 +248,7 @@ function drive_test_fetch_tracking(PDO $pdo, array $sourceKeys): array
|
||||
cc_test_ensure_tracking_table($pdo);
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($sourceKeys), '?'));
|
||||
$stmt = $pdo->prepare("SELECT source_key, estado, nota_seguimiento, user_id, direccion, referencia, agencia, sede_agencia, sede, ciudad, distrito, dni, numero_cuenta_sede_id, numero_cuenta_dni, observaciones, coordenadas, producto, cantidad, precio, monto_adelantado, confirmacion_producto, confirmacion_cantidad, confirmacion_precio, confirmacion_producto_extra, confirmacion_cantidad_extra, confirmacion_precio_extra, proxima_llamada_at, fecha_entrega_programada, numero_cuenta_enviado_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, ruta_contraentrega_subido_at, ruta_contraentrega_subido_por, pedido_rotulado_pedido_id, pedido_rotulado_subido_at, pedido_rotulado_subido_por, ultima_gestion_at, updated_at FROM callcenter_test_tracking WHERE source_key IN ($placeholders)");
|
||||
$stmt = $pdo->prepare("SELECT source_key, estado, nota_seguimiento, user_id, assigned_at, direccion, referencia, agencia, sede_agencia, sede, ciudad, distrito, dni, numero_cuenta_sede_id, numero_cuenta_dni, observaciones, coordenadas, producto, cantidad, precio, monto_adelantado, confirmacion_producto, confirmacion_cantidad, confirmacion_precio, confirmacion_producto_extra, confirmacion_cantidad_extra, confirmacion_precio_extra, proxima_llamada_at, fecha_entrega_programada, numero_cuenta_enviado_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, ruta_contraentrega_subido_at, ruta_contraentrega_subido_por, pedido_rotulado_pedido_id, pedido_rotulado_subido_at, pedido_rotulado_subido_por, ultima_gestion_at, updated_at FROM callcenter_test_tracking WHERE source_key IN ($placeholders)");
|
||||
$stmt->execute($sourceKeys);
|
||||
|
||||
$tracking = [];
|
||||
@ -251,6 +270,7 @@ function drive_test_merge_tracking(array $orders, array $tracking): array
|
||||
$userId = array_key_exists('user_id', (array) $current) ? $current['user_id'] : null;
|
||||
$order['user_id'] = $userId !== null ? ((int) $userId > 0 ? (int) $userId : null) : null;
|
||||
$order['seguimiento_actualizado'] = $current['updated_at'] ?? null;
|
||||
$order['assigned_at'] = $current['assigned_at'] ?? null;
|
||||
$order['proxima_llamada_at'] = $current['proxima_llamada_at'] ?? null;
|
||||
$order['fecha_entrega_programada'] = $current['fecha_entrega_programada'] ?? null;
|
||||
$order['numero_cuenta_enviado_at'] = $current['numero_cuenta_enviado_at'] ?? null;
|
||||
@ -628,12 +648,21 @@ function drive_test_fetch_orders_from_db(PDO $pdo, string $storeKey, ?bool $only
|
||||
$stmt->execute($params);
|
||||
|
||||
$orders = [];
|
||||
$minCodigoNumber = isset($storeConfig['min_codigo_number']) ? (int) ($storeConfig['min_codigo_number'] ?? 0) : 0;
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$isAgregado = (int) ($row['is_agregado'] ?? 0);
|
||||
$codigo = (string) ($row['codigo'] ?? '');
|
||||
$codigoNumber = drive_test_extract_codigo_number($codigo);
|
||||
|
||||
if ($minCodigoNumber > 0 && $isAgregado !== 1 && ($codigoNumber === null || $codigoNumber < $minCodigoNumber)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orders[] = [
|
||||
'id' => (int) ($row['id'] ?? 0),
|
||||
'is_agregado' => (int) ($row['is_agregado'] ?? 0),
|
||||
'is_agregado' => $isAgregado,
|
||||
'source_key' => (string) ($row['source_key'] ?? ''),
|
||||
'codigo' => (string) ($row['codigo'] ?? ''),
|
||||
'codigo' => $codigo,
|
||||
'import_id' => (string) ($row['import_id'] ?? ''),
|
||||
'source_row' => isset($row['source_row']) && (int) $row['source_row'] > 0 ? (int) $row['source_row'] : null,
|
||||
'drive_imported_at' => (string) ($row['drive_imported_at'] ?? ''),
|
||||
|
||||
@ -88,6 +88,7 @@ ensureTipoPaqueteEnumDefinition($pdo);
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$user_role = $_SESSION['user_role'] ?? 'Asesor';
|
||||
$is_admin = in_array($user_role, ['Administrador', 'admin'], true);
|
||||
|
||||
// Fetch years for the filter
|
||||
$years_query = "SELECT DISTINCT YEAR(created_at) as year FROM pedidos";
|
||||
@ -337,17 +338,19 @@ include 'layout_header.php';
|
||||
AGREGAR PEDIDOS CONTRAENTREGA
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
$excelParams = array_filter([
|
||||
'q' => $search_query,
|
||||
], static function ($value) {
|
||||
return $value !== '' && $value !== null;
|
||||
});
|
||||
$excelUrl = 'download_ruta_contraentrega.php' . (!empty($excelParams) ? '?' . http_build_query($excelParams) : '');
|
||||
?>
|
||||
<a href="<?php echo htmlspecialchars($excelUrl); ?>" class="btn btn-success">
|
||||
<i class="fas fa-file-excel me-1"></i> Descargar Excel (Mañana)
|
||||
</a>
|
||||
<?php if ($is_admin): ?>
|
||||
<?php
|
||||
$excelParams = array_filter([
|
||||
'q' => $search_query,
|
||||
], static function ($value) {
|
||||
return $value !== '' && $value !== null;
|
||||
});
|
||||
$excelUrl = 'download_ruta_contraentrega.php' . (!empty($excelParams) ? '?' . http_build_query($excelParams) : '');
|
||||
?>
|
||||
<a href="<?php echo htmlspecialchars($excelUrl); ?>" class="btn btn-success">
|
||||
<i class="fas fa-file-excel me-1"></i> Descargar Excel (Mañana)
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user