'INT NULL', 'modulos_json' => 'TEXT NULL', 'es_supervisor' => 'TINYINT(1) NOT NULL DEFAULT 0', ] 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 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'], ['id' => 'registrar_labor_validada','modulo' => 'labores', 'title' => '🌾 Cosecha y otros','description' => 'Cosecha, polinización, fertilización'], ], ], ], ], ], // ── 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.', ], ], ], // Mantenimiento no abre ni cierra ciclos —no tiene tabla de ciclos—: // se registra avance sobre lotes que ya tienen cantidad pendiente. 'registrar_mantenimiento' => [ 'type' => 'collect_and_post', 'endpoint_key' => 'labores_up', 'header' => '🔧 Mantenimiento', 'confirm' => true, 'success_text' => '✅ Enviado a Palmas360.', 'back' => 'enviar_informacion', 'requires' => ['grupo_mant' => 'grupo_id'], 'resolver' => 'ciclo_mantenimiento', 'nlu_description' => 'Registrar avance de mantenimiento: plateo, corona, poda', 'fields' => [ [ 'key' => 'fecha', 'label' => 'Fecha', 'type' => 'date_quick', 'prompt' => '📅 ¿De qué fecha?', 'other_validate' => 'date', 'other_prompt' => 'Escribe la fecha (YYYY-MM-DD):', ], [ 'key' => 'lote_id', 'label' => 'Lote', 'type' => 'select', 'prompt' => 'Elige el lote (entre paréntesis va lo que falta):', 'source_endpoint_key' => 'lotes_mantenimiento_dn', 'value_field' => 'id', 'label_field' => 'label', 'empty_text' => '⚠️ No hay lotes con cantidad pendiente para ese grupo.', ], [ 'key' => 'cantidad', 'label' => 'Cantidad', 'type' => 'text', 'prompt' => '¿Qué cantidad avanzaste?', ], ], ], // 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)', ], ], ], // Fase 3: labores con validacion previa. Un solo flujo con campos // condicionales en vez de tres: cosecha no lleva producto, y el pase // es solo de polinizacion. El ERP valida inventario y autorizaciones, // y si rechaza algo lo dice en la respuesta. 'registrar_labor_validada' => [ 'type' => 'collect_and_post', 'endpoint_key' => 'labores_up', 'header' => '🌾 Cosecha, polinización y fertilización', 'confirm' => true, 'success_text' => '✅ Enviado a Palmas360.', 'back' => 'enviar_informacion', 'nlu_description' => 'Registrar labor de cosecha, polinización o fertilización', 'fields' => [ [ 'key' => 'tipo', 'label' => 'Tipo', 'type' => 'static_select', 'prompt' => '¿Qué vas a registrar?', 'options' => [ ['id' => 'cosecha', 'label' => '🌾 Cosecha'], ['id' => 'polinizacion', 'label' => '🌸 Polinización'], ['id' => 'fertilizacion','label' => '🧪 Fertilización'], ], ], [ 'key' => 'novedad_id', 'label' => 'Labor', 'type' => 'select', 'prompt' => '¿Qué labor se hizo?', 'source_endpoint_key' => 'novedades_labor3_dn', 'value_field' => 'id', 'label_field' => 'nombre', ], [ 'key' => 'fecha', 'label' => 'Fecha', 'type' => 'date_quick', 'prompt' => '📅 ¿De qué fecha?', 'other_validate' => 'date', 'other_prompt' => 'Escribe la fecha (YYYY-MM-DD):', ], [ 'key' => 'lote_id', 'label' => 'Lote', 'type' => 'select', 'prompt' => '¿En qué lote?', 'source_endpoint_key' => 'lotes_finca_dn', 'value_field' => 'id', 'label_field' => 'label', ], [ // Cosecha no lleva producto. La dosis sale de la // fertilizacion autorizada para ese lote, por eso se // pregunta despues del lote y no antes. 'key' => 'producto_id', 'label' => 'Producto', 'type' => 'select', 'prompt' => '¿Qué producto se aplicó?', 'source_endpoint_key' => 'productos_fertilizacion_dn', 'value_field' => 'id', 'label_field' => 'nombre', 'skip_if' => ['field' => 'tipo', 'not_in' => ['polinizacion', 'fertilizacion']], 'empty_text' => '⚠️ Ese lote no tiene fertilización autorizada pendiente.', ], [ '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', 'from_phone' => 'tercero_id', ], [ 'key' => 'cantidad', 'label' => 'Cantidad', 'type' => 'text', 'prompt' => '¿Qué cantidad?', ], [ 'key' => 'fase_polinizacion', 'label' => 'Pase', 'type' => 'text', 'prompt' => '¿Qué pase de polinización?', 'skip_if' => ['field' => 'tipo', 'equals' => 'polinizacion'], ], ], ], // 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 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'], ['id' => 'registrar_labor_validada','modulo' => 'labores', 'title' => '🌾 Cosecha y otros','description' => 'Cosecha, polinización, fertilización'], ], ], ], ], ], '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= 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'], // 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'], // Fase 3: las que arrastran inventario o autorizacion previa ['key' => 'novedades_labor3_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=novedades_labor_dn&fase=3'], ['key' => 'productos_fertilizacion_dn', 'dir' => 'download', 'url' => $BASE . '?peticion=productos_fertilizacion_dn&lote_id={lote_id}'], ['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 ['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";