From faadb62da6caa3e8ff902d57c16e7521ed00ca78 Mon Sep 17 00:00:00 2001 From: DevEnv nis2-agile Date: Wed, 29 Jul 2026 15:53:54 +0200 Subject: [PATCH] =?UTF-8?q?[FEAT]=20Ruolo=20consulente:=20visibilit=C3=A0?= =?UTF-8?q?=20firm-scoped=20(org-switcher=20+=20Cruscotto=20Studio)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- application/controllers/ConsultantController.php | 14 +++++++++++++- application/controllers/OrganizationController.php | 12 ++++++++++++ public/version.json | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/application/controllers/ConsultantController.php b/application/controllers/ConsultantController.php index cb0cb29..4a0291b 100644 --- a/application/controllers/ConsultantController.php +++ b/application/controllers/ConsultantController.php @@ -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 diff --git a/application/controllers/OrganizationController.php b/application/controllers/OrganizationController.php index 3aab1ac..5079f38 100644 --- a/application/controllers/OrganizationController.php +++ b/application/controllers/OrganizationController.php @@ -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 diff --git a/public/version.json b/public/version.json index 69b6c5c..ef287fc 100644 --- a/public/version.json +++ b/public/version.json @@ -1 +1 @@ -{"version": "1.25.10", "build": "2026-07-29-v1.25.10", "date": "2026-07-29", "changelog": "Gestione Rischi: la tabella 'Rischi dai requisiti del framework di sicurezza' ora scorre verticalmente (riquadro a scorrimento con intestazione fissa) invece di allungare la pagina, e mostra SOLO i requisiti applicabili alla classe del soggetto (importante -> Allegato 1, essenziale -> Allegato 2 della Determinazione ACN 164179/2025): i requisiti non pertinenti non compaiono piu."} +{"version": "1.25.11", "build": "2026-07-29-v1.25.11", "date": "2026-07-29", "changelog": "Cruscotto Studio: ora e' previsto anche il ruolo CONSULENTE. Un utente con ruolo 'consulente' vede nel selettore azienda e nel Cruscotto Studio SOLO le aziende del proprio studio (i clienti collegati al proprio studio) piu' quelle di cui e' membro diretto, non l'intero elenco (che resta agli amministratori)."}