Passa al contenuto principale

Dominio fin — Panoramica sviluppatore

🎯 Cosa fa

Il dominio fin (funded projects) gestisce i progetti formativi finanziati da FondItalia: ciclo di vita del progetto, fasi, moduli, edizioni, aziende partecipanti, rendicontazione.

Dominio grande: 48 tabelle. Questa pagina copre il core F2 (7 tabelle): progetti, fasi, moduli, edizioni, aziende, adesioni. Le aree restanti (spese, staff, documenti, integrazioni, anagrafiche di supporto) saranno documentate in spec successive.

Le iscrizioni dei lavoratori alle edizioni FondItalia vivono in edu.workerTrainingDetails (con trainingSessionId = fin.editions.id via shared PK). Le presenze sono tracciate dalle colonne attendanceHours, attendanceRate e passed della stessa tabella. La tabella fin.enrollments è stata rimossa.

🗺️ Mappa moduli

Database — TrainingHub.Database/fin/

Core in questa documentazione (7):

AreaTabellaRuolo
Progettifin.projectsProgetto formativo finanziato
Progettifin.phasesAnagrafica fasi (ordinate, con managedType)
Progettifin.projectPhasesIstanza fase per progetto (stato + completionRate)
Strutturafin.modulesModuli formativi del progetto
Strutturafin.editionsEdizioni del modulo (replicazioni)
Partecipazionefin.projectCompaniesN:N progetto ↔ azienda + regime aiuto
Partecipazionefin.companyMembershipsAdesione azienda al fondo

Estensioni coperte lato utente (dettaglio dev pianificato come lavoro futuro):

AreaTabellePagina utente
Documenti & compliancecompanyDocuments, projectDocuments, workerDocuments, staffDocuments, documentTypesDocumenti di progetto
Fondi & bandiaccounts, budgets, cofinancing, contributions, calls, callWindows, callDocuments, callAmendments, callAmendmentWindows, callAmendmentDocumentsFondi, bandi e budget
Speseexpenses, expenseCategories, expenseChapters, expenseDocumentsSpese e rendicontazione, Foglio di rendicontazione
Staff & sessionistaff (FK nullable userId → kset.users per riuso identità / email / ruoli), staffRoles (lookup ruolo con colore), projectStaff, sessions, attendanceRegisters, phaseRequirements, projectHistoryStaff e sessioni
Calendario orecalendarEntries, calendarBlocksCalendario ore staff
VariazioniprojectVariations, variationDocuments, variationEnrollments, variationTypeDocumentsVariazioni
Anagrafiche & integrazioniteachingMethods, thematicAreas, integrationRequests, implementingBodies (Ente Attuatore, FK NOT NULL sul progetto)Anagrafiche e integrazioni
AdesionicompanyMemberships, companyMembershipStatusHistory, membershipDocuments, accountDocuments, femidataSummariesAdesioni FondItalia
Requisiti di fasephaseRequirements, projectPhaseRequirementChecksFasi e requisiti

Schema DB dettagliato di queste estensioni non incluso in Schema DB (focus sulle 7 core).

Documentazione pregressa

Ricco materiale disponibile in:

  • docs/fonditalia-client-overview.md — panoramica cliente-friendly delle fasi (call 03/04/2026).
  • docs/fonditalia-technical-overview.md — overview tecnica.
  • docs/fonditalia_context_for_claude_code.md — contesto storico.
  • docs/superpowers/specs/_archive/2026-04-02-fonditalia-integration-design.md e 2026-04-03-fonditalia-v2-design.md — spec iniziali.

UI CRUD — TrainingHub.BackOffice/Components/CRUD/fin/

45 pagine CRUD auto-generate (e 58 file di conf, perché alcune entità hanno più viste). Pattern identico a inv/reg/edu/job (vedi componenti UI inv).

Le 7 entità core:

  • Project, Phase, ProjectPhase
  • Module, Edition
  • ProjectCompany, CompanyMembership

🔧 Enum e stati

Dal file TrainingHub.Shared/Enums.cs:

public enum AccountType { rete, monoaziendale }
public enum PhaseManagedType { active, tracking, tracking_readonly }
public enum PhaseRequirementType { document, data_field, action }
// 21 stati, gli stessi vincolati da CK_projects_status
public enum ProjectStatus { BOZZA, IN_COMPOSIZIONE, PRESENTATO,
IN_CONDIVISIONE, CONDIVISIONE_OK, CONDIVISIONE_NEGATA,
INVIO_ALLEGATI_AZIENDE, IN_APPROVAZIONE, APPROVATO,
APPROVATO_CON_MODIFICHE, RIGETTATO, IN_PREPARAZIONE,
IN_EROGAZIONE, EROGAZIONE_COMPLETATA, IN_RENDICONTAZIONE,
RENDICONTAZIONE_INVIATA, CHIUSO, IN_INTEGRAZIONI, SALDO,
REVOCATO, RINUNCIATO }
public enum AidRegime { de_minimis, aiuti_formazione }
public enum ProjectPhaseStatus { not_started, in_progress, completed, skipped }
public enum MembershipStatus { active, suspended, ceased, ceased_provisional,
revoked, not_adhering, failed, not_processable }
public enum DocumentSubject { azienda, progetto, docente, modulo, sessione,
corsista, spesa, variazione }
public enum VariationType { rimodulazione, sostituzione_iscritto,
ritiro_iscritto, calendario, altro }
public enum VariationStatus { IN_ATTESA, APPROVATA }
public enum ExpenseDocumentRole { costo, pagamento }

