From 478ba3ea7354d5ad6796e7f72b2b8f8a14171d13 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 5 Aug 2026 23:53:34 -0500 Subject: [PATCH] feat: cada numero ve solo las fincas que el ERP le asigno PhoneSync guarda el alcance que llega en whatsapp_numeros, y ask_finca acota la lista con scope_from. Lista vacia = sin restriccion, para que quien tiene todas vea tambien las fincas que se creen despues. "Todas las fincas" sobrevive al filtro y pasa a significar "todas las mias"; de acotarla se encarga el ERP, que es donde estan los datos. Tolera que la columna todavia no exista: mientras tanto nadie queda restringido. Co-Authored-By: Claude Sonnet 4.6 --- services/NormalBot.php | 34 ++++++++++++++++++++++++++++++++++ services/PhoneSync.php | 9 +++++++-- setup/seed_palmas.php | 2 ++ setup/test_navegacion.php | 23 +++++++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/services/NormalBot.php b/services/NormalBot.php index 46c9d80..76dc72c 100644 --- a/services/NormalBot.php +++ b/services/NormalBot.php @@ -448,6 +448,26 @@ class NormalBot } } + /** + * Alcance sincronizado desde el ERP para este número. Vacío = sin restricción. + * Hoy solo hay fincas; el parámetro deja lugar a otros alcances sin tocar esto. + */ + private static function alcanceDelNumero(array $company, string $waNumber, string $alcance): array + { + if ($waNumber === '' || $alcance !== 'fincas') return []; + try { + $stmt = db()->prepare( + "SELECT fincas_json FROM company_phones WHERE company_id = ? AND wa_number = ? LIMIT 1" + ); + $stmt->execute([(int)$company['id'], $waNumber]); + $json = $stmt->fetchColumn(); + } catch (\PDOException $e) { + return []; // columna aún sin crear: nadie queda restringido + } + if (!$json) return []; + return array_map('strval', json_decode((string)$json, true) ?: []); + } + /** * Guarda la opción elegida con su etiqueta, para poder mostrarle al usuario * en qué finca/grupo está parado sin tener que volver a consultar la API. @@ -672,6 +692,20 @@ class NormalBot $meta = ConversationContext::getMetadata($ctxId); $items = self::fetchDynamicList($flow['source_endpoint_key'] ?? '', $company, $meta); + // scope_from acota la lista a lo que este número tiene asignado en el ERP. + // Sin restricción se deja pasar todo. El id 0 ("todas") sobrevive siempre: + // con alcance activo pasa a significar "todas las mías", y el ERP lo acota. + $alcance = $flow['scope_from'] ?? ''; + if ($alcance !== '' && is_array($items)) { + $permitidas = self::alcanceDelNumero($company, $context['from'] ?? '', $alcance); + if ($permitidas) { + $vf = $flow['value_field'] ?? 'id'; + $items = array_values(array_filter($items, fn($i) => + (string)($i[$vf] ?? '') === '0' || in_array((string)($i[$vf] ?? ''), $permitidas, true) + )); + } + } + // exclude_values descarta opciones que no son ítems reales, como el // "🌐 Todas las fincas" (id 0) que el catálogo antepone para los informes. $excluir = array_map('strval', $flow['exclude_values'] ?? []); diff --git a/services/PhoneSync.php b/services/PhoneSync.php index e4b2831..b205cc9 100644 --- a/services/PhoneSync.php +++ b/services/PhoneSync.php @@ -71,11 +71,12 @@ class PhoneSync $db = db(); $upsert = $db->prepare(" - INSERT INTO company_phones (company_id, wa_number, label, permission_type, is_active) - VALUES (?, ?, ?, ?, 1) + INSERT INTO company_phones (company_id, wa_number, label, permission_type, fincas_json, is_active) + VALUES (?, ?, ?, ?, ?, 1) ON DUPLICATE KEY UPDATE label = VALUES(label), permission_type = VALUES(permission_type), + fincas_json = VALUES(fincas_json), is_active = 1 "); @@ -84,11 +85,15 @@ class PhoneSync $waNumber = trim($n['wa_number'] ?? ''); if ($waNumber === '') continue; + // Lista vacía = sin restricción (ve todas las fincas) + $fincas = array_values(array_filter(array_map('intval', (array)($n['fincas'] ?? [])))); + $upsert->execute([ $companyId, $waNumber, trim($n['nombre'] ?? ''), (int)($n['permiso'] ?? 1), + $fincas ? json_encode($fincas) : null, ]); $activeNumbers[] = $waNumber; } diff --git a/setup/seed_palmas.php b/setup/seed_palmas.php index e0f7bd3..bb3ee14 100644 --- a/setup/seed_palmas.php +++ b/setup/seed_palmas.php @@ -253,6 +253,8 @@ $configJson = [ 'meta_group' => 'finca', 'meta_key' => 'finca_id', 'entity_key' => 'finca', + // Solo las fincas que el ERP le asigno a este numero + 'scope_from' => 'fincas', 'next_node' => 'show_main_menu', 'skip_if_set' => true, 'nlu_description' => 'Seleccionar o cambiar la finca de trabajo', diff --git a/setup/test_navegacion.php b/setup/test_navegacion.php index 607754c..b6e9bd5 100644 --- a/setup/test_navegacion.php +++ b/setup/test_navegacion.php @@ -509,6 +509,29 @@ foreach (['show_main_menu', 'descargar_informes', 'enviar_informacion'] as $k) { check('el bot ya no reordena por su cuenta', isset(flowsDe($config, '3')['registrar_ausentismo']['fields'][1]['frecuentes']), false); +echo "\nAlcance de fincas por número\n"; + +check('ask_finca respeta el alcance del número', + $flows['ask_finca']['scope_from'] ?? null, 'fincas'); + +/** Espejo del filtro de handleDynamicList */ +$acotar = function (array $items, array $permitidas): array { + if (!$permitidas) return array_column($items, 'id'); + return array_column(array_values(array_filter($items, + fn($i) => $i['id'] === '0' || in_array($i['id'], $permitidas, true))), 'id'); +}; + +$catalogo = [['id' => '0'], ['id' => '4'], ['id' => '7'], ['id' => '9']]; + +check('sin restricción ve todas', $acotar($catalogo, []), ['0', '4', '7', '9']); +check('con dos fincas ve solo esas', $acotar($catalogo, ['4', '9']), ['0', '4', '9']); +check('"todas" sobrevive al filtro', in_array('0', $acotar($catalogo, ['4']), true), true); + +// Una sola finca: handleDynamicList auto-selecciona con un unico item, pero el +// "todas" cuenta como segundo, asi que igual pregunta. Es correcto: elegir entre +// "su finca" y "todas las suyas" son cosas distintas si mañana le suman otra. +check('una sola finca deja dos opciones', $acotar($catalogo, ['7']), ['0', '7']); + echo "\nask_finca\n"; $af = $flows['ask_finca']; check('no repregunta si ya hay finca', $af['skip_if_set'] ?? false, true);