feat: editar desde el resumen sin rehacer el formulario

Cancelar obligaba a empezar de cero por una fecha mal puesta. El resumen suma
un tercer boton —WhatsApp admite tres y usaba dos—: Editar lista los campos ya
cargados con su valor, se elige uno, se corrige y se vuelve derecho al resumen.

Los campos que alimentan a otros declaran 'invalida' en el seed: cambiar el
tipo de ausentismo borra el motivo y lo vuelve a preguntar, acotado al tipo
nuevo. Sin eso quedaba un motivo de Incapacidad dentro de un Permiso y el ERP
recibia datos incoherentes sin que nadie lo notara.

El motor engancha en un solo lugar, capAskNext: mientras queden campos sin
valor los pregunta en cadena, y cuando no queda ninguno vuelve al resumen en
vez de seguir el orden del formulario. Aplica a los cinco flujos de carga.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-20 22:59:25 -05:00
co-authored by Claude Sonnet 4.6
parent 6616b63683
commit 52dbd1e203
3 changed files with 138 additions and 0 deletions
+79
View File
@@ -1374,6 +1374,7 @@ class NormalBot
'body' => ['text' => $summary],
'action' => ['buttons' => [
['type' => 'reply', 'reply' => ['id' => '__cap_confirm', 'title' => '✅ Confirmar']],
['type' => 'reply', 'reply' => ['id' => '__cap_edit', 'title' => '✏️ Editar']],
['type' => 'reply', 'reply' => ['id' => '__cap_cancel', 'title' => '❌ Cancelar']],
]],
], $company);
@@ -1422,6 +1423,32 @@ class NormalBot
private static function capAskNext(array $cap, string $to, array $company, int $ctxId = 0): ?array
{
// Editando desde el resumen: se pregunta el primer campo que quedo sin
// valor —el que se eligio, mas los que dependian de el— y al completarse
// todos se vuelve al resumen en vez de seguir el orden del formulario.
if (!empty($cap['__editando'])) {
$pendiente = null;
foreach ($cap['fields'] as $i => $f) {
$k = $f['key'] ?? '';
if ($k === '' || isset($cap['collected'][$k])) continue;
if (self::capShouldSkip($f, $cap['collected'])) continue;
$pendiente = $i;
break;
}
unset($cap['__editando']);
if ($pendiente !== null) {
$cap['__editando'] = true;
$cap['index'] = $pendiente;
}
$m = $ctxId > 0 ? ConversationContext::getMetadata($ctxId) : [];
$m['__cap'] = $cap;
if ($ctxId > 0) ConversationContext::updateMetadata($ctxId, $m);
if ($pendiente === null) {
return self::capShowConfirmOrSubmit($cap, $m, ['from' => $to], $company, $ctxId);
}
}
// Saltar campos ya pre-llenados (no contiguos)
$prefilled = $cap['prefilled'] ?? [];
while ((int)$cap['index'] < count($cap['fields'])) {
@@ -2163,6 +2190,58 @@ class NormalBot
if ($input === '__cap_confirm') {
return self::submitCapPost($meta, $context, $company, $ctxId);
}
// Elegir que corregir sin rehacer todo el formulario
if ($input === '__cap_edit') {
$cap = $meta['__cap'];
$rows = [];
foreach ($cap['fields'] as $f) {
$k = $f['key'] ?? '';
if ($k === '' || !isset($cap['collected'][$k])) continue;
$valor = $cap['collected_labels'][$k] ?? $cap['collected'][$k];
if (is_array($valor)) $valor = implode(', ', $valor);
$rows[] = [
'id' => '__cap_ed:' . $k,
'title' => mb_substr(self::capFieldLabel($cap['fields'], $k), 0, 24),
'description' => mb_substr((string)$valor, 0, 72),
];
}
$rows[] = ['id' => '__cap_ed_volver', 'title' => '↩️ Volver', 'description' => 'Sin cambios'];
return self::enqueueInteractive($context['from'], [
'type' => 'list',
'header' => ['type' => 'text', 'text' => mb_substr($cap['header'], 0, 60)],
'body' => ['text' => '¿Qué quieres corregir?'],
'footer' => ['text' => ''],
'action' => ['button' => 'Ver campos', 'sections' => [[
'title' => 'Campos',
'rows' => array_slice($rows, 0, self::LISTA_MAX_FILAS),
]]],
], $company);
}
if ($input === '__cap_ed_volver') {
return self::capShowConfirmOrSubmit($meta['__cap'], $meta, $context, $company, $ctxId);
}
if (str_starts_with($input, '__cap_ed:')) {
$clave = substr($input, strlen('__cap_ed:'));
$cap = $meta['__cap'];
// Un campo alimenta a otros: cambiar el tipo invalida el motivo,
// cambiar el lote invalida el producto. Se borran y se vuelven a
// preguntar en cadena, si no el ERP recibiria datos incoherentes.
$borrar = [$clave];
foreach ($cap['fields'] as $f) {
if (($f['key'] ?? '') === $clave) {
$borrar = array_merge($borrar, (array)($f['invalida'] ?? []));
}
}
foreach ($borrar as $k) {
unset($cap['collected'][$k], $cap['collected_labels'][$k]);
}
$cap['__editando'] = true;
$meta['__cap'] = $cap;
ConversationContext::updateMetadata($ctxId, $meta);
return self::capAskNext($cap, $context['from'], $company, $ctxId);
}
if ($input === '__cap_cancel') {
$m = ConversationContext::getMetadata($ctxId);
unset($m['__cap']);