refactor: el alcance de fincas lo resuelve el ERP, no el bot

Los catalogos ahora mandan ?wa= igual que los informes, y el endpoint fincas
devuelve solo las asignadas a ese numero. El "Todas las fincas" se antepone
unicamente a quien no tiene restriccion: con dos de cinco asignadas no
significa nada claro.

Con eso sale sobrando todo el filtrado del lado del bot —scope_from,
alcanceDelNumero y fincas_json en PhoneSync— y queda una sola fuente de verdad,
del lado donde estan los datos.

Efecto util: con una sola finca asignada el catalogo trae un unico item y
handleDynamicList auto-selecciona, asi que el usuario nunca ve la pregunta.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-05 23:59:35 -05:00
co-authored by Claude Sonnet 4.6
parent 478ba3ea73
commit 94d4dcd26a
4 changed files with 34 additions and 64 deletions
+13 -39
View File
@@ -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)) {