diff --git a/services/NormalBot.php b/services/NormalBot.php index 76dc72c..281d3df 100644 --- a/services/NormalBot.php +++ b/services/NormalBot.php @@ -307,7 +307,7 @@ class NormalBot if (!empty($meta['__nlu_entities']) && !empty($flow['source_endpoint_key_all'])) { $srcKey = $flow['source_endpoint_key_all']; } - $items = self::fetchDynamicList($srcKey, $company, $meta); + $items = self::fetchDynamicList($srcKey, $company, $meta, $context['from'] ?? ''); if ($items === null || count($items) === 0) { ConversationContext::reset($ctxId); @@ -448,26 +448,6 @@ 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. @@ -527,7 +507,7 @@ class NormalBot return $url; } - private static function fetchDynamicList(string $endpointKey, array $company, array $meta = []): ?array + private static function fetchDynamicList(string $endpointKey, array $company, array $meta = [], string $waNumber = ''): ?array { if ($endpointKey === '') return null; @@ -540,6 +520,13 @@ class NormalBot $apiKey = $company['api_key'] ?? ''; $url = self::buildUrl(self::substituteUrlVars($ep['url'], $meta), $company); + + // Quién consulta: el ERP acota el catálogo a lo que ese número tiene + // asignado. Mismo criterio que los informes. + if ($waNumber !== '') { + $url .= (str_contains($url, '?') ? '&' : '?') . 'wa=' . urlencode($waNumber); + } + $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, @@ -690,21 +677,7 @@ class NormalBot private static function handleCollectForEach(array $flow, array $context, array $company, int $ctxId): ?array { $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) - )); - } - } + $items = self::fetchDynamicList($flow['source_endpoint_key'] ?? '', $company, $meta, $context['from'] ?? ''); // exclude_values descarta opciones que no son ítems reales, como el // "🌐 Todas las fincas" (id 0) que el catálogo antepone para los informes. @@ -1328,7 +1301,8 @@ class NormalBot $items = self::fetchDynamicList( self::resolverPorCampos($field['source_endpoint_key'] ?? '', $cap['collected'] ?? []), $company, - ['__ep_vars_combined' => $cap['collected'] ?? []] + ['__ep_vars_combined' => $cap['collected'] ?? []], + $to ); if (empty($items)) { return self::sendText( @@ -1358,7 +1332,7 @@ class NormalBot if ($type === 'select') { $vf = $field['value_field'] ?? 'id'; $lf = $field['label_field'] ?? 'label'; - $items = self::fetchDynamicList($field['source_endpoint_key'] ?? '', $company, ['__ep_vars_combined' => $cap['collected'] ?? []]); + $items = self::fetchDynamicList($field['source_endpoint_key'] ?? '', $company, ['__ep_vars_combined' => $cap['collected'] ?? []], $to); if (!empty($items)) { diff --git a/services/PhoneSync.php b/services/PhoneSync.php index b205cc9..e4b2831 100644 --- a/services/PhoneSync.php +++ b/services/PhoneSync.php @@ -71,12 +71,11 @@ class PhoneSync $db = db(); $upsert = $db->prepare(" - INSERT INTO company_phones (company_id, wa_number, label, permission_type, fincas_json, is_active) - VALUES (?, ?, ?, ?, ?, 1) + INSERT INTO company_phones (company_id, wa_number, label, permission_type, is_active) + VALUES (?, ?, ?, ?, 1) ON DUPLICATE KEY UPDATE label = VALUES(label), permission_type = VALUES(permission_type), - fincas_json = VALUES(fincas_json), is_active = 1 "); @@ -85,15 +84,11 @@ 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 bb3ee14..e0f7bd3 100644 --- a/setup/seed_palmas.php +++ b/setup/seed_palmas.php @@ -253,8 +253,6 @@ $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 b6e9bd5..d901f48 100644 --- a/setup/test_navegacion.php +++ b/setup/test_navegacion.php @@ -511,26 +511,29 @@ check('el bot ya no reordena por su cuenta', 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'); +/** + * Espejo del endpoint fincas: acota al alcance del numero y antepone "Todas" + * solo a quien no tiene restriccion. Con dos de cinco asignadas, "todas" no + * significa nada claro, asi que no se ofrece. + */ +$catalogoFincas = function (array $activas, array $alcance): array { + if ($alcance) { + $activas = array_values(array_filter($activas, fn($f) => in_array($f['id'], $alcance, true))); + } + $lista = array_column($activas, 'id'); + if (!$alcance) array_unshift($lista, '0'); + return $lista; }; -$catalogo = [['id' => '0'], ['id' => '4'], ['id' => '7'], ['id' => '9']]; +$activas = [['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); +check('sin restricción ve todas y el "Todas"', $catalogoFincas($activas, []), ['0', '4', '7', '9']); +check('con dos asignadas ve solo esas', $catalogoFincas($activas, ['4', '9']), ['4', '9']); +check('y sin el "Todas"', in_array('0', $catalogoFincas($activas, ['4', '9']), true), false); -// 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']); +// Una sola finca: un unico item, y handleDynamicList auto-selecciona en ese caso +check('con una sola no hay nada que preguntar', $catalogoFincas($activas, ['7']), ['7']); +check('el bot ya no filtra por su cuenta', isset($flows['ask_finca']['scope_from']), false); echo "\nask_finca\n"; $af = $flows['ask_finca'];