Commit Graph
30 Commits
Author SHA1 Message Date
Lizandro GuarnizoandClaude Sonnet 4.6 9509c3b4fc feat: add skip_if support to collect_and_post flow fields
Allows fields to declare a skip_if condition so the bot skips them
based on previously collected values — used for eps/diagnostico fields
that only apply when novedad is an incapacidad type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 20:07:44 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 497743ceb1 feat: NLU fallback — route unrecognized text to AI in hybrid+NLU mode
NormalBot.process() gains suppressFallback param. When true, steps 7-8
(greeting menu fallback, static fallback text) return null instead of
responding, letting BotRouter hand off to NLU/AI.

BotRouter.runHybridBot() passes suppressFallback=$nluEnabled so that:
- NLU on: unrecognized text → NLU → route to flow or AI chat
- NLU off: existing fallback behavior unchanged
- Commands, active flows, first-session greeting: always unaffected

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 09:09:49 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 5883801989 fix: prepend api_base_url to relative endpoint URLs in NormalBot
After migration stripped base URLs from DB records, bot was calling
relative paths (e.g. /ruta) as-is without the base. buildUrl() helper
now prepends api_base_url for all four curl call sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 16:25:10 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 34322ae3dc Change "Reporte enviado" to "Reporte generado"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:10:09 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 97a48d0fe4 Validate dynamic_list input and fix cosecha button IDs
- dynamic_list now stores valid IDs in collecting state; rejects
  free-text input that doesn't match a list option
- Fixed submenu_ciclo_cosecha button IDs to match real flow names
  (ciclo_cosecha_dia / ciclo_cosecha_historico)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:07:52 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0ce766a9eb feat: kilos por lotes con selección de período (mes actual/anterior/personalizado)
