From 8399a86972b6cf1e68ad7d1004444e6bc8b9faa9 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 19 Aug 2026 20:32:42 -0500 Subject: [PATCH] feat: labores diarias fase 2; el bot repite lo que responde el ERP Quinto flujo de carga: fecha, labor, lote, trabajadores y cantidad. El catalogo de novedades lo filtra el ERP por fase, asi que el bot no decide cual corresponde. El trabajador vinculado se pre-llena y el supervisor elige. Ademas, collect_and_post descartaba la respuesta del POST y decia "listo" aunque el ERP hubiera rechazado parte. Ahora repite lo que contesta: cuantos entraron, quien ya tenia jornal, que quedo afuera. Y from_phone respeta el tipo del campo: multi_select espera un arreglo aunque el perfil traiga un solo tercero. Co-Authored-By: Claude Sonnet 4.6 --- services/NormalBot.php | 27 +++++++++++++--- setup/seed_palmas.php | 66 +++++++++++++++++++++++++++++++++++++++ setup/test_navegacion.php | 18 +++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) diff --git a/services/NormalBot.php b/services/NormalBot.php index 75cbd86..55d4547 100644 --- a/services/NormalBot.php +++ b/services/NormalBot.php @@ -1072,9 +1072,11 @@ class NormalBot if (empty($perfil['es_supervisor'])) { foreach ($fields as $f) { $origen = $f['from_phone'] ?? ''; - if ($origen !== '' && !empty($perfil[$origen])) { - $collected[$f['key']] = (string)$perfil[$origen]; - } + if ($origen === '' || empty($perfil[$origen])) continue; + // multi_select espera un arreglo aunque el perfil traiga uno solo + $collected[$f['key']] = ($f['type'] ?? '') === 'multi_select' + ? [(string)$perfil[$origen]] + : (string)$perfil[$origen]; } } @@ -1600,7 +1602,24 @@ class NormalBot return self::sendText('⚠️ Error al guardar los datos. Intenta de nuevo.' . $nav, $context['from'], $company); } - return self::sendText($cap['success_text'] . $nav, $context['from'], $company); + // El ERP suele responder algo que el usuario necesita saber: cuántos + // lotes entraron, quién ya tenía jornal, qué quedó afuera. Sin esto el + // bot decía "listo" sobre registros que no se guardaron. + $texto = $cap['success_text']; + $resp = json_decode((string)$response, true); + if (is_array($resp)) { + $detalle = trim((string)($resp['mensaje'] ?? '')); + if ($detalle !== '' && ($resp['status'] ?? '1') !== '1') { + $texto = '⚠️ ' . $detalle; + } elseif ($detalle !== '') { + $texto .= "\n" . $detalle; + } + if (!empty($resp['rechazados'])) { + $texto .= "\n\n_" . count($resp['rechazados']) . ' sin procesar_'; + } + } + + return self::sendText($texto . $nav, $context['from'], $company); } // ── Submit form (end of multi-step collection) ─────────────────────────── diff --git a/setup/seed_palmas.php b/setup/seed_palmas.php index a45e298..4a6c521 100644 --- a/setup/seed_palmas.php +++ b/setup/seed_palmas.php @@ -250,6 +250,7 @@ $configJson = [ ['id' => 'registrar_pluviometria','modulo' => 'pluviometria', 'title' => '🌧️ Pluviometría', 'description' => 'Milímetros de lluvia por finca'], ['id' => 'registrar_ciclo', 'modulo' => 'ciclos', 'title' => '🔄 Ciclos', 'description' => 'Abrir o cerrar lotes'], ['id' => 'registrar_mantenimiento','modulo' => 'mantenimiento', 'title' => '🔧 Mantenimiento', 'description' => 'Avance sobre lotes pendientes'], + ['id' => 'registrar_labor', 'modulo' => 'labores', 'title' => '💪 Labor diaria', 'description' => 'Tractorista, transporte, horas extra'], ], ], ], @@ -592,6 +593,65 @@ $configJson = [ ], ], + // Labores diarias sin validaciones: tractorista, transporte, horas + // extra. El ERP filtra el catalogo de novedades, el bot no decide cual + // corresponde. A quien ya tiene jornal ese dia se le registra con + // jornal 0 y el ERP lo avisa en la respuesta. + 'registrar_labor' => [ + 'type' => 'collect_and_post', + 'endpoint_key' => 'labores_up', + 'header' => '💪 Labor diaria', + 'confirm' => true, + 'success_text' => '✅ Enviado a Palmas360.', + 'back' => 'enviar_informacion', + 'nlu_description' => 'Registrar una labor diaria: tractorista, transporte, horas extra, labores varias', + 'fields' => [ + [ + 'key' => 'fecha', + 'label' => 'Fecha', + 'type' => 'date_quick', + 'prompt' => '📅 ¿De qué fecha es la labor?', + 'other_validate' => 'date', + 'other_prompt' => 'Escribe la fecha (YYYY-MM-DD):', + ], + [ + 'key' => 'novedad_id', + 'label' => 'Labor', + 'type' => 'select', + 'prompt' => '¿Qué labor se hizo?', + 'source_endpoint_key' => 'novedades_labor_dn', + 'value_field' => 'id', + 'label_field' => 'nombre', + ], + [ + 'key' => 'lote_id', + 'label' => 'Lote', + 'type' => 'select', + 'prompt' => '¿En qué lote?', + 'source_endpoint_key' => 'lotes_finca_dn', + 'value_field' => 'id', + 'label_field' => 'label', + ], + [ + 'key' => 'empleados', + 'label' => 'Trabajadores', + 'type' => 'multi_select', + 'prompt' => '¿Quiénes la hicieron? (puedes marcar varios)', + 'source_endpoint_key' => 'empleados_labor_dn', + 'value_field' => 'id', + 'label_field' => 'nombre', + // El trabajador vinculado se pre-llena; el supervisor elige + 'from_phone' => 'tercero_id', + ], + [ + 'key' => 'cantidad', + 'label' => 'Cantidad', + 'type' => 'text', + 'prompt' => '¿Qué cantidad? (jornales, horas o unidades según la labor)', + ], + ], + ], + // Upload 'enviar_informacion' => ['type' => 'menu', 'menu' => 'submenu_subir_ciclos', 'nlu_skip' => true, 'back' => 'show_main_menu'], @@ -625,6 +685,7 @@ $configJson = [ ['id' => 'registrar_pluviometria','modulo' => 'pluviometria', 'title' => '🌧️ Pluviometría', 'description' => 'Milímetros de lluvia por finca'], ['id' => 'registrar_ciclo', 'modulo' => 'ciclos', 'title' => '🔄 Ciclos', 'description' => 'Abrir o cerrar lotes'], ['id' => 'registrar_mantenimiento','modulo' => 'mantenimiento', 'title' => '🔧 Mantenimiento', 'description' => 'Avance sobre lotes pendientes'], + ['id' => 'registrar_labor', 'modulo' => 'labores', 'title' => '💪 Labor diaria', 'description' => 'Tractorista, transporte, horas extra'], ], ], ], @@ -742,6 +803,11 @@ $endpoints = [ ['key' => 'lotes_cierre_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_abiertos_dn&ciclo={ciclo}&finca_id={finca_id}&todos=1'], ['key' => 'ciclos_up', 'dir' => 'upload', 'url' => $BASE . '?peticion=ciclos_{ciclo}_up'], // Mantenimiento: lotes con cantidad pendiente de asignar, no apertura/cierre + // Labores diarias — fase 2: las que no arrastran inventario ni autorizacion + ['key' => 'novedades_labor_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=novedades_labor_dn&fase=2'], + ['key' => 'lotes_finca_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_x_finca&finca_id={finca_id}'], + ['key' => 'empleados_labor_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=empleados&estado=1&items=200'], + ['key' => 'labores_up', 'dir' => 'upload', 'url' => $BASE . '?peticion=labores_up'], ['key' => 'lotes_mantenimiento_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_mantenimiento_dn&grupo={grupo_id}&finca_id={finca_id}'], ['key' => 'lotes_mantenimiento_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_mantenimiento_dn&grupo={grupo_id}&finca_id={finca_id}&todos=1'], // Todos los lotes (PDF) por ciclo diff --git a/setup/test_navegacion.php b/setup/test_navegacion.php index 34db8dc..0834e67 100644 --- a/setup/test_navegacion.php +++ b/setup/test_navegacion.php @@ -562,6 +562,24 @@ $cic = array_column($flows['registrar_ciclo']['fields'], null, 'key'); check('mantenimiento no está entre los ciclos', in_array('mantenimiento', array_column($cic['ciclo']['options'], 'id'), true), false); +echo "\nLabores diarias (POST)\n"; + +$lab = $flows['registrar_labor'] ?? []; +$lf = array_column($lab['fields'] ?? [], null, 'key'); + +check('pide fecha, labor, lote, trabajadores y cantidad', + array_keys($lf), ['fecha', 'novedad_id', 'lote_id', 'empleados', 'cantidad']); +check('el catálogo lo filtra el ERP por fase', + $lf['novedad_id']['source_endpoint_key'] ?? null, 'novedades_labor_dn'); +check('los trabajadores se eligen de a varios', + $lf['empleados']['type'] ?? null, 'multi_select'); +check('y el vinculado se pre-llena', + $lf['empleados']['from_phone'] ?? null, 'tercero_id'); +check('postea a labores_up', $lab['endpoint_key'] ?? null, 'labores_up'); +foreach (['novedades_labor_dn', 'lotes_finca_dn', 'empleados_labor_dn', 'labores_up'] as $k) { + check("endpoint {$k} registrado", isset($eps[$k]), true); +} + echo "\nAlcance de fincas por número\n"; /**