Autosave: 20260730-065528
This commit is contained in:
parent
cd63f23d4e
commit
b009b4b95c
@ -14,7 +14,7 @@ $provinciasPorDepartamentoContraentrega = contraentregaProvinciasPorDepartamento
|
||||
$distritosPorProvinciaContraentrega = contraentregaDistritosPorProvincia();
|
||||
$departamentosContraentrega = array_keys($provinciasPorDepartamentoContraentrega);
|
||||
$pageTitle = 'Pedidos Asignados | Bandeja de la asesora';
|
||||
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con la misma interfaz, estados, historial y acciones.';
|
||||
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con vistas de hoy, últimos 3 días hábiles, estados, historial y acciones.';
|
||||
|
||||
$kpiPeriodConfig = cc_test_resolve_kpi_period((string) ($_GET['kpi_period'] ?? 'today'), (string) ($_GET['kpi_date'] ?? ''));
|
||||
$kpiPeriodKey = (string) ($kpiPeriodConfig['key'] ?? 'today');
|
||||
@ -83,7 +83,57 @@ function cc_test_format_date(?string $value, string $fallback = 'Sin fecha'): st
|
||||
function cc_test_format_date_input(?string $value): string
|
||||
{
|
||||
$date = cc_test_parse_datetime($value);
|
||||
return $date ? $date->format('Y-m-d') : '';
|
||||
return $date ? $date->format('Y-m-d\TH:i') : '';
|
||||
}
|
||||
|
||||
function cc_test_is_working_day(DateTimeImmutable $date): bool
|
||||
{
|
||||
return (int) $date->format('N') !== 7;
|
||||
}
|
||||
|
||||
function cc_test_last_working_day_keys(DateTimeImmutable $todayStart, int $days = 3): array
|
||||
{
|
||||
$days = max(1, $days);
|
||||
$cursor = $todayStart->setTime(0, 0, 0);
|
||||
|
||||
while (!cc_test_is_working_day($cursor)) {
|
||||
$cursor = $cursor->sub(new DateInterval('P1D'));
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
while (count($keys) < $days) {
|
||||
if (cc_test_is_working_day($cursor)) {
|
||||
$keys[] = $cursor->format('Y-m-d');
|
||||
}
|
||||
$cursor = $cursor->sub(new DateInterval('P1D'));
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
function cc_test_recent_followup_anchor(array $order): ?DateTimeImmutable
|
||||
{
|
||||
$assignedDate = cc_test_parse_datetime($order['assigned_at'] ?? null);
|
||||
if ($assignedDate instanceof DateTimeImmutable) {
|
||||
return $assignedDate;
|
||||
}
|
||||
|
||||
return cc_test_followup_started_at($order);
|
||||
}
|
||||
|
||||
function cc_test_is_within_last_days_window(array $order, DateTimeImmutable $todayStart, int $days = 3): bool
|
||||
{
|
||||
if ($days < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$anchorDate = cc_test_recent_followup_anchor($order);
|
||||
if (!$anchorDate instanceof DateTimeImmutable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$anchorKey = $anchorDate->setTime(0, 0, 0)->format('Y-m-d');
|
||||
return in_array($anchorKey, cc_test_last_working_day_keys($todayStart, $days), true);
|
||||
}
|
||||
|
||||
function cc_test_format_price(?string $value): string
|
||||
@ -309,6 +359,24 @@ $viewCards = $isTuaniRecuperablesStore ? [
|
||||
'stat_key' => 'nuevos_hoy',
|
||||
'active_class' => 'bg-primary-subtle border border-primary',
|
||||
],
|
||||
'ultimos_3_dias' => [
|
||||
'label' => 'Últimos 3 días hábiles',
|
||||
'heading' => 'Pedidos asignados de los últimos 3 días hábiles',
|
||||
'stat_key' => 'ultimos_3_dias',
|
||||
'active_class' => 'bg-info-subtle border border-info',
|
||||
],
|
||||
'observados' => [
|
||||
'label' => 'Observados',
|
||||
'heading' => 'Observados',
|
||||
'stat_key' => 'observados',
|
||||
'active_class' => 'bg-warning-subtle border border-warning',
|
||||
],
|
||||
'seguimiento' => [
|
||||
'label' => 'Seguimiento',
|
||||
'heading' => 'Seguimiento',
|
||||
'stat_key' => 'seguimiento',
|
||||
'active_class' => 'bg-primary-subtle border border-primary',
|
||||
],
|
||||
'confirmados' => [
|
||||
'label' => 'Confirmados',
|
||||
'heading' => 'Confirmados',
|
||||
@ -321,24 +389,12 @@ $viewCards = $isTuaniRecuperablesStore ? [
|
||||
'stat_key' => 'recuperados',
|
||||
'active_class' => 'bg-warning-subtle border border-warning',
|
||||
],
|
||||
'seguimiento' => [
|
||||
'label' => 'Seguimiento',
|
||||
'heading' => 'Seguimiento',
|
||||
'stat_key' => 'seguimiento',
|
||||
'active_class' => 'bg-primary-subtle border border-primary',
|
||||
],
|
||||
'promo_final' => [
|
||||
'label' => 'Promo Final',
|
||||
'heading' => 'Promo Final',
|
||||
'stat_key' => 'promo_final',
|
||||
'active_class' => 'bg-danger-subtle border border-danger',
|
||||
],
|
||||
'observados' => [
|
||||
'label' => 'Observados',
|
||||
'heading' => 'Observados',
|
||||
'stat_key' => 'observados',
|
||||
'active_class' => 'bg-warning-subtle border border-warning',
|
||||
],
|
||||
'cerrados' => [
|
||||
'label' => 'Cerrados',
|
||||
'heading' => 'Cerrados / descartados',
|
||||
@ -366,7 +422,7 @@ $_SESSION[cc_test_callcenter_view_session_key()] = cc_test_normalize_context_key
|
||||
|
||||
$viewStatesNote = $isTuaniRecuperablesStore
|
||||
? 'Estados disponibles: <strong>Carritos recuperables</strong>, <strong>Seguimiento</strong>, <strong>Observados</strong>, <strong>Confirmados</strong>, <strong>Recuperados</strong> y <strong>Todos</strong>. Esta bandeja compartida está disponible para cualquier asesora y, cuando una asesora gestiona un carrito recuperable, el sistema lo asigna automáticamente a quien lo trabaja. Si el teléfono ya existe en <strong>TUANI principal</strong>, se marca en gris como <strong>Pedido repetido en tienda</strong> solo en esta bandeja.'
|
||||
: 'Estados disponibles: Por llamar, Devolver llamada, Observado, Se envió número de cuenta, Confirmado contraentrega, Confirmado envío, Cancelado y Repetido. <strong>Promo Final</strong> se habilita desde el Día 4 y exige imagen de sustento para mover el pedido. 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.';
|
||||
: '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 4 y exige imagen de sustento para mover el pedido. 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.';
|
||||
|
||||
$errorMessage = null;
|
||||
$noticeMessage = null;
|
||||
@ -386,6 +442,7 @@ $stats = [
|
||||
'total' => 0,
|
||||
'pendientes_hoy' => 0,
|
||||
'nuevos_hoy' => 0,
|
||||
'ultimos_3_dias' => 0,
|
||||
'confirmados' => 0,
|
||||
'recuperados' => 0,
|
||||
'seguimiento' => 0,
|
||||
@ -649,6 +706,7 @@ try {
|
||||
$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_ultimos_3_dias'] = cc_test_is_within_last_days_window($order, $todayStart, 3);
|
||||
$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['assessor_key'] = cc_test_find_assessor_key_by_user_id(isset($order['user_id']) ? (int) $order['user_id'] : null, $assessors);
|
||||
@ -665,6 +723,9 @@ try {
|
||||
if ($order['es_nuevo_hoy']) {
|
||||
$stats['nuevos_hoy']++;
|
||||
}
|
||||
if ($order['es_ultimos_3_dias']) {
|
||||
$stats['ultimos_3_dias']++;
|
||||
}
|
||||
if ($order['es_promo_final']) {
|
||||
$stats['promo_final']++;
|
||||
}
|
||||
@ -751,6 +812,7 @@ try {
|
||||
return match ($view) {
|
||||
'pendientes_hoy' => (bool) ($order['es_pendiente_hoy'] ?? false),
|
||||
'nuevos_hoy' => (bool) ($order['es_nuevo_hoy'] ?? false),
|
||||
'ultimos_3_dias' => (bool) ($order['es_ultimos_3_dias'] ?? false),
|
||||
'confirmados' => in_array(($order['estado'] ?? ''), cc_test_confirmed_states(), true),
|
||||
'recuperados' => cc_test_is_recovered_today($order, $todayStart),
|
||||
'seguimiento' => ($order['estado'] ?? '') === 'SE ENVIO NUMERO DE CUENTA' && !($order['es_promo_final'] ?? false),
|
||||
@ -779,6 +841,25 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
if ($view === 'ultimos_3_dias') {
|
||||
$aRecent = cc_test_recent_followup_anchor($a);
|
||||
$bRecent = cc_test_recent_followup_anchor($b);
|
||||
$aRecentTs = $aRecent instanceof DateTimeImmutable ? $aRecent->getTimestamp() : null;
|
||||
$bRecentTs = $bRecent instanceof DateTimeImmutable ? $bRecent->getTimestamp() : null;
|
||||
if ($aRecentTs !== null && $bRecentTs !== null && $aRecentTs !== $bRecentTs) {
|
||||
return $aRecentTs <=> $bRecentTs;
|
||||
}
|
||||
if ($aRecentTs !== null && $bRecentTs === null) {
|
||||
return -1;
|
||||
}
|
||||
if ($aRecentTs === null && $bRecentTs !== null) {
|
||||
return 1;
|
||||
}
|
||||
if ($aImport !== $bImport) {
|
||||
return $bImport <=> $aImport;
|
||||
}
|
||||
}
|
||||
|
||||
if ($view === 'recuperados') {
|
||||
$aRecovered = cc_test_dashboard_action_datetime($a);
|
||||
$bRecovered = cc_test_dashboard_action_datetime($b);
|
||||
@ -946,7 +1027,7 @@ if ($isTuaniRecuperablesStore) {
|
||||
$pageDescription = 'Bandeja compartida para que cualquier asesora gestione carritos recuperables con vistas de seguimiento y observados desde Drive.';
|
||||
} else {
|
||||
$pageTitle = 'Pedidos Asignados | Bandeja de la asesora';
|
||||
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con la misma interfaz, estados, historial y acciones.';
|
||||
$pageDescription = 'Bandeja para que cada asesora gestione sus pedidos asignados con vistas de hoy, últimos 3 días hábiles, estados, historial y acciones.';
|
||||
}
|
||||
|
||||
$callCenterSectionLabel = $isTuaniRecuperablesStore ? 'Carritos recuperables' : 'Pedidos Asignados';
|
||||
@ -1586,7 +1667,7 @@ require_once 'layout_header.php';
|
||||
<button type="button" class="btn-close mt-1" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="border border-primary-subtle rounded-4 bg-primary-subtle p-2 mb-3" id="airDroidModalCard-<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
<div class="border border-primary-subtle rounded-4 bg-primary-subtle p-2 mb-3 sticky-top shadow-sm" id="airDroidModalCard-<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||
<div>
|
||||
<div class="small text-muted mb-1">Llamar / Gestionar</div>
|
||||
@ -1636,12 +1717,18 @@ require_once 'layout_header.php';
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="confirmacion_producto-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Producto</label>
|
||||
<select class="form-select w-100" id="confirmacion_producto-<?php echo htmlspecialchars($order['source_key']); ?>" title="Seleccione un producto del sistema">
|
||||
<option value="">Seleccione un producto del sistema</option>
|
||||
<?php foreach ($catalogoProductos as $catalogoProducto): ?>
|
||||
<option value="<?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?>" <?php echo ((string) ($order['confirmacion_producto'] ?? '') === (string) ($catalogoProducto['nombre'] ?? '')) ? 'selected' : ''; ?>><?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control w-100"
|
||||
id="confirmacion_producto-<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||
list="catalogo-productos-list"
|
||||
value="<?php echo htmlspecialchars((string) ($order['confirmacion_producto'] ?? '')); ?>"
|
||||
placeholder="Escribe para buscar un producto"
|
||||
title="Escribe para buscar un producto del sistema"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<label for="confirmacion_cantidad-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Cantidad</label>
|
||||
@ -1663,12 +1750,18 @@ require_once 'layout_header.php';
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="confirmacion_producto_extra-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Producto adicional</label>
|
||||
<select class="form-select w-100" id="confirmacion_producto_extra-<?php echo htmlspecialchars($order['source_key']); ?>" title="Seleccione un producto adicional">
|
||||
<option value="">Seleccione un producto adicional</option>
|
||||
<?php foreach ($catalogoProductos as $catalogoProducto): ?>
|
||||
<option value="<?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?>" <?php echo ((string) ($order['confirmacion_producto_extra'] ?? '') === (string) ($catalogoProducto['nombre'] ?? '')) ? 'selected' : ''; ?>><?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control w-100"
|
||||
id="confirmacion_producto_extra-<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||
list="catalogo-productos-list"
|
||||
value="<?php echo htmlspecialchars((string) ($order['confirmacion_producto_extra'] ?? '')); ?>"
|
||||
placeholder="Escribe para buscar un producto adicional"
|
||||
title="Escribe para buscar un producto adicional"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<label for="confirmacion_cantidad_extra-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Cantidad</label>
|
||||
@ -1991,6 +2084,21 @@ require_once 'layout_header.php';
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if (!empty($catalogoProductos)): ?>
|
||||
<datalist id="catalogo-productos-list">
|
||||
<?php
|
||||
$catalogoProductoNombres = [];
|
||||
foreach ($catalogoProductos as $catalogoProducto):
|
||||
$catalogoProductoNombre = trim((string) ($catalogoProducto['nombre'] ?? ''));
|
||||
if ($catalogoProductoNombre === '' || isset($catalogoProductoNombres[$catalogoProductoNombre])) {
|
||||
continue;
|
||||
}
|
||||
$catalogoProductoNombres[$catalogoProductoNombre] = true;
|
||||
?>
|
||||
<option value="<?php echo htmlspecialchars($catalogoProductoNombre); ?>"></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($modalsHtml)): ?>
|
||||
<?php echo implode("
|
||||
", $modalsHtml); ?>
|
||||
@ -2323,15 +2431,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('select[id^="confirmacion_producto-"]').forEach(select => {
|
||||
const sourceKey = select.id.replace('confirmacion_producto-', '');
|
||||
document.querySelectorAll('input[id^="confirmacion_producto-"], input[id^="confirmacion_producto_extra-"]').forEach(input => {
|
||||
const syncTitle = () => {
|
||||
const selectedOption = select.selectedOptions && select.selectedOptions[0] ? select.selectedOptions[0] : null;
|
||||
const label = selectedOption && selectedOption.textContent ? selectedOption.textContent.trim() : (select.value || 'Sin producto seleccionado');
|
||||
select.title = label;
|
||||
const value = (input.value || '').trim();
|
||||
input.title = value !== '' ? value : (input.getAttribute('placeholder') || 'Escribe para buscar un producto');
|
||||
};
|
||||
syncTitle();
|
||||
select.addEventListener('change', syncTitle);
|
||||
input.addEventListener('input', syncTitle);
|
||||
input.addEventListener('change', syncTitle);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.js-toggle-confirmacion-extra').forEach(button => {
|
||||
|
||||
@ -2195,7 +2195,7 @@ require_once 'layout_header.php';
|
||||
<button type="button" class="btn-close mt-1" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="border border-primary-subtle rounded-4 bg-primary-subtle p-2 mb-3" id="airDroidModalCard-<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
<div class="border border-primary-subtle rounded-4 bg-primary-subtle p-2 mb-3 sticky-top shadow-sm" id="airDroidModalCard-<?php echo htmlspecialchars($order['source_key']); ?>">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||
<div>
|
||||
<div class="small text-muted mb-1">Llamar / Gestionar</div>
|
||||
@ -2245,12 +2245,18 @@ require_once 'layout_header.php';
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="confirmacion_producto-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Producto</label>
|
||||
<select class="form-select w-100" id="confirmacion_producto-<?php echo htmlspecialchars($order['source_key']); ?>" title="Seleccione un producto del sistema">
|
||||
<option value="">Seleccione un producto del sistema</option>
|
||||
<?php foreach ($catalogoProductos as $catalogoProducto): ?>
|
||||
<option value="<?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?>" <?php echo ((string) ($order['confirmacion_producto'] ?? '') === (string) ($catalogoProducto['nombre'] ?? '')) ? 'selected' : ''; ?>><?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control w-100"
|
||||
id="confirmacion_producto-<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||
list="catalogo-productos-list"
|
||||
value="<?php echo htmlspecialchars((string) ($order['confirmacion_producto'] ?? '')); ?>"
|
||||
placeholder="Escribe para buscar un producto"
|
||||
title="Escribe para buscar un producto del sistema"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<label for="confirmacion_cantidad-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Cantidad</label>
|
||||
@ -2272,12 +2278,18 @@ require_once 'layout_header.php';
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="confirmacion_producto_extra-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Producto adicional</label>
|
||||
<select class="form-select w-100" id="confirmacion_producto_extra-<?php echo htmlspecialchars($order['source_key']); ?>" title="Seleccione un producto adicional">
|
||||
<option value="">Seleccione un producto adicional</option>
|
||||
<?php foreach ($catalogoProductos as $catalogoProducto): ?>
|
||||
<option value="<?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?>" <?php echo ((string) ($order['confirmacion_producto_extra'] ?? '') === (string) ($catalogoProducto['nombre'] ?? '')) ? 'selected' : ''; ?>><?php echo htmlspecialchars((string) ($catalogoProducto['nombre'] ?? '')); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control w-100"
|
||||
id="confirmacion_producto_extra-<?php echo htmlspecialchars($order['source_key']); ?>"
|
||||
list="catalogo-productos-list"
|
||||
value="<?php echo htmlspecialchars((string) ($order['confirmacion_producto_extra'] ?? '')); ?>"
|
||||
placeholder="Escribe para buscar un producto adicional"
|
||||
title="Escribe para buscar un producto adicional"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<label for="confirmacion_cantidad_extra-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Cantidad</label>
|
||||
@ -2605,6 +2617,21 @@ require_once 'layout_header.php';
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if (!empty($catalogoProductos)): ?>
|
||||
<datalist id="catalogo-productos-list">
|
||||
<?php
|
||||
$catalogoProductoNombres = [];
|
||||
foreach ($catalogoProductos as $catalogoProducto):
|
||||
$catalogoProductoNombre = trim((string) ($catalogoProducto['nombre'] ?? ''));
|
||||
if ($catalogoProductoNombre === '' || isset($catalogoProductoNombres[$catalogoProductoNombre])) {
|
||||
continue;
|
||||
}
|
||||
$catalogoProductoNombres[$catalogoProductoNombre] = true;
|
||||
?>
|
||||
<option value="<?php echo htmlspecialchars($catalogoProductoNombre); ?>"></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<?php endif; ?>
|
||||
<?php if (is_resource($modalsHtml)): ?>
|
||||
<?php rewind($modalsHtml); fpassthru($modalsHtml); fclose($modalsHtml); ?>
|
||||
<?php endif; ?>
|
||||
@ -2981,15 +3008,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('select[id^="confirmacion_producto-"]').forEach(select => {
|
||||
const sourceKey = select.id.replace('confirmacion_producto-', '');
|
||||
document.querySelectorAll('input[id^="confirmacion_producto-"], input[id^="confirmacion_producto_extra-"]').forEach(input => {
|
||||
const syncTitle = () => {
|
||||
const selectedOption = select.selectedOptions && select.selectedOptions[0] ? select.selectedOptions[0] : null;
|
||||
const label = selectedOption && selectedOption.textContent ? selectedOption.textContent.trim() : (select.value || 'Sin producto seleccionado');
|
||||
select.title = label;
|
||||
const value = (input.value || '').trim();
|
||||
input.title = value !== '' ? value : (input.getAttribute('placeholder') || 'Escribe para buscar un producto');
|
||||
};
|
||||
syncTitle();
|
||||
select.addEventListener('change', syncTitle);
|
||||
input.addEventListener('input', syncTitle);
|
||||
input.addEventListener('change', syncTitle);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.js-toggle-confirmacion-extra').forEach(button => {
|
||||
|
||||
@ -36,6 +36,8 @@ function drive_test_available_stores(): array
|
||||
'status_sync_column' => 'V',
|
||||
'advisor_sync_start_row' => 7207,
|
||||
'advisor_sync_column' => 'X',
|
||||
'delivery_sync_start_row' => 7291,
|
||||
'delivery_sync_column' => 'W',
|
||||
],
|
||||
// Bandeja compartida de carritos recuperables: misma estructura, pero desde la fila 2000.
|
||||
'tuani_recuperables' => [
|
||||
@ -188,6 +190,70 @@ function drive_test_advisor_sync_column(string $storeKey): string
|
||||
return preg_match('/^[A-Z]+$/', $column) ? $column : 'R';
|
||||
}
|
||||
|
||||
function drive_test_delivery_sync_start_row(string $storeKey): ?int
|
||||
{
|
||||
$storeConfig = drive_test_get_store_config($storeKey);
|
||||
if (!array_key_exists('delivery_sync_start_row', $storeConfig)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$startRow = (int) $storeConfig['delivery_sync_start_row'];
|
||||
return $startRow > 0 ? $startRow : null;
|
||||
}
|
||||
|
||||
function drive_test_delivery_sync_column(string $storeKey): string
|
||||
{
|
||||
$storeConfig = drive_test_get_store_config($storeKey);
|
||||
$column = strtoupper(trim((string) ($storeConfig['delivery_sync_column'] ?? 'W')));
|
||||
|
||||
return preg_match('/^[A-Z]+$/', $column) ? $column : 'W';
|
||||
}
|
||||
|
||||
function drive_test_format_programmed_delivery_label(?string $deliveryDate): string
|
||||
{
|
||||
$deliveryDate = trim((string) $deliveryDate);
|
||||
if ($deliveryDate === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parsedDeliveryDate = DateTimeImmutable::createFromFormat('Y-m-d', $deliveryDate);
|
||||
if (!$parsedDeliveryDate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'PROGRAMADO ' . $parsedDeliveryDate->format('d');
|
||||
}
|
||||
|
||||
function drive_test_format_sheet_w_label(string $estado, ?string $advisorName = null, ?string $deliveryDate = null): string
|
||||
{
|
||||
$estado = cc_test_normalize_state(trim($estado));
|
||||
$advisorName = drive_test_compact_text($advisorName);
|
||||
|
||||
if ($estado === 'CONFIRMADO CONTRAENTREGA') {
|
||||
return drive_test_format_programmed_delivery_label($deliveryDate);
|
||||
}
|
||||
|
||||
$labelMap = [
|
||||
'SE ENVIO NUMERO DE CUENTA' => 'SE ENVIO NUMERO DE CUENTA',
|
||||
'DEVOLVER LLAMADA' => 'DEVOLVER LLAMADA',
|
||||
'OBSERVADO' => 'OBSERVADO',
|
||||
'CANCELADO' => 'CANCELADO',
|
||||
'REPETIDO' => 'REPETIDO',
|
||||
'CONFIRMADO ENVIO' => 'ENVIO',
|
||||
];
|
||||
|
||||
if (!isset($labelMap[$estado])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$label = $labelMap[$estado];
|
||||
if ($advisorName !== '') {
|
||||
$label .= ' - ' . $advisorName;
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
function drive_test_status_sync_reference_datetime(array $sourceOrder): ?DateTimeImmutable
|
||||
{
|
||||
$candidates = [
|
||||
@ -241,7 +307,7 @@ function drive_test_should_sync_status_to_sheet(array $sourceOrder): bool
|
||||
return $referenceDate->format('Y-m-d') >= $cutoff->format('Y-m-d');
|
||||
}
|
||||
|
||||
function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?string $advisorName = null): array
|
||||
function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?string $advisorName = null, ?string $deliveryDate = null): array
|
||||
{
|
||||
$storeKey = trim((string) ($sourceOrder['store_key'] ?? ''));
|
||||
if ($storeKey === '') {
|
||||
@ -290,6 +356,16 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
|
||||
$service = new Google\Service\Sheets($client);
|
||||
$statusRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $statusColumn . $sourceRow);
|
||||
$estadoNormalizado = cc_test_normalize_state(trim($estado));
|
||||
|
||||
$deliveryDate = trim((string) $deliveryDate);
|
||||
if ($deliveryDate !== '') {
|
||||
$parsedDeliveryDate = DateTimeImmutable::createFromFormat('Y-m-d', $deliveryDate);
|
||||
$deliveryDate = $parsedDeliveryDate ? $parsedDeliveryDate->format('Y-m-d') : '';
|
||||
}
|
||||
|
||||
$advisorName = drive_test_compact_text($advisorName);
|
||||
$deliveryLabel = drive_test_format_sheet_w_label($estadoNormalizado, $advisorName, $deliveryDate);
|
||||
|
||||
$updates = [
|
||||
new Google\Service\Sheets\ValueRange([
|
||||
'range' => $statusRange,
|
||||
@ -297,7 +373,17 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
|
||||
]),
|
||||
];
|
||||
|
||||
$advisorName = drive_test_compact_text($advisorName);
|
||||
$deliveryRange = null;
|
||||
$deliveryStartRow = drive_test_delivery_sync_start_row($storeKey);
|
||||
if ($deliveryStartRow !== null && $sourceRow >= $deliveryStartRow) {
|
||||
$deliveryColumn = drive_test_delivery_sync_column($storeKey);
|
||||
$deliveryRange = drive_test_resolve_sheet_a1_range($service, $spreadsheetId, $sheetGid, $deliveryColumn . $sourceRow);
|
||||
$updates[] = new Google\Service\Sheets\ValueRange([
|
||||
'range' => $deliveryRange,
|
||||
'values' => [[ $deliveryLabel ]],
|
||||
]);
|
||||
}
|
||||
|
||||
$advisorRange = null;
|
||||
$advisorStartRow = drive_test_advisor_sync_start_row($storeKey);
|
||||
if ($advisorName !== '' && $advisorStartRow !== null && $sourceRow >= $advisorStartRow) {
|
||||
@ -322,8 +408,11 @@ function drive_test_sync_status_to_sheet(array $sourceOrder, string $estado, ?st
|
||||
'source_row' => $sourceRow,
|
||||
'range' => $statusRange,
|
||||
'advisor_range' => $advisorRange,
|
||||
'delivery_range' => $deliveryRange,
|
||||
'estado' => $estadoNormalizado,
|
||||
'advisor_name' => $advisorRange !== null ? $advisorName : null,
|
||||
'delivery_date' => $deliveryRange !== null ? $deliveryDate : null,
|
||||
'delivery_label' => $deliveryRange !== null ? $deliveryLabel : null,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -1368,7 +1368,7 @@ try {
|
||||
$driveSyncResult = null;
|
||||
if ($sourceOrderForAuth) {
|
||||
try {
|
||||
$driveSyncResult = drive_test_sync_status_to_sheet($sourceOrderForAuth, $estado, $advisorNameForDrive);
|
||||
$driveSyncResult = drive_test_sync_status_to_sheet($sourceOrderForAuth, $estado, $advisorNameForDrive, $fechaEntrega);
|
||||
} catch (Throwable $driveException) {
|
||||
error_log('update_callcenter_test_tracking.php drive sync: ' . $driveException->getMessage());
|
||||
$driveSyncResult = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user