Files
nis2-agile/application/cli/seed_management_reviews.php
DevEnv nis2-agileandClaude Opus 4.8 79ca72fba9 [FEAT] ISO-readiness: Audit interni (§9.2) + Riesame Direzione (§9.3) + Calendario scadenze (mig.055-056, v1.22.0)
Rilascio unico (3 moduli) costruito in flotta parallela + verifica avversariale.

MODULO A — Audit interni (ISO 27001 §9.2, mig.055): internal_audits + internal_audit_items;
codice AUD-NNN; checklist pre-popolata (clausole 4-10 + Annex A applicabili dal SoA);
esiti per riga; apertura non conformità collegata a NCR/CAPA (source polimorfico);
report HTML stampabile. InternalAuditController + internal-audits.html.

MODULO B — Riesame di Direzione (ISO 27001 §9.3, mig.056): management_reviews +
management_review_decisions; codice RD-AAAA-NN; INPUT aggregati automaticamente dai
moduli (gather: NC/CAPA, audit interni, rischi+trattamenti, KPI/score, obiettivi, formazione,
stakeholder, normative, scadenze), congelati nello snapshot all'approvazione; decisioni;
verbale stampabile. ManagementReviewController + management-review.html.

MODULO C — Calendario unico scadenze: aggregatore SOLA LETTURA (nessuna migrazione) di tutte
le scadenze (incidenti/policy/rischi/NC-CAPA/formazione/stakeholder/audit/riesame/review_schedule),
griglia mensile + lista + filtri + deep-link. CalendarController + calendario.html.

Integrazione: router (3 controller+actionMap), api.js, sidebar V2+legacy, help.js (3 sezioni),
i18n (IT+EN), review_schedule ENUM += internal_audit. v1.22.0 + sw cache + cache-buster 20260630.

Verifica flotta (28 agenti, 4 dim + avversariale): 9 finding confermati, TUTTI corretti —
MAJOR gatherRisks (risk_treatments.organization_id inesistente → JOIN risks); nextCode numerico
(no dup >99/anno); footer report audit con etichetta "ISO buona prassi, non obbligo"; help 24→25
clausole; ARIA tab/calendario; focus modali; tasti su celle calendario; rimossi helper api morti.

Smoke prod OK (calendario 18 eventi, audit AUD-001 25 item + report + audit→NCR + audit→calendario,
riesame gather/decisione/approve/report, gatherRisks ora available); org 151 ripulita. Additivo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 07:48:49 +02:00

78 lines
3.2 KiB
PHP

<?php
/**
* NIS2 Agile — DDL idempotente Riesame di Direzione (ISO 27001 §9.3, mig.056).
* ----------------------------------------------------------------------------
* Crea (se mancanti) le tabelle del Modulo B "Riesame di Direzione":
* - management_reviews (il verbale: input aggregati congelati + esiti)
* - management_review_decisions (decisioni/output → owner ruolo, scadenza, CAPA)
*
* NESSUN dato di sistema: i riesami sono per-org (creati dall'utente).
*
* Idempotente: CREATE TABLE IF NOT EXISTS (re-eseguibile senza errore; in caso
* di tabella già presente ignora gli errno 1050/1061). Allineato a
* docs/sql/056_management_reviews.sql.
*
* Uso (dentro il container app, o sull'host con la stessa connessione PDO):
* docker exec nis2-app php /var/www/nis2-agile/application/cli/seed_management_reviews.php
*/
if (PHP_SAPI !== 'cli') { http_response_code(403); exit("Solo da CLI.\n"); }
require_once __DIR__ . '/../config/env.php';
require_once __DIR__ . '/../config/database.php';
$pdo = Database::getInstance();
$pdo->exec("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");
$ddl = [
"CREATE TABLE IF NOT EXISTS management_reviews (
id INT NOT NULL AUTO_INCREMENT,
organization_id INT NOT NULL,
code VARCHAR(20) NULL,
review_date DATE NULL,
period_label VARCHAR(100) NULL,
chair_user_id INT NULL,
attendees JSON NULL,
status ENUM('draft','approved') NOT NULL DEFAULT 'draft',
approved_by INT NULL,
approved_at DATETIME NULL,
snapshot JSON NULL,
conclusions TEXT NULL,
created_by INT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_mgr_org (organization_id),
CONSTRAINT fk_mgr_org FOREIGN KEY (organization_id) REFERENCES organizations (id) ON DELETE CASCADE,
CONSTRAINT fk_mgr_chair FOREIGN KEY (chair_user_id) REFERENCES users (id) ON DELETE SET NULL,
CONSTRAINT fk_mgr_approved FOREIGN KEY (approved_by) REFERENCES users (id) ON DELETE SET NULL,
CONSTRAINT fk_mgr_creator FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS management_review_decisions (
id INT NOT NULL AUTO_INCREMENT,
review_id INT NOT NULL,
decision TEXT NOT NULL,
owner_role_id INT NULL,
due_date DATE NULL,
status ENUM('open','in_progress','done') NOT NULL DEFAULT 'open',
capa_id INT NULL,
ord INT NOT NULL DEFAULT 0,
PRIMARY KEY (id),
KEY idx_mrd_review (review_id),
CONSTRAINT fk_mrd_review FOREIGN KEY (review_id) REFERENCES management_reviews (id) ON DELETE CASCADE,
CONSTRAINT fk_mrd_owner FOREIGN KEY (owner_role_id) REFERENCES org_roles (id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
];
foreach ($ddl as $stmt) {
try { $pdo->exec($stmt); }
catch (PDOException $e) { if (!in_array($e->errorInfo[1] ?? 0, [1050, 1061], true)) { throw $e; } }
}
$counts = [];
foreach (['management_reviews', 'management_review_decisions'] as $t) {
$counts[$t] = (int) $pdo->query("SELECT COUNT(*) FROM $t")->fetchColumn();
}
echo "OK seed-management-reviews — " . json_encode($counts, JSON_UNESCAPED_UNICODE) . "\n";