[FEAT] Epic C / C5.2a — Attività stakeholder: questionari tipo + attività + calendario (mig.052)

Fondazione delle attività verso gli stakeholder (Simon C5 §4):
- Questionari tipo (template): tipo questionario-da-compilare / firma-di-avvenuta-lettura,
  domande (JSON) + collegamento m2m a procedure (policies) E a misure/requisiti del
  framework (cfg_nis2_misure/requisiti).
- Attività: basate (opz.) su un template, assegnabili a un CODICE stakeholder (tutti
  quelli di quel tipo) o a singoli stakeholder; pianificabili con data e scadenza.
- Calendario NIS2: review_schedule esteso (ENUM entity_type += 'stakeholder_activity');
  su create/update con scadenza la riga di calendario viene sincronizzata.
- Invio: genera un magic-link per destinatario (token SHA-256) per il portale esterno
  (C5.2b). Email disattivate (kill-switch) -> i link si condividono manualmente.

mig.052: stk_questionnaire_templates, stk_template_(procedures|misure|requisiti),
stk_activities, stk_activity_targets, stk_activity_procedures + ALTER review_schedule.
StakeholderActivityController + /api/stakeholder-activities/*; stakeholder-activities.html
(2 tab: attività / questionari tipo + dettaglio invio); voce sidebar V2; help/i18n.
Smoke prod OK (template m2m, attività by_code+calendario, assign, send magic-link,
anti-IDOR individual, negativi 422; cleanup org 151 pulita). Additivo. v1.20.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-16 22:12:11 +02:00
co-authored by Claude Opus 4.8
parent c782aa54fa
commit a5ff29e0da
46 changed files with 1562 additions and 142 deletions
+19
View File
@@ -309,6 +309,25 @@ class NIS2API {
stkUpdate(id, d) { return this._acn(this.put(`/stakeholders/${id}`, d || {})); }
stkDelete(id) { return this._acn(this.del(`/stakeholders/${id}`)); }
// ═══════════════════════════════════════════════════════════════════
// Attività stakeholder (Epic C / C5.2). Tutti _acn.
// Questionari tipo (template) + attività (questionari/azioni) + assegnazione
// (per codice o singoli) + invio (genera magic-link per il portale esterno).
// ═══════════════════════════════════════════════════════════════════
stkActTemplates() { return this._acn(this.get('/stakeholder-activities/templates')); }
stkActGetTemplate(id) { return this._acn(this.get(`/stakeholder-activities/templates/${id}`)); }
stkActCreateTemplate(d) { return this._acn(this.post('/stakeholder-activities/templates', d || {})); }
stkActUpdateTemplate(id, d) { return this._acn(this.put(`/stakeholder-activities/templates/${id}`, d || {})); }
stkActDeleteTemplate(id) { return this._acn(this.del(`/stakeholder-activities/templates/${id}`)); }
stkActPickers() { return this._acn(this.get('/stakeholder-activities/pickers')); }
stkActList() { return this._acn(this.get('/stakeholder-activities/list')); }
stkActGet(id) { return this._acn(this.get(`/stakeholder-activities/${id}`)); }
stkActCreate(d) { return this._acn(this.post('/stakeholder-activities/create', d || {})); }
stkActUpdate(id, d) { return this._acn(this.put(`/stakeholder-activities/${id}`, d || {})); }
stkActDelete(id) { return this._acn(this.del(`/stakeholder-activities/${id}`)); }
stkActAssign(id, d) { return this._acn(this.post(`/stakeholder-activities/${id}/assign`, d || {})); }
stkActSend(id) { return this._acn(this.post(`/stakeholder-activities/${id}/send`, {})); }
// ═══════════════════════════════════════════════════════════════════
// Dashboard
// ═══════════════════════════════════════════════════════════════════