[FEAT] #384 p.2.2/3 fondazione — modello relazionale REQUISITO<->POLICY (mig.059)

Migrazione 059 additiva/idempotente/reversibile:
- policies.proc_code VARCHAR(16) NULL: ripristina la chiave di join verso il
  catalogo canonico (cfg_nis2_procedure/cfg_nis2_requisiti), che andava persa
  quando le 42 procedure di default venivano copiate nell'elenco Policy (mig.048).
- tabella requisito_policy: M:N org-scoped requisito<->policy (un requisito puo'
  avere piu' policy e una policy piu' requisiti) — base per p.2.2 e p.3.
- backfill sicuro: proc_code valorizzato su 42 policy (match esatto titolo) +
  116 associazioni di default popolate (INSERT IGNORE, WHERE proc_code IS NULL).
- PolicyController::ensureFrameworkProcedures: i nuovi seed conservano proc_code
  e popolano requisito_policy (try/catch dedicato, non blocca il seed).

Nessun impatto utente in questo passo (nessuna superficie legge ancora i nuovi
campi). UI multi-valore + link bidirezionali (2.2/3) nei prossimi cicli. v1.23.6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-18 19:12:07 +02:00
co-authored by Claude Opus 4.8
parent 8a8045904b
commit 02f3e69227
4 changed files with 190 additions and 2 deletions
+15 -1
View File
@@ -99,15 +99,29 @@ class PolicyController extends BaseController
. "\n\nProcedura di default del framework NIS2 (Determinazione ACN n. 164179 del 14 aprile 2025)."
. ($p['misure'] ? ' Copre le misure: ' . $p['misure'] . '.' : '')
. ' Contenuto iniziale personalizzabile e cancellabile.';
Database::insert('policies', [
$pid = Database::insert('policies', [
'organization_id' => $orgId,
'title' => mb_substr($p['proc_descr'], 0, 255),
'category' => $cat,
'nis2_article' => mb_substr($p['first_misura'] ?? '', 0, 20),
'proc_code' => $p['proc_code'],
'content' => $content,
'status' => 'draft',
'ai_generated' => 0,
]);
// Ticket #384 (p.2.2/p.3): conserva il legame requisito<->policy.
// Collega tutti i requisiti che puntano a questa proc_code (M:N org-scoped,
// tabella requisito_policy mig.059). try/catch dedicato: un'eventuale assenza
// della tabella NON deve far fallire il seed delle procedure di default.
try {
Database::query(
"INSERT IGNORE INTO requisito_policy (organization_id, requisito_id, policy_id, is_default)
SELECT ?, q.id, ?, 1 FROM cfg_nis2_requisiti q WHERE q.proc_code = ?",
[$orgId, $pid, $p['proc_code']]
);
} catch (Throwable $eRp) {
error_log('[PolicyController] link requisito_policy (org ' . $orgId . ', proc ' . ($p['proc_code'] ?? '?') . '): ' . $eRp->getMessage());
}
}
Database::query(
'UPDATE organizations SET framework_procedures_seeded_at = NOW() WHERE id = ?',