DocumentStatus è stato rimosso (sotto-progetto D, 2026-07): companyDocuments/projectDocuments/workerDocuments sono bridge Pattern-A puri (documentId NOT NULL, niente status/documentTypeId/notes) — il tipo del file si legge dal categorySlug di oss.documents, non da una colonna sul bridge.

🧩 Pattern chiave

Ciclo a fasi

Il progetto segue una sequenza di fasi regolamentate. Ogni fase ha:

  • Un managedType: active (gestita nel sistema), tracking (solo stato), tracking_readonly (esterna, no dati).
  • Requisiti (phaseRequirements fuori scope): documenti, campi dati, azioni richieste per chiudere la fase.
  • Un flag hideable che permette di saltarla.

Lo stato del progetto (projects.status) è sincronizzato con la fase attiva, ma con più granularità: 21 stati su 8 fasi.

Il motore è fatto di quattro pezzi, ciascuno con una sola responsabilità:

PezzoResponsabilità
Services/FundedTraining/ProjectStatusPhaseMap.csmappa stato → fase: dato uno stato, quale fase risulta attiva
Services/FundedTraining/ProjectStatusTransitions.csquali transizioni di stato sono ammesse da ciascuno stato. Porta due FIXME validate-fonditalia (righe 13 e 19), ipotesi da confermare col process owner
Services/FundedTraining/ProjectLifecycleService.csorchestrazione: applica la transizione, riallinea le fasi, scrive lo storico. La prima fase si sceglie per sortOrder minimo, quindi è dinamica: togliere una fase dal seed basta
Services/FundedTraining/PhaseRequirementEvaluators.csi valutatori automatici dei requisiti di fase

I valutatori registrati sono cinque, indirizzati per evaluatorKey: project.allAppointmentsHaveDates, project.hasWorkersEnrolled, project.hasCompanies, project.chaptersWithinLimits, project.noPendingVariations. Un requisito senza evaluatorKey non è valutabile automaticamente: si spunta a mano, e la spunta vive in fin.projectPhaseRequirementChecks.

Le fasi non si avanzano direttamente: si cambia lo stato del progetto e le fasi seguono. Non esiste un comando «completa fase».

Avvisi, sportelli, rettifiche

Il modello dei bandi ha tre livelli, non uno (non esiste più alcun fin.notices):

  • fin.calls — l'avviso pubblicato dal fondo: il contenitore.
  • fin.callWindows — lo sportello, cioè la finestra di presentazione con i propri termini. Il progetto sta su uno sportello, non su un avviso: è lo sportello a fissare le scadenze.
  • fin.callAmendments — la rettifica che il fondo pubblica su un avviso già uscito, con fin.callAmendmentWindows per gli sportelli che tocca.

Il punto delicato è il congelamento dei termini: callAmendmentWindows conserva sia i nuovi termini sia quelli precedenti, così una rettifica resta ricostruibile a posteriori invece di sovrascrivere la storia. La logica sta in Services/FundedTraining/CallAmendmentService.cs:30-55, con Services/QueryModifiers/fin/CallAmendmentWindowsQueryModifier.cs a presidiare le scritture.

Avvisi, sportelli e rettifiche hanno ciascuno il proprio bridge documenti (callDocuments, callAmendmentDocuments).

Variazioni in itinere

Una variazione (fin.projectVariations) è la richiesta che l'Ente Attuatore inoltra al fondo durante il progetto: rimodulazione ore, cambio calendario, sostituzione o ritiro di iscritti. È un fascicolo, non un campo: tipo, descrizione, documenti a supporto ed esito del fondo.

  • fin.variationEnrollments registra i nominativi coperti: l'iscrizione uscente e, se è una sostituzione, quella entrante.
  • fin.variationTypeDocuments mappa quali tipi documento ciascun tipo di variazione si aspetta — è seedato, non si compila caso per caso.
  • La logica sta in Services/FundedTraining/ProjectVariationService.cs:128-210.

Il legame col ciclo di vita passa dall'evaluator project.noPendingVariations: una variazione ancora aperta tiene indietro la fase.

Multi-progetto per azienda

Un'azienda può partecipare a più progetti contemporaneamente via projectCompanies. La disponibilità sul conto (companyMemberships.availableBalance) è condivisa: il sistema dovrebbe scalarla con l'avvio di nuovi progetti, ma la logica non è chiara.

