[FEAT] A4 Fase 4.2 — Competenze (skill/requisiti ruolo/gap) + alert→azione NCR+CAPA

Secondo modulo del modello relazionale A4 (docs/DESIGN_A4_RELATIONAL.md).

Backend:
- Migration 042 (docs/sql/042_competences.sql) + runner aggiornato (scripts/migrate-a4.php).
  4 tabelle additive/idempotenti: skills (org-owned o globali), role_skills
  (competenze richieste dal ruolo, UNIQUE role+skill), user_skills (possedute,
  UNIQUE org+user+skill), skill_course_map. APPLICATA su prod (nis2-db v8.0.45,
  TLSv1.3).
- CompetenceController (route 'competences'): catalog, skills CRUD, roleSkills,
  userSkills, skillCourses (map/unmap), gapGrid (richiesto−posseduto per ruolo
  con titolare), openAction. Multi-tenancy + anti-IDOR + livelli 1-5; competenze
  globali in sola lettura per gli utenti org.
- alert→azione: openAction RIUSA il workflow NCR/CAPA reale (la tabella azioni è
  capa_actions figlia di non_conformities, NON 'corrective_actions' che non
  esiste): crea NCR (source='management_review', source_entity_type='competence_gap',
  source_entity_id=role_skills.id) + capa_actions figlia. Anti-duplicato sul gap.
  Zero ALTER alle tabelle esistenti. Ancoraggio PR.AT-01/02, GV.RR-04, art.24 D.Lgs.

Frontend:
- competenze.html (4 schede: Catalogo, Requisiti per ruolo, Competenze persone,
  Gap & azioni) + js/competenze.js. Bootstrap Italia V2. CTA "Assegna corso"
  (riuso /training/assign) e "Apri non conformità".
- Voce sidebar "Competenze" (common.js + common-bi.js + BI_PAGES) + nav.competences
  i18n IT/EN. api.js: metodi comp* + assignTraining.

Help/KB/PWA:
- help.js: guida contestuale 'competences' (schede, calcolo gap, PR.AT-01/02,
  GV.RR-04, art.24 D.Lgs., disclaimer no-parere-legale) + mappa pagina.
- sw.js: nome cache nis2-shell-v1.17.0 (allineamento PWA).
- Design doc corretto (rettifica capa_actions vs corrective_actions) + avanzamento 4.1/4.2.

Cache-buster: ?v=20260618 dei 5 JS condivisi su 32 HTML. version.json 1.16.0 -> 1.17.0.
Smoke E2E su prod (fpm reale): catalogo, requisito, competenza, gap=2, openAction
(NCR+CAPA), anti-dup 409, mappatura corso — tutti verdi; dati di test ripuliti.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-15 19:01:32 +02:00
co-authored by Claude Opus 4.8
parent 47199f1e4e
commit 5368b0a61c
47 changed files with 1594 additions and 143 deletions
+19
View File
@@ -87,6 +87,7 @@ $controllerMap = [
'auth' => 'AuthController',
'organizations' => 'OrganizationController',
'org-roles' => 'OrgRoleController', // A4 Fase 4.1 — Organigramma (ruoli/gerarchia, Art.23 governance)
'competences' => 'CompetenceController', // A4 Fase 4.2 — Competenze (skill, requisiti ruolo, gap, alert->azione)
'assessments' => 'AssessmentController',
'acn-gap' => 'AcnAssessmentController', // Gap Analysis ACN (Det. 164179/2025, misure/requisiti)
'isms' => 'IsmsModelController', // Modello Organizzativo SGSI (ISO 27001/27017/27018 + SoA)
@@ -204,6 +205,24 @@ $actionMap = [
'DELETE:{id}' => 'delete',
],
// ── CompetenceController — Competenze (A4 Fase 4.2) ──
'competences' => [
'GET:catalog' => 'catalog',
'POST:skills' => 'createSkill',
'PUT:skills/{subId}' => 'updateSkill',
'DELETE:skills/{subId}' => 'deleteSkill',
'GET:roleSkills/{subId}' => 'roleSkills', // {subId} = role_id
'POST:roleSkills' => 'setRoleSkill',
'DELETE:roleSkills/{subId}' => 'removeRoleSkill',
'GET:userSkills/{subId}' => 'userSkills', // {subId} = user_id
'POST:userSkills' => 'setUserSkill',
'DELETE:userSkills/{subId}' => 'removeUserSkill',
'POST:skillCourses' => 'mapCourse',
'DELETE:skillCourses/{subId}' => 'unmapCourse',
'GET:gapGrid' => 'gapGrid',
'POST:openAction' => 'openAction',
],
// ── AssessmentController ────────────────────────
'assessments' => [
'GET:list' => 'list',