- Add last_month date_mode to executeApiReport
- prod_kilos_lotes now shows period menu instead of immediately asking dates
- lotes_mes_actual / lotes_mes_anterior use api_report with date_mode
- lotes_personalizado collects fecha_desde + fecha_hasta (2 messages only when needed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 19:35:12 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b288b07ff5 feat: welcome message, fix per_type save, add missing ciclos flows
- Show welcome_msg (button with "Ver menú") for all new sessions before category menu
- Fix saveConfig() AJAX to include per_type_menu DOM values so they survive individual saves
- Add welcome_menu field to General tab in admin (render + save)
- Add 'atras' command alias (→ show_main_menu)
- Add ciclos_plagas, ciclos_palm, ciclos_tratamientos flows to DB config
- Fix per_type in DB from [] to {} (proper JSON object)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 18:39:04 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e9bbf8efce feat: per-type greeting_menu takes priority over global show_main_menu
When user says "menu/inicio/volver", bot now shows the menu configured
for their category (per_type.greeting_menu) instead of the global
show_main_menu. Same redirect applies to interactive button taps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 16:54:47 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 6bac0ee823 fix: dynamic list body fallback uses ?: to handle empty string
WhatsApp rejects list messages with empty body.text. Using ?? missed
empty string from config; ?: correctly falls back to default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 16:32:58 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 322aa287dc fix: fetchDynamicList accepts 'datos' key (Palmas360 API convention)
API returns {status,datos:[...]} but bot was looking for {data:[...]},
causing empty rows and dynamic lists never rendering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 16:19:25 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 ff9ff67445 feat: URL var config per endpoint + smart date handling in api_report
- Admin: each endpoint with {vars} shows a vars config section; per-var
  mode is "ask user" (bot prompts in WhatsApp) or "fixed" (preset: today,
  month_start, month_end, last_7, last_30, year_start, or literal)
- Bot: resolves fixed vars before calling endpoint; queues "ask" vars and
  collects them sequentially from the user before executing the report
- date_mode now uses fecha_desde/fecha_hasta (ERP expected names); added
  current_month mode
- JSON responses from ERP (sin_datos etc.) shown as text, not uploaded as file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 13:36:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 16a03b546d fix: commands clear in-progress state + per_type config rebuilt in DB
Two fixes:
1. Any command (menu, salir, etc.) now clears collecting/__foreach/__cap
   meta before executing, so navigation commands are always a clean reset
   regardless of what flow the user was in.

2. DB per_type was empty ([]); rebuilt with correct structure:
   - Type 1: greeting=submenu_subir, menu/inicio/volver → recibir_info
   - Type 2: greeting=submenu_informes, menu/inicio/volver → descargar_informes
   - Type 3: greeting=show_main_menu, inherits global commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:42:56 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b2abece097 fix: per-type commands extend globals instead of replacing them
Changed commands merge from ?? (replace) to array_merge (extend) so that
per-type overrides for menu/inicio/volver are applied without losing the
global salir, exit, informes, info commands.

Also updated DB config per_type:
- Type 1 (subir info): menu/inicio/volver → recibir_info → submenu_subir
- Type 2 (descargar): menu/inicio/volver → descargar_informes → submenu_informes
- Type 3 (admin): menu → show_main_menu (inherits global)

Each category now lands on its own menu from the first message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:25:15 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 21d30a0895 feat: auto-detect collect_and_post fields from endpoint body_fields
Instead of defining fields inline per flow, the admin now defines them
once on the endpoint (direction=upload). The bot reads body_fields from
company_endpoints at runtime, so the same endpoint can be reused by
multiple collect_and_post flows without duplicating field config.

DB: ALTER TABLE company_endpoints ADD COLUMN body_fields TEXT DEFAULT NULL

Changes:
- NormalBot: falls back to endpoint.body_fields when flow.fields is empty
- DashboardController: endpoint rows show a body_fields section for
  upload endpoints (key / label / prompt per field, add/remove rows)
- companyEndpointSave: reads + validates body_fields JSON and persists it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:10:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 3c7ea9fdf9 feat: collect_and_post flow type — sequential field collection + POST
New flow type that asks the user for a configured list of fields one
by one, shows a summary with confirm/cancel, then POSTs all answers
as JSON to the configured endpoint with Bearer auth.

Config example:
  {
    "type": "collect_and_post",
    "endpoint_key": "subir_cosecha",
    "header": "🌿 Ciclos Cosecha",
    "confirm": true,
    "success_text": " Ciclo registrado.",
    "fields": [
      {"key":"fecha",    "label":"Fecha",    "prompt":"📅 ¿Fecha del ciclo? (YYYY-MM-DD)"},
      {"key":"cantidad", "label":"Racimos",  "prompt":"🔢 ¿Cantidad de racimos?"},
      {"key":"finca",    "label":"Finca",    "prompt":"🏡 ¿Nombre de la finca?"}
    ]
  }

POST payload: {fecha, cantidad, finca, telefono, nombre}

Also adds the UI panel in bot-config with dynamic add/remove field rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:06:42 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b45f0fbe51 feat: add collect_for_each flow type for sequential per-item data entry
New flow type that fetches a dynamic list from an endpoint then asks
the user for a value for each item one by one. On completion shows a
summary with confirm/cancel buttons, then POSTs all collected data as
a JSON array to a configured upload endpoint with Bearer + X-API-Key auth.

Usage in flow config:
  {
    "type": "collect_for_each",
    "source_endpoint_key": "fincas_list",
    "endpoint_key": "pluvio_upload",
    "value_field": "id",
    "label_field": "name",
    "question": "¿Cuál es el valor de pluviometría? (mm)",
    "header": "🌧 Pluviometría",
    "success_text": " Pluviometría registrada."
  }

POST payload: [{id, label, valor}, ...]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 10:36:44 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 2c0ac04993 fix(flows): CSS display conflict, addFlowCard, endpoint CRUD, menu re-send, URL vars
- Fix display:none vs display:flex conflict in PHP flow card rendering (sections were always visible)
- Add addFlowCard() JS function that properly inserts new flow cards into the PHP form
- Fix flowTabChange() to use block vs flex display correctly per section type
- Rewrite endpoints tab as full CRUD table (add/edit/delete) with name, direction, active toggle
- Add companyEndpointDelete() handler and route
- Update companyEndpointSave() to handle ep_id, name, params, is_active fields
- Add company_endpoints columns: name, params
- Add saveEpRow/testEpRow/deleteEpRow/addNewEp JS functions
- NormalBot: set __greeted sentinel after showing greeting menu to prevent re-send on every message
- NormalBot: add substituteUrlVars() to replace {param} tokens in endpoint URLs with collected metadata
- DB: submenu_ciclos_sanidad (4 items) → list; prod_kilos_finca endpoint linked; all >3-button menus → list
- DB: fincas_list endpoint activated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:04:05 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 316d9ce73e feat: dynamic lists, multi-step forms, improved flow editor
NormalBot new flow types:
- collect_input: asks user a question, saves answer to metadata
- dynamic_list: calls endpoint to get options, displays WhatsApp list,
  saves selection to metadata; max 10 rows
- submit_form: reads accumulated metadata fields and sends to api_report
Commands always take priority (escape from any collecting state).
Interactive selections during collecting state are caught before the
static menu lookup.

Admin flow editor redesign:
- All fields use select dropdowns (menus, endpoints, next-node)
- Grouped options: Respuesta / Navegar / Acción directa / Formulario paso a paso
- collect_input, dynamic_list, submit_form panels with contextual fields
- addNewFlow() opens blank modal; saveFlow() handles all types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 13:51:33 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 b6a85c33f7 fix: salir button works, cap at 3 buttons, no AI error on unmatched input
- Add 'goodbye' function: resets context + sends farewell with menu hint
- Add safety slice to 3 buttons in buildMenuResponse (WhatsApp hard limit)
- In hybrid mode: when greeting_menu is configured for the user's category,
  use it as fallback instead of escalating to AI — prevents "Lo siento,
  tengo problemas..." from appearing when nothing matches
- Expose buildGreetingMenu() as public for BotRouter access
- DB: fixed submenu_informes and submenu_ciclos from 4→3 buttons,
  added salir flow (goodbye function) and salir/exit commands (company 2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 13:10:08 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 32e2a2a82e feat: per-category greeting menu in bot config
Add a "Por Categoría" tab in the bot config UI where each permission
category (1, 2, 3) can be assigned its own greeting button menu.
When a user writes without an active context, the bot shows the menu
configured for their category instead of falling through to the global
greeting text.

NormalBot.process() now checks per_type[X].greeting_menu first, which
directly references a menu key and serves as both greeting and fallback
for that category. Fully backward-compatible with existing greeting_flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 13:04:29 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 0b851208ea fix: show navigation hint after api_report completes or fails
After delivering a report (or on any error), send a text message telling
the user to write "menu" or "salir" so they are never left without guidance.
Also return the hint as a proper response on success (was returning null).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 11:43:57 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 dee459d53f fix: reset context on api_report failure so user is never stuck
On any api_report failure (missing endpoint, curl error, HTTP error,
upload failure), reset the bot_context so the next message goes through
the normal flow instead of retrying the failed report indefinitely.
Also reduce curl timeout from 60s to 15s to avoid WhatsApp webhook
timeouts that cause Meta to retry the same message repeatedly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 11:40:23 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 639c3e7938 feat: convert all WhatsApp menus from list to button type
- NormalBot: fix type check 'buttons' -> 'button' in buildMenuResponse
- DashboardController: botConfig editor now shows buttons editor for button-type menus,
  list fields (header/footer/button trigger) hidden for button type; menuTypeChange/addBtnRow
  JS functions toggle the UI; addMenu() defaults to button type
- DashboardController: renderMenuNode and renderFlowNode now read buttons[] array for
  button-type menus instead of only sections[].rows
- DashboardController: editMenu modal shows buttons editor or sections editor based on type;
  editMenuTypeToggle/addEditBtnRow; saveMenu reads buttons for button type
- public/index.php: botConfigSave handler parses menu_btn_id/menu_btn_title for button menus;
  saves compact {type,body,buttons} structure; list menus still save {type,header,...,sections}
- DB: migrated all list menus to button type for both companies (show_main_menu,
  submenu_informes, submenu_ciclos, show_menu_cat1, show_menu_cat2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 19:27:37 -05:00
Lizandro GuarnizoandClaude Sonnet 4.6 e4841e2741 fix: resolveMenu usa menus fusionados de per_type + bugs de $endpoint y tipo de retorno
- resolveMenu ahora recibe los menus ya fusionados (global + per_type) para
  que flujos que referencian menús de otro per_type se resuelvan correctamente
- handleFlow y processInteractive propagan el array de menus fusionados
- Corrige variable indefinida $endpoint → $endpointKey en 3 logs de executeApiReport
- buildMenuResponse cambia tipo de retorno a ?array para evitar TypeError en PHP 8
- BD: flow global show_main_menu apunta a menú show_main_menu (completo) en lugar
  de show_menu_cat2 (solo existía en per_type[2]), fix para usuarios permission_type=3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 19:09:24 -05:00
Lizandro Guarnizo 932a7a78a7 fix: fallback_flow por categoria, menu directo en saludo/fallback 2026-06-27 18:53:41 -05:00
Lizandro Guarnizo 02108b7ffa fix: reset nodo tras saludo + fallback por categoria 3 2026-06-27 18:51:30 -05:00
Lizandro Guarnizo b96dbcc394 feat: menus por permission_type (1=info, 2=informes, 3=completo) 2026-06-27 18:46:35 -05:00
Lizandro Guarnizo 010613846c fix: api_report busca URL en company_endpoints por endpoint_key
- executeApiReport ahora consulta company_endpoints para obtener la URL
- Soporta metodos GET/POST segun configuracion
- Date_mode: today/last_30 para fechas dinámicas
2026-06-27 11:38:25 -05:00
Lizandro Guarnizo 51c187e47d feat: menu navegable con submenus y descarga de reportes via api_report
- NormalBot: nueva funcion api_report para descargar PDF/Excel del ERP y enviarlo por WhatsApp
- NormalBot: resolveMenu() para referenciar menus por nombre en los flujos
- WhatsAppSender: uploadMedia() y sendDocument() para envio de documentos
- BotRouter: guarda respuestas en conversations para visibilidad en el chat
- Script apply-menus.php con estructura completa de menus y endpoints
2026-06-27 11:30:08 -05:00
Lizandro Guarnizo bc422e0a1c cambios importantes 2026-06-12 12:18:21 -05:00