[FEAT] Ruolo Consulente + Wizard Registrazione v2

- register.html: step 0 scelta profilo (Azienda / Consulente)
- onboarding.html: wizard 4-step con P.IVA obbligatoria (auto-fetch CertiSource)
- companies.html: nuova dashboard consulente con cards aziende e compliance score
- common.js: org-switcher sidebar + role labels corretti per consulente
- login.html: routing post-login (consulente → companies.html)
- api.js: isConsultant(), setUserRole(), register con user_type
- AuthController: user_type=consultant → role=consultant in users table
- OnboardingController: multi-org per consulente, duplicate VAT check
- 005_consultant_support.sql: aggiunge 'consultant' a user_organizations.role ENUM

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-02-20 08:53:30 +01:00
co-authored by Claude Sonnet 4.6
parent ba21534e6a
commit 7080695d06
9 changed files with 976 additions and 323 deletions
@@ -132,17 +132,34 @@ class OnboardingController extends BaseController
$this->validateRequired(['name', 'sector']);
$userId = $this->getCurrentUserId();
$currentUser = $this->getCurrentUser();
$isConsultant = ($currentUser['role'] === 'consultant');
// Check if user already has an organization
// Check if user already has an organization (blocca solo per non-consulenti)
$existingOrg = Database::fetchOne(
'SELECT organization_id FROM user_organizations WHERE user_id = ? AND is_primary = 1',
[$userId]
);
if ($existingOrg) {
if ($existingOrg && !$isConsultant) {
$this->jsonError('Hai già un\'organizzazione configurata', 409, 'ORG_EXISTS');
}
// Per consulenti: verifica che la P.IVA non sia già associata al loro account
$vatNumber = $this->getParam('vat_number');
if ($isConsultant && $vatNumber) {
$cleanVat = preg_replace('/^IT/i', '', preg_replace('/\s+/', '', $vatNumber));
$dupOrg = Database::fetchOne(
'SELECT o.id FROM organizations o
JOIN user_organizations uo ON uo.organization_id = o.id
WHERE uo.user_id = ? AND o.vat_number = ?',
[$userId, $cleanVat]
);
if ($dupOrg) {
$this->jsonError('Questa azienda è già presente nel tuo portafoglio clienti', 409, 'ORG_DUPLICATE_VAT');
}
}
Database::beginTransaction();
try {
// Create organization
@@ -178,12 +195,14 @@ class OnboardingController extends BaseController
'voluntary_compliance' => $voluntaryCompliance,
], 'id = ?', [$orgId]);
// Link user as org_admin
// Link user: consulente → ruolo 'consultant', is_primary solo se prima org
$orgRole = $isConsultant ? 'consultant' : 'org_admin';
$isPrimary = $existingOrg ? 0 : 1;
Database::insert('user_organizations', [
'user_id' => $userId,
'organization_id' => $orgId,
'role' => 'org_admin',
'is_primary' => 1,
'role' => $orgRole,
'is_primary' => $isPrimary,
]);
// Update user profile if provided
@@ -207,7 +226,8 @@ class OnboardingController extends BaseController
$this->logAudit('onboarding_completed', 'organization', $orgId, [
'name' => $orgData['name'],
'sector' => $orgData['sector'],
'entity_type' => $entityType
'entity_type' => $entityType,
'is_consultant' => $isConsultant,
]);
$this->jsonSuccess([