Files
Lizandro GuarnizoandClaude Sonnet 4.6 673c15dae3 fix: los lotes para abrir ciclo salian en orden alfabetico
El pedido es mostrar los 10 con los ciclos mas largos —los que toca abrir— y
una opcion para ver el resto. La apertura ofrecia todos los lotes de la finca
por nombre, sin señal de cual estaba mas atrasado.

Ahora apertura y cierre traen 10 ordenados por dias, con los dias en la
etiqueta, y "Otro lote" cambia al listado completo sin perder lo ya marcado.
Si ningun lote tiene historial la consulta cae sola en alfabetico, porque el
DATEDIFF queda nulo y desempata por nombre.

Solo toca el flujo de carga; los informes quedan como estaban.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-11 21:09:51 -05:00

819 lines
50 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
require_once dirname(__DIR__) . '/config/env.php';
require_once dirname(__DIR__) . '/config/db.php';
$db = db();
// ─── Columnas que el perfil por número necesita ───────────────────────────────
// Se crean acá y no con un ALTER a mano porque el seed ya se corre en cada
// despliegue. Es idempotente: si la columna existe, no hace nada.
foreach ([
'tercero_id' => 'INT NULL',
'modulos_json' => 'TEXT NULL',
] as $columna => $tipo) {
$existe = $db->query("SHOW COLUMNS FROM company_phones LIKE '{$columna}'")->fetch();
if (!$existe) {
$db->exec("ALTER TABLE company_phones ADD COLUMN {$columna} {$tipo}");
echo "✔ company_phones.{$columna} creada\n";
}
}
// ─── CONFIG JSON completo (Company 2 y 3 comparten la misma base) ────────────
$configJson = [
'greeting' => '¡Bienvenido a Palmas360! Escribe *menu* para ver las opciones disponibles.',
'fallback' => 'No entendí. Escribe *menu* para ver las opciones disponibles.',
'nlu_enabled' => true,
// ── Bienvenida: una vez por día y por empresa ───────────────────────────
// Tokens: {saludo} {empresa} {finca} {empresas}
// No menciona la finca porque llega antes de elegirla; la finca activa se
// ve en el footer de cada menú.
'welcome' => [
'enabled' => true,
'text' => [
// Cat 1 — solo reporta: sin ejemplos de descarga, que no puede usar
'1' => "{saludo}, bienvenido a {empresa}.\n\n"
. "Desde acá enviás los datos de campo:\n"
. " • _\"registrar un ausentismo\"_\n"
. " • _\"abrir ciclos de cosecha\"_\n"
. " • _\"registrar pluviometría\"_\n\n"
. "Cuando quieras:\n"
. " *menú* ver las opciones · *salir* terminar{empresas}",
'2' => "{saludo}, bienvenido a {empresa}.\n\n"
. "Pedime lo que necesites con tus palabras:\n"
. " • _\"informe de mantenimiento de plateo\"_\n"
. " • _\"quién faltó hoy\"_\n"
. " • _\"producción de este mes\"_\n\n"
. "O navegá con los menús. Cuando quieras:\n"
. " *menú* volver al inicio · *atrás* subir un nivel · *salir* terminar\n"
. " *cambiar finca* para consultar otra finca{empresas}",
'3' => "{saludo}, bienvenido a {empresa}.\n\n"
. "Pedime lo que necesites con tus palabras:\n"
. " • _\"informe de mantenimiento de plateo\"_\n"
. " • _\"quién faltó hoy\"_\n"
. " • _\"registrar un ausentismo\"_\n\n"
. "O navegá con los menús. Cuando quieras:\n"
. " *menú* volver al inicio · *atrás* subir un nivel · *salir* terminar\n"
. " *cambiar finca* para consultar otra finca{empresas}",
],
],
'commands' => [
// Al menú principal
'menu' => 'show_main_menu',
'inicio' => 'show_main_menu',
'salir' => 'show_main_menu',
'cancelar' => 'show_main_menu',
// Un nivel arriba (__back lo resuelve con el mapa 'back' del nodo actual)
'atras' => '__back',
'volver' => '__back',
'regresar' => '__back',
// Atajos
'informes' => 'descargar_informes',
'info' => 'enviar_informacion',
'finca' => 'reset_finca',
'cambiar finca' => 'reset_finca',
],
// ── Menus globales ──────────────────────────────────────────────────────
'menus' => [
// Menú modo trabajo (para per_type=3 tras seleccionar finca)
'menu_modo_trabajo' => [
'type' => 'list',
'header' => '🌴 Palmas360',
'body' => '¿Qué deseas hacer?',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Opciones',
'rows' => [
['id' => 'descargar_informes', 'title' => '📥 Descargar informes', 'description' => 'Ciclos, Producción, Ausentismos'],
['id' => 'enviar_informacion', 'title' => '📤 Enviar información', 'description' => 'Subir ciclos y datos'],
],
],
],
],
// Menú cat2 (descargar informes) — accesible globalmente
'show_menu_cat2' => [
'type' => 'list',
'header' => '📋 Informes',
'body' => '¿Qué informe deseas?',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Informes',
'rows' => [
['id' => 'submenu_produccion', 'modulo' => 'produccion', 'title' => '🌿 Producción', 'description' => 'Informes de producción'],
['id' => 'submenu_ciclos', 'modulo' => 'ciclos', 'title' => '🔄 Ciclos', 'description' => 'Ciclos de campo'],
['id' => 'submenu_ausentismos', 'modulo' => 'ausentismos', 'title' => '👥 Ausentismos', 'description' => 'Faltas y novedades'],
],
],
],
],
// Submenú producción
'submenu_produccion' => [
'type' => 'list',
'header' => '🌿 Producción',
'body' => 'Selecciona el informe:',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Producción',
'rows' => [
['id' => 'produccion_total', 'title' => '📊 Totales facturados', 'description' => 'Resumen y PDF del año'],
['id' => 'produccion_lotes', 'title' => '📋 Por lotes (Excel)', 'description' => 'Detalle por lote'],
],
],
],
],
// Submenú ciclos
'submenu_ciclos' => [
'type' => 'list',
'header' => '🔄 Ciclos',
'body' => 'Selecciona el tipo de ciclo:',
'footer' => 'Palmas360',
'button' => 'Ver ciclos',
'sections' => [
[
'title' => 'Ciclos',
'rows' => [
['id' => 'ciclo_cosecha', 'title' => '🌾 Cosecha', 'description' => 'Ciclos de cosecha'],
['id' => 'ciclo_polinizacion', 'title' => '🌸 Polinización', 'description' => 'Ciclos de polinización'],
['id' => 'submenu_ciclos_sanidad','title' => '🧪 Sanidad', 'description' => 'Plagas, Censo, Palmarum, Tratamientos'],
['id' => 'ciclo_mantenimiento', 'title' => '🔧 Mantenimiento', 'description' => 'Ciclos de mantenimiento'],
],
],
],
],
// Sub-menús por ciclo (3 botones: más largos / todos / histórico)
'submenu_ciclo_cosecha' => ['type'=>'button','body'=>'Ciclos de Cosecha — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_cosecha_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_cosecha_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'ciclo_cosecha_historico','title'=>'📅 Histórico 30d'],
]],
'submenu_ciclo_polinizacion' => ['type'=>'button','body'=>'Ciclos de Polinización — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_polinizacion_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_polinizacion_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'ciclo_polinizacion_historico','title'=>'📅 Histórico 30d'],
]],
'submenu_ciclo_plagas' => ['type'=>'button','body'=>'Ciclos de Plagas — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_plagas_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_plagas_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'ciclo_plagas_historico','title'=>'📅 Histórico 30d'],
]],
'submenu_ciclo_censo' => ['type'=>'button','body'=>'Ciclos de Censo — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_censo_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_censo_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'ciclo_censo_historico','title'=>'📅 Histórico 30d'],
]],
'submenu_ciclo_palm' => ['type'=>'button','body'=>'Ciclos de Palmarum — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_palm_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_palm_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'ciclo_palm_historico','title'=>'📅 Histórico 30d'],
]],
'submenu_ciclo_tratamiento' => ['type'=>'button','body'=>'Ciclos de Tratamientos — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_tratamiento_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_tratamiento_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'ciclo_tratamiento_historico','title'=>'📅 Histórico 30d'],
]],
'submenu_ciclo_mantenimiento' => ['type'=>'button','body'=>'Ciclos de Mantenimiento — ¿Qué deseas ver?','buttons'=>[
['id'=>'ciclo_mantenimiento_top', 'title'=>'🏆 Más largos'],
['id'=>'ciclo_mantenimiento_todos', 'title'=>'📋 Todos los lotes'],
['id'=>'reset_grupo_mant', 'title'=>'🔄 Otro grupo'],
]],
// Submenú sanidad vegetal (orden: Censo, Plagas, Rincofurus, Tratamiento)
'submenu_ciclos_sanidad' => [
'type' => 'list',
'header' => '🧪 Sanidad Vegetal',
'body' => 'Selecciona el ciclo de sanidad:',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Sanidad',
'rows' => [
['id' => 'ciclo_censo', 'title' => '📝 Censo', 'description' => 'Ciclos de censo'],
['id' => 'ciclo_plagas', 'title' => '🐛 Plagas', 'description' => 'Ciclos de plagas'],
['id' => 'ciclo_palm', 'title' => '🌴 Palmarum', 'description' => 'Rhynchophorus Palmarum'],
['id' => 'ciclo_tratamiento', 'title' => '💊 Tratamiento', 'description' => 'Ciclos de tratamientos'],
],
],
],
],
// Submenú ausentismos
'submenu_ausentismos' => [
'type' => 'list',
'header' => '👥 Ausentismos',
'body' => 'Selecciona el período:',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Período',
'rows' => [
['id' => 'ausentismos_hoy', 'title' => '📅 Hoy', 'description' => '¿Quién faltó hoy?'],
['id' => 'ausentismos_semana', 'title' => '📆 Esta semana', 'description' => '¿Quién faltó esta semana?'],
['id' => 'ausentismos_mes', 'title' => '📋 Mes (PDF)', 'description' => 'Reporte mensual PDF'],
],
],
],
],
// Submenú subir ciclos (para per_type=3)
'submenu_subir_ciclos' => [
'type' => 'list',
'header' => '📤 Enviar información',
'body' => 'Selecciona qué deseas enviar:',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Opciones',
'rows' => [
['id' => 'registrar_ausentismo', 'modulo' => 'ausentismos', 'title' => '👥 Ausentismo', 'description' => 'Registrar falta, incapacidad o permiso'],
['id' => 'registrar_pluviometria','modulo' => 'pluviometria', 'title' => '🌧️ Pluviometría', 'description' => 'Milímetros por finca · pronto formulario'],
['id' => 'registrar_ciclo', 'modulo' => 'ciclos', 'title' => '🔄 Ciclos', 'description' => 'Abrir o cerrar lotes · pronto formulario'],
],
],
],
],
],
// ── Flows globales ──────────────────────────────────────────────────────
'flows' => [
// Selección de finca (dynamic_list — guarda finca_id en meta)
'ask_finca' => [
'type' => 'dynamic_list',
'source_endpoint_key' => 'fincas_dn',
'value_field' => 'id',
'label_field' => 'label',
'header' => '🌴 Palmas360',
'body' => '¿En qué finca trabajas?',
'section_title' => 'Fincas disponibles',
'meta_group' => 'finca',
'meta_key' => 'finca_id',
'entity_key' => 'finca',
'next_node' => 'show_main_menu',
'skip_if_set' => true,
'nlu_description' => 'Seleccionar o cambiar la finca de trabajo',
],
// Acción NLU: limpiar finca y volver a ask_finca
'reset_finca' => [
'type' => 'function',
'function' => 'reset_finca',
'nlu_description' => 'Cambiar de finca o seleccionar una finca diferente',
],
// Vuelve a pedir el grupo de mantenimiento (el grupo persiste entre informes)
'reset_grupo_mant' => [
'type' => 'function',
'function' => 'reset_grupo_mant',
'nlu_description' => 'Cambiar el grupo de mantenimiento (plateo, corona, poda, etc.)',
],
// Producción (texto del mes + PDF encadenado)
'produccion_total' => ['type' => 'function', 'function' => 'api_report', 'nlu_description' => 'Producción total facturada del mes actual con PDF del año', 'params' => [
'endpoint_key' => 'produccion_total_bot_dn',
'date_mode' => 'current_month',
'next_endpoint_key' => 'produccion_total_pdf_bot_dn',
'next_caption' => '📊 Producción del año (PDF)',
'next_filename' => 'produccion_año.pdf',
]],
'produccion_lotes' => ['type' => 'function', 'function' => 'api_report', 'nlu_description' => 'Producción detallada por lotes en Excel', 'params' => [
'endpoint_key' => 'produccion_lotes_bot_dn',
'filename' => 'produccion_lotes.xlsx',
'caption' => 'Producción por lotes del año',
]],
// Ausentismos
'ausentismos_hoy' => ['type' => 'function', 'function' => 'api_report', 'nlu_description' => 'Ausentismos de hoy, quién faltó hoy', 'params' => [
'endpoint_key' => 'ausentismos_bot_texto_dn',
'date_mode' => 'today',
]],
'ausentismos_semana' => ['type' => 'function', 'function' => 'api_report', 'nlu_description' => 'Ausentismos de esta semana', 'params' => [
'endpoint_key' => 'ausentismos_bot_texto_dn',
'date_mode' => 'last_7',
]],
'ausentismos_mes' => ['type' => 'function', 'function' => 'api_report', 'nlu_description' => 'Reporte PDF de ausentismos del mes', 'params' => [
'endpoint_key' => 'ausentismos_bot_pdf_dn',
'date_mode' => 'current_month',
'filename' => 'ausentismos_mes.pdf',
'caption' => 'Ausentismos del mes',
]],
// Menus de sección (nlu_skip: navegación interna, no exponer al NLU)
// 'back' = a dónde sube el comando "atrás" desde cada nivel
'submenu_produccion' => ['type' => 'menu', 'menu' => 'submenu_produccion', 'back' => 'show_main_menu', 'nlu_dir' => 'descarga', 'nlu_description' => 'Producción en general: totales facturados o detalle por lotes'],
'submenu_ciclos' => ['type' => 'menu', 'menu' => 'submenu_ciclos', 'back' => 'show_main_menu', 'nlu_dir' => 'descarga', 'nlu_description' => 'Consultar ciclos de campo en general, sin decir cuál'],
'submenu_ciclos_sanidad' => ['type' => 'menu', 'menu' => 'submenu_ciclos_sanidad', 'back' => 'submenu_ciclos', 'nlu_dir' => 'descarga', 'nlu_description' => 'Ciclos de sanidad en general: censo, plagas, palmarum, tratamiento'],
'submenu_ausentismos' => ['type' => 'menu', 'menu' => 'submenu_ausentismos', 'back' => 'show_main_menu', 'nlu_dir' => 'descarga', 'nlu_description' => 'Consultar ausentismos: quién faltó hoy, esta semana o el mes'],
// Ciclos — cada uno abre un sub-menú de 3 opciones
'ciclo_cosecha' => ['type' => 'menu', 'menu' => 'submenu_ciclo_cosecha', 'back' => 'submenu_ciclos', 'nlu_description' => 'Ver ciclos de cosecha (lotes más largos o todos)'],
'ciclo_polinizacion' => ['type' => 'menu', 'menu' => 'submenu_ciclo_polinizacion', 'back' => 'submenu_ciclos', 'nlu_description' => 'Ver ciclos de polinización'],
'ciclo_plagas' => ['type' => 'menu', 'menu' => 'submenu_ciclo_plagas', 'back' => 'submenu_ciclos_sanidad', 'nlu_description' => 'Ver ciclos de plagas'],
'ciclo_censo' => ['type' => 'menu', 'menu' => 'submenu_ciclo_censo', 'back' => 'submenu_ciclos_sanidad', 'nlu_description' => 'Ver ciclos de censo'],
'ciclo_palm' => ['type' => 'menu', 'menu' => 'submenu_ciclo_palm', 'back' => 'submenu_ciclos_sanidad', 'nlu_description' => 'Ver ciclos de Palmarum (Rhynchophorus)'],
'ciclo_tratamiento' => ['type' => 'menu', 'menu' => 'submenu_ciclo_tratamiento', 'back' => 'submenu_ciclos_sanidad', 'nlu_description' => 'Ver ciclos de tratamientos'],
// Sub-opciones: cosecha
'ciclo_cosecha_top' => ['type'=>'function','function'=>'api_report','nlu_description'=>'Lotes de cosecha con ciclos más largos','params'=>['endpoint_key'=>'cosecha_top_texto_bot_dn','date_mode'=>'last_30']],
'ciclo_cosecha_todos' => ['type'=>'function','function'=>'api_report','nlu_description'=>'PDF de ciclos de cosecha todos los lotes','params'=>['endpoint_key'=>'cosecha_todos_dn','date_mode'=>'today','filename'=>'ciclos_cosecha.pdf','caption'=>'Ciclos de Cosecha — todos los lotes']],
'ciclo_cosecha_historico'=> ['type'=>'function','function'=>'api_report','nlu_description'=>'Histórico de cosecha últimos 30 días','params'=>['endpoint_key'=>'cosecha_historico_bot_dn','date_mode'=>'last_30','filename'=>'historico_cosecha.pdf','caption'=>'Histórico Cosecha — últimos 30 días']],
// Sub-opciones: polinizacion
'ciclo_polinizacion_top' => ['type'=>'function','function'=>'api_report','nlu_description'=>'Lotes de polinización con ciclos más largos','params'=>['endpoint_key'=>'polinizacion_top_texto_bot_dn','date_mode'=>'last_30']],
'ciclo_polinizacion_todos' => ['type'=>'function','function'=>'api_report','nlu_description'=>'PDF de ciclos de polinización todos los lotes','params'=>['endpoint_key'=>'polinizacion_todos_dn','date_mode'=>'today','filename'=>'ciclos_polinizacion.pdf','caption'=>'Ciclos de Polinización — todos los lotes']],
'ciclo_polinizacion_historico'=> ['type'=>'function','function'=>'api_report','nlu_description'=>'Histórico de polinización últimos 30 días','params'=>['endpoint_key'=>'polinizacion_historico_bot_dn','date_mode'=>'last_30','filename'=>'historico_polinizacion.pdf','caption'=>'Histórico Polinización — últimos 30 días']],
// Sub-opciones: plagas
'ciclo_plagas_top' => ['type'=>'function','function'=>'api_report','nlu_description'=>'Lotes de plagas con ciclos más largos','params'=>['endpoint_key'=>'plagas_top_texto_bot_dn','date_mode'=>'last_30']],
'ciclo_plagas_todos' => ['type'=>'function','function'=>'api_report','nlu_description'=>'PDF de ciclos de plagas todos los lotes','params'=>['endpoint_key'=>'plagas_todos_dn','date_mode'=>'today','filename'=>'ciclos_plagas.pdf','caption'=>'Ciclos de Plagas — todos los lotes']],
'ciclo_plagas_historico'=> ['type'=>'function','function'=>'api_report','nlu_description'=>'Histórico de plagas últimos 30 días','params'=>['endpoint_key'=>'plagas_historico_bot_dn','date_mode'=>'last_30','filename'=>'historico_plagas.pdf','caption'=>'Histórico Plagas — últimos 30 días']],
// Sub-opciones: censo
'ciclo_censo_top' => ['type'=>'function','function'=>'api_report','nlu_description'=>'Lotes de censo con ciclos más largos','params'=>['endpoint_key'=>'censo_top_texto_bot_dn','date_mode'=>'last_30']],
'ciclo_censo_todos' => ['type'=>'function','function'=>'api_report','nlu_description'=>'PDF de ciclos de censo todos los lotes','params'=>['endpoint_key'=>'censo_todos_dn','date_mode'=>'today','filename'=>'ciclos_censo.pdf','caption'=>'Ciclos de Censo — todos los lotes']],
'ciclo_censo_historico'=> ['type'=>'function','function'=>'api_report','nlu_description'=>'Histórico de censo últimos 30 días','params'=>['endpoint_key'=>'censo_historico_bot_dn','date_mode'=>'last_30','filename'=>'historico_censo.pdf','caption'=>'Histórico Censo — últimos 30 días']],
// Sub-opciones: palm
'ciclo_palm_top' => ['type'=>'function','function'=>'api_report','nlu_description'=>'Lotes de Palmarum con ciclos más largos','params'=>['endpoint_key'=>'palm_top_texto_bot_dn','date_mode'=>'last_30']],
'ciclo_palm_todos' => ['type'=>'function','function'=>'api_report','nlu_description'=>'PDF de ciclos de Palmarum todos los lotes','params'=>['endpoint_key'=>'palm_todos_dn','date_mode'=>'today','filename'=>'ciclos_palmarum.pdf','caption'=>'Ciclos de Palmarum — todos los lotes']],
'ciclo_palm_historico'=> ['type'=>'function','function'=>'api_report','nlu_description'=>'Histórico de Palmarum últimos 30 días','params'=>['endpoint_key'=>'palm_historico_bot_dn','date_mode'=>'last_30','filename'=>'historico_palmarum.pdf','caption'=>'Histórico Palmarum — últimos 30 días']],
// Sub-opciones: tratamiento
'ciclo_tratamiento_top' => ['type'=>'function','function'=>'api_report','nlu_description'=>'Lotes de tratamiento con ciclos más largos','params'=>['endpoint_key'=>'tratamiento_top_texto_bot_dn','date_mode'=>'last_30']],
'ciclo_tratamiento_todos' => ['type'=>'function','function'=>'api_report','nlu_description'=>'PDF de ciclos de tratamientos todos los lotes','params'=>['endpoint_key'=>'tratamiento_todos_dn','date_mode'=>'today','filename'=>'ciclos_tratamientos.pdf','caption'=>'Ciclos de Tratamientos — todos los lotes']],
'ciclo_tratamiento_historico'=> ['type'=>'function','function'=>'api_report','nlu_description'=>'Histórico de tratamientos últimos 30 días','params'=>['endpoint_key'=>'tratamiento_historico_bot_dn','date_mode'=>'last_30','filename'=>'historico_tratamientos.pdf','caption'=>'Histórico Tratamientos — últimos 30 días']],
// ciclo_mantenimiento → selección dinámica de grupo (top3 + "Otro")
'ciclo_mantenimiento' => [
'type' => 'dynamic_list',
'source_endpoint_key' => 'mantenimiento_grupos_top3_dn',
// Con entities del NLU se busca en el catálogo completo: "plateo" puede
// no estar entre los 3 grupos más frecuentes.
'source_endpoint_key_all' => 'mantenimiento_grupos_all_dn',
'back' => 'submenu_ciclos',
'value_field' => 'id',
'label_field' => 'label',
'header' => '🔧 Mantenimiento',
'body' => 'Selecciona el grupo de mantenimiento:',
'section_title' => 'Grupos frecuentes',
'meta_group' => 'grupo_mant',
'meta_key' => 'grupo_id',
'entity_key' => 'grupo',
'next_node' => 'submenu_ciclo_mantenimiento',
'append_options' => [['id' => 'select_grupo_mant_otros', 'title' => ' Otro grupo']],
'nlu_description' => 'Informes de mantenimiento de un grupo (plateo, corona, poda...) — usar cuando NO dice qué informe quiere',
],
'select_grupo_mant_otros' => [
'type' => 'dynamic_list',
'source_endpoint_key' => 'mantenimiento_grupos_otros_dn',
'value_field' => 'id',
'label_field' => 'label',
'header' => '🔧 Mantenimiento',
'body' => 'Selecciona el grupo:',
'section_title' => 'Otros grupos',
'meta_group' => 'grupo_mant',
'meta_key' => 'grupo_id',
'next_node' => 'submenu_ciclo_mantenimiento',
'nlu_skip' => true,
],
// Sub-opciones: mantenimiento.
// requires/resolver: si el grupo aún no está elegido, el bot lo pide primero
// y al resolverlo vuelve a este informe (no al submenú).
'ciclo_mantenimiento_top' => [
'type'=>'function','function'=>'api_report',
'requires'=>['grupo_mant'=>'grupo_id'], 'resolver'=>'ciclo_mantenimiento',
'back'=>'submenu_ciclo_mantenimiento',
'nlu_description'=>'Lotes MÁS ATRASADOS de un grupo de mantenimiento — solo si pide los más largos o atrasados',
'params'=>['endpoint_key'=>'mantenimiento_top_texto_bot_dn','date_mode'=>'today'],
],
'ciclo_mantenimiento_todos' => [
'type'=>'function','function'=>'api_report',
'requires'=>['grupo_mant'=>'grupo_id'], 'resolver'=>'ciclo_mantenimiento',
'back'=>'submenu_ciclo_mantenimiento',
'nlu_description'=>'PDF con TODOS los lotes de un grupo de mantenimiento — solo si pide explícitamente todos',
'params'=>['endpoint_key'=>'mantenimiento_todos_bot_dn','date_mode'=>'today','filename'=>'mantenimiento_todos.pdf','caption'=>'Mantenimiento — todos los lotes'],
],
// ── Carga de datos (POST) — Fase 1 ──────────────────────────────────
// Palmas360 decide si auto-aprueba o encola para revisión; el bot solo
// envía. Campos opcionales del contrato (eps, diagnostico_id) quedan
// fuera hasta confirmar qué novedades son incapacidad.
'registrar_ausentismo' => [
'type' => 'collect_and_post',
'endpoint_key' => 'ausentismos_up',
'header' => '👥 Registrar ausentismo',
'confirm' => true,
'success_text' => '✅ Ausentismo enviado a Palmas360.',
'back' => 'enviar_informacion',
'nlu_description' => 'Registrar un ausentismo, incapacidad o permiso de un trabajador',
'fields' => [
[
'key' => 'empleado_id',
'label' => 'Trabajador',
'type' => 'lookup',
// Numero vinculado a un trabajador: no se le pregunta
'from_phone' => 'tercero_id',
'prompt' => '👤 Escribe el nombre o documento del trabajador:',
'lookup_endpoint_key' => 'empleados_buscar_dn',
'lookup_param' => 'filtro',
'value_field' => 'id',
'display_field' => 'nombre',
'not_found_text' => '⚠️ No encontré ese trabajador. Escribe el nombre de nuevo:',
],
[
'key' => 'novedad_id',
'label' => 'Motivo',
'type' => 'select',
'prompt' => '¿Cuál es el motivo del ausentismo?',
'source_endpoint_key' => 'novedades_ausentismo_dn',
'value_field' => 'id',
'label_field' => 'nombre',
],
[
'key' => 'fecha_inicial',
'label' => 'Desde',
'type' => 'date_quick',
'prompt' => '📅 ¿Desde qué fecha?',
'other_validate' => 'date',
'other_prompt' => 'Escribe la fecha de inicio (YYYY-MM-DD):',
],
[
'key' => 'fecha_final',
'label' => 'Hasta',
'type' => 'date_quick',
'prompt' => '📅 ¿Hasta qué fecha? (si es un solo día, elige la misma)',
'other_validate' => 'date',
'other_prompt' => 'Escribe la fecha final (YYYY-MM-DD):',
'min_field' => 'fecha_inicial',
'min_error' => '⚠️ La fecha final no puede ser anterior a la inicial. Elige otra:',
],
],
],
// Pluviometría: un valor de mm por finca, todas contra la misma fecha.
// exclude_values saca el "🌐 Todas las fincas" (id 0) que el catálogo
// antepone para los informes y que acá no es una finca real.
'registrar_pluviometria' => [
'type' => 'collect_for_each',
'source_endpoint_key' => 'fincas_dn',
'endpoint_key' => 'pluvio_upload',
'value_field' => 'id',
'label_field' => 'label',
'exclude_values' => ['0'],
'ask_date' => true,
'date_question' => '📅 ¿De qué fecha es la lectura?',
'question' => '🌧️ ¿Cuántos milímetros?',
'header' => '🌧️ Pluviometría',
'items_key' => 'fincas',
'success_text' => '✅ Pluviometría enviada a Palmas360.',
'back' => 'enviar_informacion',
'nlu_description' => 'Registrar pluviometría, lluvia o milímetros por finca',
],
// Apertura y cierre de lotes, para los seis tipos de ciclo.
// Mantenimiento no va acá: no tiene tabla de ciclos ni apertura/cierre,
// es una labor. El orden sigue el de la pantalla web.
'registrar_ciclo' => [
'type' => 'collect_and_post',
'endpoint_key' => 'ciclos_up',
'header' => '🔄 Ciclos',
'confirm' => true,
'success_text' => '✅ Enviado a Palmas360.',
'back' => 'enviar_informacion',
'nlu_description' => 'Abrir o cerrar ciclos de lotes (cosecha, sanidad, censo, palmarum, tratamiento, polinización)',
'fields' => [
[
'key' => 'ciclo',
'label' => 'Ciclo',
'type' => 'static_select',
'prompt' => '¿Qué ciclo vas a registrar?',
'options' => [
['id' => 'cosecha', 'label' => '🌾 Cosecha'],
['id' => 'polinizacion', 'label' => '🌸 Polinización'],
['id' => 'sanidad', 'label' => '🐛 Plagas'],
['id' => 'censo', 'label' => '📝 Censo'],
['id' => 'palm', 'label' => '🌴 Palmarum'],
['id' => 'tratamiento', 'label' => '💊 Tratamiento'],
],
],
[
'key' => 'accion',
'label' => 'Acción',
'type' => 'static_select',
'prompt' => '¿Qué vas a hacer?',
'options' => [
['id' => 'apertura', 'label' => '📂 Abrir lotes'],
['id' => 'cierre', 'label' => '📁 Cerrar lotes'],
],
],
[
'key' => 'fecha',
'label' => 'Fecha',
'type' => 'date_quick',
'prompt' => '📅 ¿De qué fecha?',
'other_validate' => 'date',
'other_prompt' => 'Escribe la fecha (YYYY-MM-DD):',
],
[
'key' => 'lote_ids',
'label' => 'Lotes',
'type' => 'multi_select',
'prompt' => 'Elige los lotes (puedes marcar varios):',
// Apertura ofrece los lotes de la finca; cierre, solo los abiertos
'source_endpoint_key' => 'lotes_{accion}_dn',
// "Otro lote" cambia al listado completo
'full_endpoint_key' => 'lotes_{accion}_todos_dn',
'value_field' => 'id',
'label_field' => 'label',
'empty_text' => '⚠️ No hay lotes disponibles para esta combinación.',
],
],
],
// Upload
'enviar_informacion' => ['type' => 'menu', 'menu' => 'submenu_subir_ciclos', 'nlu_skip' => true, 'back' => 'show_main_menu'],
// Alias globales (nlu_skip: son alias de navegación)
'show_main_menu' => ['type' => 'menu', 'menu' => 'show_menu_cat2', 'nlu_skip' => true],
'descargar_informes' => ['type' => 'menu', 'menu' => 'show_menu_cat2', 'nlu_skip' => true, 'back' => 'show_main_menu'],
],
// ── Per-type config ─────────────────────────────────────────────────────
'per_type' => [
// Cat 1: solo reporta (envía info, no descarga)
'1' => [
'greeting_flow' => 'show_menu_cat1',
'fallback_flow' => 'show_menu_cat1',
'greeting' => null,
'fallback' => null,
'commands' => ['menu' => 'show_menu_cat1'],
'menus' => [
'show_menu_cat1' => [
'type' => 'list',
'header' => '🌴 Palmas360',
'body' => '¿Qué deseas hacer?',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Opciones',
'rows' => [
['id' => 'registrar_ausentismo', 'modulo' => 'ausentismos', 'title' => '👥 Ausentismo', 'description' => 'Registrar falta, incapacidad o permiso'],
['id' => 'registrar_pluviometria','modulo' => 'pluviometria', 'title' => '🌧️ Pluviometría', 'description' => 'Milímetros por finca · pronto formulario'],
['id' => 'registrar_ciclo', 'modulo' => 'ciclos', 'title' => '🔄 Ciclos', 'description' => 'Abrir o cerrar lotes · pronto formulario'],
],
],
],
],
],
'flows' => [
'show_menu_cat1' => ['type' => 'menu', 'menu' => 'show_menu_cat1'],
'show_main_menu' => ['type' => 'menu', 'menu' => 'show_menu_cat1'],
],
],
// Cat 2: solo descarga
'2' => [
'greeting_flow' => 'ask_finca',
'fallback_flow' => 'show_menu_cat2',
'greeting' => '',
'fallback' => null,
'commands' => ['menu' => 'show_menu_cat2'],
'menus' => [
'show_menu_cat2' => [
'type' => 'list',
'header' => '📋 Informes',
'body' => '¿Qué informe deseas?',
'footer' => 'Palmas360',
'button' => 'Ver opciones',
'sections' => [
[
'title' => 'Informes',
'rows' => [
['id' => 'submenu_produccion', 'modulo' => 'produccion', 'title' => '🌿 Producción', 'description' => 'Informes de producción'],
['id' => 'submenu_ciclos', 'modulo' => 'ciclos', 'title' => '🔄 Ciclos', 'description' => 'Ciclos de campo'],
['id' => 'submenu_ausentismos', 'modulo' => 'ausentismos', 'title' => '👥 Ausentismos', 'description' => 'Faltas y novedades'],
],
],
],
],
],
'flows' => [
'show_menu_cat2' => ['type' => 'menu', 'menu' => 'show_menu_cat2'],
'show_main_menu' => ['type' => 'menu', 'menu' => 'show_menu_cat2'],
'descargar_informes' => ['type' => 'menu', 'menu' => 'show_menu_cat2', 'back' => 'show_main_menu'],
],
],
// Cat 3: descarga + envío
'3' => [
'greeting_flow' => 'ask_finca',
'fallback_flow' => 'show_main_menu',
'greeting' => '',
'fallback' => null,
'commands' => ['menu' => 'show_main_menu'],
'menus' => [
// Solo cat 3 puede consultar Y registrar, asi que "ausentismo" a
// secas es ambiguo unicamente para el. Cat 1 y cat 2 lo resuelve
// el filtro de permisos: cada uno ve una sola opcion.
'menu_ausentismo_que' => ['type' => 'button', 'body' => '¿Qué necesitas con los ausentismos?', 'buttons' => [
['id' => 'submenu_ausentismos', 'title' => '📊 Consultar'],
['id' => 'registrar_ausentismo', 'title' => '📝 Registrar'],
]],
],
'flows' => [
'ausentismo' => [
'type' => 'menu',
'menu' => 'menu_ausentismo_que',
'back' => 'show_main_menu',
'nlu_description' => 'Ausentismos cuando no dice si quiere consultarlos o registrar uno',
],
'show_main_menu' => ['type' => 'menu', 'menu' => 'menu_modo_trabajo'],
'descargar_informes' => ['type' => 'menu', 'menu' => 'show_menu_cat2', 'back' => 'show_main_menu'],
'enviar_informacion' => ['type' => 'menu', 'menu' => 'submenu_subir_ciclos', 'back' => 'show_main_menu'],
],
],
],
];
// ─── Endpoints ─────────────────────────────────────────────────────────────────
$BASE = 'https://app.palmas360.com/rosablanca/php/controller/controller_api_externa.php';
$endpoints = [
['key' => 'produccion_total_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=produccion_total_bot&finca_id={finca_id}'],
['key' => 'produccion_total_pdf_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=produccion_total_pdf_bot&finca_id={finca_id}'],
['key' => 'produccion_lotes_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=produccion_lotes_bot&finca_id={finca_id}'],
['key' => 'ausentismos_bot_texto_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ausentismos_bot_texto'],
['key' => 'ausentismos_bot_pdf_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ausentismos_bot_pdf'],
['key' => 'cosecha_top_texto_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=cosecha_top_texto_bot&finca_id={finca_id}'],
['key' => 'polinizacion_top_texto_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=polinizacion_top_texto_bot&finca_id={finca_id}'],
['key' => 'censo_top_texto_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=censo_top_texto_bot&finca_id={finca_id}'],
['key' => 'plagas_top_texto_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=plagas_top_texto_bot&finca_id={finca_id}'],
['key' => 'palm_top_texto_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=palm_top_texto_bot&finca_id={finca_id}'],
['key' => 'tratamiento_top_texto_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=tratamiento_top_texto_bot&finca_id={finca_id}'],
['key' => 'cosecha_historico_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=cosecha_historico_bot&finca_id={finca_id}'],
['key' => 'polinizacion_historico_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=polinizacion_historico_bot&finca_id={finca_id}'],
['key' => 'censo_historico_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=censo_historico_bot&finca_id={finca_id}'],
['key' => 'plagas_historico_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=plagas_historico_bot&finca_id={finca_id}'],
['key' => 'palm_historico_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=palm_historico_bot&finca_id={finca_id}'],
['key' => 'tratamiento_historico_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=tratamiento_historico_bot&finca_id={finca_id}'],
['key' => 'fincas_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=fincas'],
['key' => 'numeros_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=whatsapp_numeros&estado=1'],
// ── Carga de datos (POST) — Fase 1: Ausentismo ──────────────────────────
// El lookup agrega &filtro=<texto> a esta URL; estado=1 restringe a activos.
['key' => 'empleados_buscar_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=empleados&estado=1&items=10'],
['key' => 'novedades_ausentismo_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=novedades_ausentismo_dn'],
['key' => 'ausentismos_up', 'dir' => 'upload', 'url' => $BASE . '?peticion=ausentismos_up'],
['key' => 'pluvio_upload', 'dir' => 'upload', 'url' => $BASE . '?peticion=pluvio_upload'],
// ── Carga de ciclos ─────────────────────────────────────────────────────
// {ciclo} y {accion} se resuelven con lo que el usuario ya eligió, así los
// seis tipos y las dos acciones comparten estas tres entradas.
// Los 10 mas atrasados; con todos=1, el listado completo para "Otro lote"
['key' => 'lotes_apertura_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_apertura_dn&ciclo={ciclo}&finca_id={finca_id}'],
['key' => 'lotes_cierre_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_abiertos_dn&ciclo={ciclo}&finca_id={finca_id}'],
['key' => 'lotes_apertura_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=lotes_apertura_dn&ciclo={ciclo}&finca_id={finca_id}&todos=1'],
['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'],
// Todos los lotes (PDF) por ciclo
['key' => 'cosecha_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ciclos_cosecha_dn&finca_id={finca_id}'],
['key' => 'polinizacion_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ciclos_polinizacion_dn&finca_id={finca_id}'],
['key' => 'plagas_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ciclos_plagas_dn&finca_id={finca_id}'],
['key' => 'censo_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ciclos_censo_dn&finca_id={finca_id}'],
['key' => 'palm_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ciclos_palm_dn&finca_id={finca_id}'],
['key' => 'tratamiento_todos_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=ciclos_tratamientos_dn&finca_id={finca_id}'],
['key' => 'mantenimiento_grupos_top3_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=grupos_mantenimiento&limit=3'],
['key' => 'mantenimiento_grupos_otros_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=grupos_mantenimiento&limit=10&offset=3'],
['key' => 'mantenimiento_grupos_all_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=grupos_mantenimiento&limit=200'],
['key' => 'mantenimiento_top_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=mantenimiento_dn&top_lotes=10&grupo={grupo_id}&finca_id={finca_id}'],
['key' => 'mantenimiento_top_texto_bot_dn','dir' => 'download', 'url' => $BASE . '?peticion=mantenimiento_top_texto_bot&grupo={grupo_id}&finca_id={finca_id}'],
['key' => 'mantenimiento_todos_bot_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=mantenimiento_dn&grupo={grupo_id}&finca_id={finca_id}'],
];
// ─── Companies ─────────────────────────────────────────────────────────────────
$companies = [
[
'id' => 2,
'name' => 'rosablanca',
'display_name' => 'Rosa Blanca',
'phone_number_id' => '1168511569675820',
'display_phone' => '',
'api_base_url' => 'https://app.palmas360.com/rosablanca', // ERP, no el Graph API (WhatsAppSender lo tiene hardcodeado)
'api_key' => 'EAAqbNQQekwUBRm8ZAAfCvXZAsxcM1BTkiHjVJYeOfXYCg5t4qYtBL7ap2IdHGa1iXclZBO47gZBshYZBAlQRuUvgSib2Owh7gPnAWfNlHXZAT61ccElXoihuXuEuy0qLiB95tDhLMWPqoKYuDwAHCU78RQxoi1nzz1OsOY40EK7U2tKZBVuZABQZCJctZBrLK7rRiC7AZDZD',
'bot_type' => 'normal',
'requires_approval' => 0,
'is_active' => 1,
],
[
'id' => 3,
'name' => 'palmas360_test',
'display_name' => 'Palmas360 Test',
'phone_number_id' => '1168511569675820',
'display_phone' => '',
'api_base_url' => 'https://app.palmas360.com/rosablanca', // ERP, no el Graph API (WhatsAppSender lo tiene hardcodeado)
'api_key' => 'EAAqbNQQekwUBRm8ZAAfCvXZAsxcM1BTkiHjVJYeOfXYCg5t4qYtBL7ap2IdHGa1iXclZBO47gZBshYZBAlQRuUvgSib2Owh7gPnAWfNlHXZAT61ccElXoihuXuEuy0qLiB95tDhLMWPqoKYuDwAHCU78RQxoi1nzz1OsOY40EK7U2tKZBVuZABQZCJctZBrLK7rRiC7AZDZD',
'bot_type' => 'normal',
'requires_approval' => 0,
'is_active' => 1,
],
];
// ─── Insert / update companies ────────────────────────────────────────────────
/**
* El seed solo es dueño de la estructura del bot (menús, flujos, comandos...).
* Todo lo demás que viva en config_json —modelos de IA, temperatura, prompt,
* whisper— lo configura el admin y debe sobrevivir a cada corrida.
*/
function configPreservando(PDO $db, int $companyId, array $configSeed): string
{
$stmt = $db->prepare("SELECT config_json FROM companies WHERE id = ? LIMIT 1");
$stmt->execute([$companyId]);
$actual = json_decode((string)$stmt->fetchColumn(), true);
if (is_array($actual)) {
$conservadas = array_diff_key($actual, $configSeed);
if ($conservadas) {
$configSeed = array_merge($configSeed, $conservadas);
echo " ↳ conservadas: " . implode(', ', array_keys($conservadas)) . "\n";
}
}
return json_encode($configSeed, JSON_UNESCAPED_UNICODE);
}
$stmtCo = $db->prepare("
INSERT INTO companies (id, name, display_name, phone_number_id, display_phone, api_base_url, api_key, bot_type, requires_approval, is_active, config_json)
VALUES (:id, :name, :display_name, :phone_number_id, :display_phone, :api_base_url, :api_key, :bot_type, :requires_approval, :is_active, :config_json)
ON DUPLICATE KEY UPDATE
display_name = VALUES(display_name),
phone_number_id = VALUES(phone_number_id),
api_base_url = VALUES(api_base_url),
bot_type = VALUES(bot_type),
is_active = VALUES(is_active),
config_json = VALUES(config_json)
");
foreach ($companies as $co) {
echo "✔ Company {$co['id']} ({$co['display_name']})\n";
$cfg = configPreservando($db, (int)$co['id'], $configJson);
$stmtCo->execute([
':id' => $co['id'],
':name' => $co['name'],
':display_name' => $co['display_name'],
':phone_number_id' => $co['phone_number_id'],
':display_phone' => $co['display_phone'],
':api_base_url' => $co['api_base_url'],
':api_key' => $co['api_key'],
':bot_type' => $co['bot_type'],
':requires_approval'=> $co['requires_approval'],
':is_active' => $co['is_active'],
':config_json' => $cfg,
]);
}
// ─── Insert / update endpoints ────────────────────────────────────────────────
$stmtEp = $db->prepare("
INSERT INTO company_endpoints (company_id, endpoint_key, direction, url)
VALUES (:company_id, :endpoint_key, :direction, :url)
ON DUPLICATE KEY UPDATE url = VALUES(url), direction = VALUES(direction)
");
foreach ($companies as $co) {
foreach ($endpoints as $ep) {
$stmtEp->execute([
':company_id' => $co['id'],
':endpoint_key' => $ep['key'],
':direction' => $ep['dir'],
':url' => $ep['url'],
]);
}
echo "✔ " . count($endpoints) . " endpoints OK para company {$co['id']}\n";
}
echo "\n✅ Seed completo.\n";