[FEAT] Ruolo consulente: visibilità firm-scoped (org-switcher + Cruscotto Studio)

Un utente con role='consultant' e consulting_firm_id vede solo i CLIENTI del
proprio studio (organizations.consulting_firm_id = suo firm) UNION le proprie
membership dirette — non tutte le org (quello resta super_admin), non solo le
membership. Applicato in OrganizationController::list (selettore azienda) e
ConsultantController::portfolio (cruscotto). Additivo: super_admin e altri ruoli
invariati. Verificato: utente-test consultant/firm1 vede 2 aziende (cliente studio
996003 + membership 996001), non tutte. version 1.25.11.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-07-29 15:53:54 +02:00
co-authored by Claude Opus 4.8
parent a8516efafc
commit faadb62da6
3 changed files with 26 additions and 2 deletions
@@ -19,12 +19,24 @@ class ConsultantController extends BaseController
$user = $this->getCurrentUser() ?? [];
$uid = (int) $this->getCurrentUserId();
// Aziende visibili all'utente (stessa regola di OrganizationController::list).
// Aziende visibili all'utente (stessa regola di OrganizationController::list):
// super_admin = tutte; consulente = clienti del proprio studio (consulting_firm_id)
// UNION le proprie membership; altri = solo le proprie membership.
$firmId = (int) ($user['consulting_firm_id'] ?? 0);
if (($user['role'] ?? '') === 'super_admin') {
$orgs = Database::fetchAll(
"SELECT id, name, entity_type, voluntary_compliance, sector
FROM organizations WHERE is_active = 1 ORDER BY name LIMIT 200"
);
} elseif (($user['role'] ?? '') === 'consultant' && $firmId > 0) {
$orgs = Database::fetchAll(
"SELECT DISTINCT o.id, o.name, o.entity_type, o.voluntary_compliance, o.sector
FROM organizations o
LEFT JOIN user_organizations uo ON uo.organization_id = o.id AND uo.user_id = ?
WHERE o.is_active = 1 AND (o.consulting_firm_id = ? OR uo.user_id IS NOT NULL)
ORDER BY o.name LIMIT 200",
[$uid, $firmId]
);
} else {
$orgs = Database::fetchAll(
"SELECT o.id, o.name, o.entity_type, o.voluntary_compliance, o.sector
@@ -124,8 +124,20 @@ class OrganizationController extends BaseController
{
$this->requireAuth();
$firmId = (int) ($this->currentUser['consulting_firm_id'] ?? 0);
if ($this->currentUser['role'] === 'super_admin') {
$orgs = Database::fetchAll('SELECT * FROM organizations WHERE is_active = 1 ORDER BY name');
} elseif ($this->currentUser['role'] === 'consultant' && $firmId > 0) {
// Consulente: vede TUTTI i clienti del proprio studio (organizations.consulting_firm_id
// = il suo firm) UNION le org di cui è membro diretto. Non l'intero DB (quello è super_admin).
$orgs = Database::fetchAll(
'SELECT DISTINCT o.*, uo.role AS user_role, uo.is_primary
FROM organizations o
LEFT JOIN user_organizations uo ON uo.organization_id = o.id AND uo.user_id = ?
WHERE o.is_active = 1 AND (o.consulting_firm_id = ? OR uo.user_id IS NOT NULL)
ORDER BY uo.is_primary DESC, o.name',
[$this->getCurrentUserId(), $firmId]
);
} else {
$orgs = Database::fetchAll(
'SELECT o.*, uo.role as user_role, uo.is_primary