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:
co-authored by
Claude Sonnet 4.6
parent
eb0ae1f208
commit
478ba3ea73
@@ -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
|
* 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.
|
* en qué finca/grupo está parado sin tener que volver a consultar la API.
|
||||||
@@ -672,6 +692,20 @@ class NormalBot
|
|||||||
$meta = ConversationContext::getMetadata($ctxId);
|
$meta = ConversationContext::getMetadata($ctxId);
|
||||||
$items = self::fetchDynamicList($flow['source_endpoint_key'] ?? '', $company, $meta);
|
$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
|
// exclude_values descarta opciones que no son ítems reales, como el
|
||||||
// "🌐 Todas las fincas" (id 0) que el catálogo antepone para los informes.
|
// "🌐 Todas las fincas" (id 0) que el catálogo antepone para los informes.
|
||||||
$excluir = array_map('strval', $flow['exclude_values'] ?? []);
|
$excluir = array_map('strval', $flow['exclude_values'] ?? []);
|
||||||
|
|||||||
@@ -71,11 +71,12 @@ class PhoneSync
|
|||||||
|
|
||||||
$db = db();
|
$db = db();
|
||||||
$upsert = $db->prepare("
|
$upsert = $db->prepare("
|
||||||
INSERT INTO company_phones (company_id, wa_number, label, permission_type, is_active)
|
INSERT INTO company_phones (company_id, wa_number, label, permission_type, fincas_json, is_active)
|
||||||
VALUES (?, ?, ?, ?, 1)
|
VALUES (?, ?, ?, ?, ?, 1)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
label = VALUES(label),
|
label = VALUES(label),
|
||||||
permission_type = VALUES(permission_type),
|
permission_type = VALUES(permission_type),
|
||||||
|
fincas_json = VALUES(fincas_json),
|
||||||
is_active = 1
|
is_active = 1
|
||||||
");
|
");
|
||||||
|
|
||||||
@@ -84,11 +85,15 @@ class PhoneSync
|
|||||||
$waNumber = trim($n['wa_number'] ?? '');
|
$waNumber = trim($n['wa_number'] ?? '');
|
||||||
if ($waNumber === '') continue;
|
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([
|
$upsert->execute([
|
||||||
$companyId,
|
$companyId,
|
||||||
$waNumber,
|
$waNumber,
|
||||||
trim($n['nombre'] ?? ''),
|
trim($n['nombre'] ?? ''),
|
||||||
(int)($n['permiso'] ?? 1),
|
(int)($n['permiso'] ?? 1),
|
||||||
|
$fincas ? json_encode($fincas) : null,
|
||||||
]);
|
]);
|
||||||
$activeNumbers[] = $waNumber;
|
$activeNumbers[] = $waNumber;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,6 +253,8 @@ $configJson = [
|
|||||||
'meta_group' => 'finca',
|
'meta_group' => 'finca',
|
||||||
'meta_key' => 'finca_id',
|
'meta_key' => 'finca_id',
|
||||||
'entity_key' => 'finca',
|
'entity_key' => 'finca',
|
||||||
|
// Solo las fincas que el ERP le asigno a este numero
|
||||||
|
'scope_from' => 'fincas',
|
||||||
'next_node' => 'show_main_menu',
|
'next_node' => 'show_main_menu',
|
||||||
'skip_if_set' => true,
|
'skip_if_set' => true,
|
||||||
'nlu_description' => 'Seleccionar o cambiar la finca de trabajo',
|
'nlu_description' => 'Seleccionar o cambiar la finca de trabajo',
|
||||||
|
|||||||
@@ -509,6 +509,29 @@ foreach (['show_main_menu', 'descargar_informes', 'enviar_informacion'] as $k) {
|
|||||||
check('el bot ya no reordena por su cuenta',
|
check('el bot ya no reordena por su cuenta',
|
||||||
isset(flowsDe($config, '3')['registrar_ausentismo']['fields'][1]['frecuentes']), false);
|
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";
|
echo "\nask_finca\n";
|
||||||
$af = $flows['ask_finca'];
|
$af = $flows['ask_finca'];
|
||||||
check('no repregunta si ya hay finca', $af['skip_if_set'] ?? false, true);
|
check('no repregunta si ya hay finca', $af['skip_if_set'] ?? false, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user