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 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-05 23:53:34 -05:00
co-authored by Claude Sonnet 4.6
parent eb0ae1f208
commit 478ba3ea73
4 changed files with 66 additions and 2 deletions
+34
View File
@@ -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'] ?? []);
+7 -2
View File
@@ -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;
}