feat: flujo de registro de ausentismo (Fase 1, carga de datos)

Primer flujo de POST. El bot recolecta trabajador, motivo y rango de fechas, y
postea a ausentismos_up; Palmas360 decide si auto-aprueba o encola para
revision, como ya hace bot_entrada.

El campo lookup asumia que el endpoint devolvia un solo objeto, pero /empleados
devuelve una lista paginada: con eso el trabajador salia como "Desconocido" y
sin id. Ahora distingue lista de objeto, y si hay varias coincidencias las
ofrece para elegir en vez de quedarse con la primera.

Campos opcionales del contrato (eps, diagnostico_id) quedan fuera hasta
confirmar con Palmas360 que novedades son incapacidad.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-04 19:37:16 -05:00
co-authored by Claude Sonnet 4.6
parent 0bb1d3447e
commit 0e72aa3d5a
3 changed files with 160 additions and 5 deletions
+55 -3
View File
@@ -898,8 +898,23 @@ class NormalBot
return self::sendText("*{$cap['header']}*\n\n{$prompt}", $context['from'], $company);
}
// ── 3a. Ya se mostraron varias coincidencias: el usuario eligió una ──
if (!empty($cap['__lookup_options']) && isset($cap['__lookup_options'][$value])) {
$cap['collected'][$field['key']] = $value;
$cap['collected_labels'][$field['key']] = $cap['__lookup_options'][$value];
unset($cap['__lookup_options']);
$cap['index'] = $idx + 1;
while ($cap['index'] < count($fields) && self::capShouldSkip($fields[$cap['index']], $cap['collected'])) { $cap['index']++; }
$meta['__cap'] = $cap;
ConversationContext::updateMetadata($ctxId, $meta);
return $cap['index'] < count($fields)
? self::capAskNext($cap, $context['from'], $company, $ctxId)
: self::capShowConfirmOrSubmit($cap, $meta, $context, $company, $ctxId);
}
// ── 3. Lookup field — search entity by entered value (e.g. cédula) ───
if (($field['type'] ?? 'text') === 'lookup') {
unset($cap['__lookup_options']);
$result = self::callLookupEndpoint(
$field['lookup_endpoint_key'] ?? '',
$value,
@@ -907,13 +922,38 @@ class NormalBot
$company
);
if ($result === null) {
$vf = $field['value_field'] ?? 'id';
$df = $field['display_field'] ?? 'nombre';
// Catálogos como /empleados devuelven una lista; /diagnostico_buscar, un objeto.
$esLista = is_array($result) && array_is_list($result);
if ($esLista) {
$result = self::acotarCoincidencias($result, $vf, $df);
if (count($result) > 1) {
$opciones = [];
foreach ($result as $r) $opciones[(string)($r[$vf] ?? '')] = (string)($r[$df] ?? '');
$cap['__lookup_options'] = $opciones;
$meta['__cap'] = $cap;
ConversationContext::updateMetadata($ctxId, $meta);
return self::buildDynamicListResponse($result, [
'value_field' => $vf,
'label_field' => $df,
'header' => $cap['header'],
'body' => 'Encontré varias coincidencias. ¿Cuál es?',
'button' => 'Ver coincidencias',
'section_title' => $field['label'] ?? 'Resultados',
], $context['from'], $company);
}
$result = $result[0] ?? null;
}
if (!$result) {
$notFound = $field['not_found_text'] ?? '⚠️ No encontré resultados. Intenta de nuevo:';
return self::sendText("*{$cap['header']}*\n\n{$notFound}", $context['from'], $company);
}
$displayName = (string)($result[$field['display_field'] ?? 'nombre'] ?? 'Desconocido');
$entityId = (string)($result[$field['value_field'] ?? 'id'] ?? '');
$displayName = (string)($result[$df] ?? 'Desconocido');
$entityId = (string)($result[$vf] ?? '');
$cap['__lookup_pending'] = ['id' => $entityId, 'label' => $displayName];
$meta['__cap'] = $cap;
@@ -971,6 +1011,18 @@ class NormalBot
return self::submitCapPost($meta, $context, $company, $ctxId);
}
/**
* Una lista de WhatsApp admite pocas filas, y con 40 homónimos elegir no es
* viable. Se recortan a 10 y se descartan los que no traigan id o etiqueta.
*/
private static function acotarCoincidencias(array $items, string $valueField, string $labelField): array
{
$validos = array_values(array_filter($items, fn($r) =>
is_array($r) && ($r[$valueField] ?? '') !== '' && ($r[$labelField] ?? '') !== ''
));
return array_slice($validos, 0, 10);
}
private static function callLookupEndpoint(string $epKey, string $value, string $param, array $company): ?array
{
if ($epKey === '' || $value === '') return null;