fix: 7 de las 17 novedades de ausentismo eran inalcanzables

buildDynamicListResponse cortaba en 10 filas con array_slice y descartaba el
resto sin aviso, asi que el catalogo de motivos mostraba 10 de 17 y las otras
7 no habia forma de elegirlas.

Ahora pagina: 9 opciones y "Ver mas" con cuantas faltan, hasta agotarlas. El
10 dejo de estar suelto en el codigo y quedo como LISTA_MAX_FILAS, que es el
limite de WhatsApp de donde salio el problema.

Solo cubre los campos select; dynamic_list lo va a necesitar para los lotes de
Ciclos y va con ese trabajo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-04 22:16:17 -05:00
co-authored by Claude Sonnet 4.6
parent f06a400d16
commit 576ea7de40
2 changed files with 64 additions and 1 deletions
+29 -1
View File
@@ -3,6 +3,9 @@ declare(strict_types=1);
class NormalBot
{
/** Filas máximas de una lista interactiva de WhatsApp. Estaba suelto como 10. */
private const LISTA_MAX_FILAS = 10;
public static function process(array $company, array $context, string $input, bool $suppressFallback = false): ?array
{
$config = self::getConfig($company);
@@ -549,12 +552,28 @@ class NormalBot
$sections[] = ['title' => $secTitle, 'rows' => array_slice($rows, 0, 10)];
}
} else {
// Con más opciones que filas disponibles se pagina: el último lugar
// queda para "Ver más". Sin esto las sobrantes eran inalcanzables.
$pagina = max(0, (int)($flow['__page'] ?? 0));
$hayMas = count($items) > self::LISTA_MAX_FILAS;
$porPagina = $hayMas ? self::LISTA_MAX_FILAS - 1 : self::LISTA_MAX_FILAS;
$visibles = array_slice($items, $pagina * $porPagina, $porPagina);
$rows = [];
foreach (array_slice($items, 0, 10) as $item) {
foreach ($visibles as $item) {
$id = (string)($item[$valueField] ?? '');
if ($id === '') continue;
$rows[] = ['id' => $id, 'title' => mb_substr((string)($item[$labelField] ?? $id), 0, 24)];
}
$restantes = count($items) - (($pagina + 1) * $porPagina);
if ($hayMas && $restantes > 0) {
$rows[] = [
'id' => '__mas',
'title' => ' Ver más',
'description' => "Faltan {$restantes}",
];
}
$sections = [['title' => mb_substr($flow['section_title'] ?? 'Opciones', 0, 24), 'rows' => $rows]];
}
@@ -963,6 +982,14 @@ class NormalBot
return self::capShowConfirmOrSubmit($cap, $meta, $context, $company, $ctxId);
}
// ── 1b. "Ver más": avanza la página y vuelve a mostrar la misma lista ─
if ($value === '__mas') {
$cap['__page'][$field['key']] = (int)($cap['__page'][$field['key']] ?? 0) + 1;
$meta['__cap'] = $cap;
ConversationContext::updateMetadata($ctxId, $meta);
return self::capAskNext($cap, $context['from'], $company, $ctxId);
}
// ── 2. User chose "Otra opción" — ask for manual text ────────────────
if ($value === '__cap_other') {
$cap['__awaiting_other'] = true;
@@ -1172,6 +1199,7 @@ class NormalBot
'body' => $field['prompt'] ?? ('Selecciona ' . ($field['label'] ?? $field['key'])),
'button' => 'Ver opciones',
'section_title' => $field['label'] ?? $field['key'],
'__page' => (int)($cap['__page'][$field['key']] ?? 0),
], $to, $company);
}
}
+35
View File
@@ -371,6 +371,41 @@ check('cancelar sigue cancelando', $rutaFe($esperandoFecha, '__foreach_cance
check('con fecha ya elegida no interfiere', $rutaFe($conFecha, 'submenu_ciclos'), 'menus');
check('un foreach sin fecha tampoco', $rutaFe(['ask_date' => false], 'submenu_ciclos'), 'menus');
echo "\nPaginado de listas largas\n";
/** Espejo del paginado de buildDynamicListResponse() */
$paginar = function (array $items, int $pagina, int $max = 10): array {
$hayMas = count($items) > $max;
$porPagina = $hayMas ? $max - 1 : $max;
$filas = array_slice($items, $pagina * $porPagina, $porPagina);
$restantes = count($items) - (($pagina + 1) * $porPagina);
if ($hayMas && $restantes > 0) $filas[] = '__mas';
return $filas;
};
// El caso real: 17 novedades de ausentismo, de las que 7 eran inalcanzables
$novedades = array_map(fn($n) => "N{$n}", range(1, 17));
$vistas = [];
$pagina = 0;
do {
$filas = $paginar($novedades, $pagina);
$hayMas = in_array('__mas', $filas, true);
foreach ($filas as $f) if ($f !== '__mas') $vistas[] = $f;
$pagina++;
} while ($hayMas && $pagina < 20);
check('recorriendo las páginas se llega a todas', count($vistas), 17);
check('ninguna se repite', count(array_unique($vistas)), 17);
check('ninguna se pierde', array_diff($novedades, $vistas), []);
check('termina, no cicla', $pagina <= 3, true);
check('la última página no ofrece "ver más"',
in_array('__mas', $paginar($novedades, 1), true), false);
// Con 10 o menos no debe aparecer el botón
check('10 opciones entran sin paginar',
$paginar(array_slice($novedades, 0, 10), 0), array_slice($novedades, 0, 10));
echo "\nask_finca\n";
$af = $flows['ask_finca'];
check('no repregunta si ya hay finca', $af['skip_if_set'] ?? false, true);