Integrazione esterna FemiWeb

Presentazione, rendicontazione e comunicazioni con FondItalia passano da FemiWeb (piattaforma esterna). Il sistema esporta/importa dati con FemiWeb — la tabella integrationRequests (fuori scope) traccia queste comunicazioni.

📦 Dipendenze

  • Brighela.SimpleCRUD — CRUD base
  • UI stack standard (DevExpress, Tabiot)
  • Cross-dominio:
    • fin.projectCompanies.companyIdreg.companies(id)
    • fin.companyMemberships.companyIdreg.companies(id)
    • edu.workerTrainingDetails.trainingSessionIdfin.editions(id) (iscrizioni lavoratori)

📁 File chiave

  • Database/fin/Tables/*.sql (~29)
  • BackOffice/Components/CRUD/fin/*.razor (~29)
  • Shared/Enums.cs — enum progetto, fase, regime, stato
  • docs/fonditalia-*.md — documentazione storica

⚠️ Domande aperte / debito tecnico

  • Sincronizzazione projects.statusprojectPhases.status. Aggiorna lo stato: ProjectStatusPhaseMap dice quale fase corrisponde a ogni stato, e ProjectLifecycleService fa avanzare le fasi di conseguenza. Non esiste il percorso inverso — non si completa una fase per far cambiare stato al progetto.
  • Transizioni stato progetto: macchina a stati formale o ad hoc? Formale e centralizzata: Services/FundedTraining/ProjectStatusTransitions.cs dichiara Allowed come dizionario stato → stati raggiungibili, con IsAllowed(from, to). Restano due FIXME validate-fonditalia da confermare col process owner.
  • Validazione saldo disponibile. Un progetto che supera la disponibilità dell'azienda non è bloccato automaticamente.
  • fin.editions vs edu.trainingSessions. Risolto: le edizioni FondItalia condividono la PK con edu.trainingSessions via pattern di ereditarietà; le iscrizioni lavoratori vivono in edu.workerTrainingDetails.
  • Integrazione FemiWeb. Gestita via integrationRequests ma non esiste documentazione di protocollo/payload.
  • phaseRequirements: dov'è la validazione "fase completabile solo se requisiti OK"? In Services/FundedTraining/PhaseRequirementsService.cs, che risolve i requisiti data_field tramite il registry PhaseRequirementEvaluators.cs (chiavi project.*). I requisiti con hardBlock = 1 sono gli unici che fermano l'avanzamento; gli altri compaiono come cose da confermare. Un test di drift (PhaseRequirementEvaluatorsTests) verifica che ogni evaluatorKey seedato sia registrato.

🔁 Workflow di promozione edu → fin

Un corso/sessione/appuntamento di edu può essere "promosso" alla controparte FondItalia in fin (shared PK):

Origine eduTarget finTab UIServizio
coursesmodules"Progetto FondItalia" sulla pagina CourseIFundedTrainingService.PromoteCourseToModuleAsync
trainingSessionseditions"Edizione FondItalia" sulla pagina TrainingSessionPromoteSessionToEditionAsync
appointmentssessions"Sessione FondItalia" sulla pagina AppointmentPromoteAppointmentToFinSessionAsync
(n/a — workflow inverso)aggancio bulk"Aggancia corso esistente" sulla pagina Projectusa PromoteCourseToModuleAsync per ogni course selezionato

Stati progetto ammessi alla promozione (hard-coded in FundedTrainingService.PromotableStatuses): BOZZA, IN_COMPOSIZIONE, IN_PREPARAZIONE. Gli stati post-presentazione (PRESENTATO in avanti, elencati in NonPromotableStatuses) richiederebbero una richiesta formale di modifica al fondo, quindi sono esclusi.

Vincoli a catena: per promuovere un appointment a fin.sessions la session parent deve essere già promossa a fin.editions. Analogamente, per promuovere una session a fin.editions almeno uno dei suoi corsi deve essere già promosso a fin.modules. I tab UI mostrano un messaggio informativo se il prerequisito manca, invece di abilitare il bottone.

Iscrizione worker in regime finanziato: il vincolo non è più su IFundedTrainingService, che non espone più EnrollWorkerInEditionAsync. Vive lato dati, in Services/QueryModifiers/edu/WorkerTrainingDetailsQueryModifier.cs:96-97: l'azienda del lavoratore deve comparire fra quelle restituite da IFundedEligibilityService.GetEligibleCompanyIdsForSessionAsync, cioè essere nel progetto e con adesione attiva sul conto FondItalia. Altrimenti il modifier lancia BusinessRuleException("fin_worker_company_not_eligible"). La fundedness si deriva via shared PK, niente flag denormalizzato.

Authorization: i tab riusano policy esistenti — page-fin-modules_R per visualizzare il tab Course→Module, page-fin-modules_C per la promozione effettiva (e analoghi per editions/sessions). Niente policy custom.

🔗 Vedi anche