Dominio job β Panoramica sviluppatore
π― Cosa faβ
Il dominio job (workers & risks) gestisce anagrafica lavoratori,
mansioni, ruoli aziendali, reparti e rischi. Γ il ponte logico
tra reg (aziende) ed edu (formazione): da reg eredita il
contesto azienda/sedi, verso edu determina quali corsi sono
obbligatori tramite il cascade rischi.
Dominio di dimensione media: 17 tabelle, tutte in scope.
πΊοΈ Mappa moduliβ
Database β TrainingHub.Database/job/β
| Area | Tabella | Ruolo |
|---|---|---|
| Lavoratori | job.workers | Anagrafica dipendenti |
| Lavoratori | job.workerJobHistory | Storico mansioni/ruoli/reparti |
| Mansioni | job.jobs | Mansioni (con riskLevel e inheritsCompanyRisks) |
| Mansioni | job.jobGroups | Raggruppamenti visuali di mansioni |
| Mansioni | job.jobSubcategories | N:N mansione β sottocategoria azienda |
| Mansioni | job.workersJobs | N:N lavoratore β mansione |
| Ruoli | job.roles | Ruoli aziendali (lavoratore, preposto, dirigente) |
| Struttura | job.departments | Reparti gerarchici (parentId) |
| Rischi | job.risks | Anagrafica rischi |
| Rischi | job.riskLevel | Livelli (1=basso, 2=medio, 3=alto) |
| Rischi | job.atecoCodes | Codici ATECO gerarchici (parentCode) con riskLevelId suggerito |
| Rischi | job.companiesRisks | Rischi aziendali |
| Rischi | job.jobsRisks | Rischi per mansione |
| Rischi | job.workersRisks | Override/esclusione rischio per lavoratore |
| Rischi | job.workerEffectiveRisksCache | Cache di vw_workerEffectiveRisks, popolata da sp_refreshEffectiveRisksForScope |
| Rischi | job.atecoCodesLegacy | Mapping codici ATECO legacy β 2025 |
| Documenti | job.workerDocuments | Bridge lavoratore β oss.documents |
| Compliance | job.vw_workerComplianceSummary | Vista β rollup per-lavoratore: worstStatus (expired > missing > expiring > insufficient > ok) + conteggi per stato, incluso insufficientCount, + minDaysRemaining. Attenzione: okCount conta solo status = 'ok' AND hoursShortfall = 0. Filtrabile per companyId. Aggrega edu.vw_workerTrainingStatus |
Service layer β TrainingHub.Shared/Services/β
| File | Ruolo |
|---|---|
IRiskInheritanceService.cs / RiskInheritanceService.cs | Sola lettura dei rischi effettivi (job.vw_workerEffectiveRisks), per lavoratore o per azienda |
DataLayer/job/workers.cs | UpdateRiskLevel β materializza workers.riskLevelId; SyncSiblingsByFiscalCode β allinea l'anagrafica fra worker con lo stesso CF |
ITrainingExpirationService.cs / TrainingExpirationService.cs | Usa i rischi del lavoratore per calcolare compliance (condiviso con edu) |
QueryModifiers β TrainingHub.BackOffice/Services/QueryModifiers/job/β
Sono sette:
| File | Scope |
|---|---|
CompaniesRisksQueryModifier.cs | Hook su companiesRisks β UpdateRiskLevel(companyId) + refresh cache variante e rischi |
WorkersQueryModifier.cs | Hook su workers β UpdateRiskLevel, sync anagrafico per CF, tre stored procedure di refresh; e in PreExecutionQuery la guardia di cancellazione |
WorkersJobsQueryModifier.cs | Hook su workersJobs β UpdateRiskLevel + apertura/chiusura del periodo in workerJobHistory |
JobsQueryModifier.cs | Hook su jobs |
JobsRisksQueryModifier.cs | Hook su jobsRisks |
WorkersRisksQueryModifier.cs | Hook su workersRisks β solo refresh cache, non tocca riskLevelId |
AtecoCodesQueryModifier.cs | Hook su atecoCodes β propaga il livello suggerito alle aziende (reg.company.UpdateRiskLevel) |
UI CRUD β TrainingHub.BackOffice/Components/CRUD/job/β
EntitΓ CRUD standard:
Worker,WorkerJobHistory,WorkersData(vista aggregata)Job,JobGroup,JobSubcategory,JobsDataWorkersJob(associazione)Role,DepartmentRisk,RiskLevel,AtecoCodeCompaniesRisk,JobsRisk,WorkersRiskWorkerEffectiveRiskβ vista calcolata dei rischi effettivi del lavoratore (cascade applicato)WorkerComplianceSummaryβ CRUD auto-generato sulla viewvw_workerComplianceSummary(read-only). FK manuali su worker/company/role/department, badge stato in colonnaworstStatus. Embed nel wizardCompanyFormPopupcome step "ConformitΓ formativa". Pattern descritto in Architettura β view β CRUD
π§ API pubblicaβ
IRiskInheritanceServiceβ
Legge i rischi effettivi, non li propaga. Due sole query su
job.vw_workerEffectiveRisks (IRiskInheritanceService.cs:5-9):
GetEffectiveRisksAsync(workerId) e
GetEffectiveRisksByCompanyAsync(companyId). Consumato da
Worker.razor.cs, Company.razor.cs e Program.cs.
worker.UpdateRiskLevel β la propagazione veraβ
TrainingHub.Shared/DataLayer/job/workers.cs:7:
public static async Task<int> UpdateRiskLevel(
ISimpleCRUDService simpleCRUD,
Guid? jobId = null, Guid? companyId = null, Guid? workerId = null);
Un UPDATE che scrive workers.riskLevelId come massimo fra le
mansioni, con fallback sul livello azienda. Invocato da
reg/CompaniesQueryModifier.cs:78,82,
job/CompaniesRisksQueryModifier.cs:28,
job/WorkersQueryModifier.cs:72,74,
job/WorkersJobsQueryModifier.cs:62, job/JobsQueryModifier.cs:34,40,
job/JobsRisksQueryModifier.cs:28 e
Shared/Services/TrainingImportService.Processing.cs:884.
La propagazione ATECO β azienda Γ¨ un passo a monte e distinto:
job/AtecoCodesQueryModifier.cs:29, che chiama
reg.company.UpdateRiskLevel.
Dettaglio in logica applicativa.
ITrainingExpirationServiceβ
Calcola compliance formativa combinando rischi del lavoratore (dominio job) con formazione ricevuta (dominio edu). Dettaglio in Logica applicativa edu.
π§© Pattern chiaveβ
Cascade rischiβ
Codice ATECO
βββ riskLevelId suggerito
βββ azienda.riskLevelId (default)
βββ jobs.riskLevelId (opzionale β override per mansione)
β βββ jobs.inheritsCompanyRisks
β β βββ aggiunge i rischi companiesRisks ai rischi jobsRisks
β β βββ tranne quelli con companiesRisks.onlyForExposedJobs = 1 (rischio d'attivitΓ : vale solo per le mansioni che lo dichiarano)
β βββ jobsRisks.onlyIfCompanyHasRisk (indipendente da inheritsCompanyRisks β il rischio mansione entra solo se l'azienda ha lo stesso rischio)
βββ workers.riskLevelId (staticizzato per lavoratore)
βββ workersRisks (override singolo rischio + esclusioni)
Propagazione automatica:
- Cambio ATECO azienda β
CompaniesQueryModifierβUpdateRiskLevel - Cambio riskLevel azienda β idem
- Cambio
companiesRisksβCompaniesRisksQueryModifierβ ricalcolo - Cambio mansione lavoratore β staticizzazione
workers.riskLevelId
Storico mansioniβ
workerJobHistory Γ¨ uno snapshot storico della sola mansione.
WorkersJobsQueryModifier apre il periodo sull'Insert di
workersJobs (righe 67-77) e lo chiude sul DeleteSingle (29-51),
registrando i valori correnti di riskLevelId, roleId,
departmentId.
β οΈ I cambi di ruolo e reparto passano da job.workers e
WorkersQueryModifier (:64-93) non tocca workerJobHistory: non
lasciano traccia nello storico. Entrambe le operazioni sono
best-effort β un errore nell'audit non blocca l'operazione CRUD.
Vista WorkerEffectiveRiskβ
Componente UI non-CRUD che mostra i rischi finali del lavoratore applicando il cascade (rischi azienda + mansione + override lavoratore, meno esclusioni). Utile per DVR e per determinare formazione richiesta.
Gerarchia departmentsβ
Tabella auto-referenziante: departments.parentId crea un albero
reparti (es. "Produzione" β "Linea A" β "Turno notte"). UI deve
supportare visualizzazione ad albero.
Codici ATECO gerarchiciβ
atecoCodes.parentCode simile ai reparti: ATECO hanno gerarchia
(es. "F - Costruzioni" β "F.41 - Costruzione di edifici" β "F.41.1 -
Sviluppo di progetti immobiliari"). Flag selectable indica quali
codici possono essere assegnati direttamente alle aziende (foglie o
nodi selezionabili).
π¦ Dipendenzeβ
Brighela.SimpleCRUDβ CRUD base + QueryModifier- Stack UI comune (DevExpress, Tabiot, Oss)
Cross-dominio in entrata (FK da altri domini a job):
reg.companies.riskLevelIdβjob.riskLevel(id)reg.companies.atecoCodeβjob.atecoCodes(code)edu.courses.trainingVariantIdnon tocca job, ma varianti formative (fuori scope edu) mappano verso rischi
Cross-dominio in uscita (FK da job):
job.workers.companyIdβreg.companies(id)job.workers.companyLocationIdβreg.companyLocations(id)job.workers.academicQualificationIdβreg.academicQualifications(id)job.departments.companyIdβreg.companies(id)job.jobSubcategories.companySubcategoryIdβreg.companySubcategories(id)
π File chiaveβ
Database/job/Tables/*.sql(17)Database/job/Views/vw_workerEffectiveRisks.sqlβ l'autoritΓ sul cascadeDatabase/job/Functions/fn_getWorkerRisks.sqlBackOffice/Services/QueryModifiers/job/*.cs(7)BackOffice/Components/CRUD/job/Worker.razor{,.cs,.tt.cs}+Forms/WorkerForm.razorBackOffice/Components/CRUD/job/WorkerEffectiveRisk.razorβ vista rischi effettivi calcolatiShared/Services/IRiskInheritanceService.cs+ impl
β οΈ Domande aperte / debito tecnicoβ
-
Regola calcolo rischio finale del lavoratore.Γ il massimo fra le mansioni, con fallback sul livello dell'azienda:ISNULL(MAX(J.riskLevelId), C.riskLevelId)(Shared/DataLayer/job/workers.cs:19-27). Gli override personali (workersRisks) valgono per i rischi effettivi, non per questa colonna. -
Storico mansioni β trigger automatico.Automatico:OpenPeriodAsyncsull'InsertdiworkersJobs,ClosePeriodAsyncsulDeleteSingle(WorkersJobsQueryModifier.cs:44,71), entrambe idempotenti e best-effort. Copre solo la mansione, non ruolo e reparto. -
Da otto punti:workers.riskLevelIdstaticizzato β quando viene refreshato?WorkersQueryModifier.cs:72,74,WorkersJobsQueryModifier.cs:62,JobsQueryModifier.cs:34,40,JobsRisksQueryModifier.cs:28,CompaniesRisksQueryModifier.cs:28,reg/CompaniesQueryModifier.cs:78,82,TrainingImportService.Processing.cs:884. Dettaglio in logica applicativa. -
Refresh massivo senzaNon Γ¨ un problema: l'companyId.UPDATEfiltra suWHERE ISNULL(W.riskLevelId,-1) <> ISNULL(...)(workers.cs:28), quindi tocca solo le righe fuori sync. Γ anzi il meccanismo di rebuild. - Nessuna validazione del checksum del codice fiscale.
WorkerForm.razor.cs:10controlla soloLength >= 16. Vedi il debito in logica applicativa. - Mansioni globali vs per-azienda. Oggi
jobsè tabella globale: non hacompanyId. Un'azienda "Acme" e "Beta" che hanno un "Saldatore" condividono lo stesso record. Se una vuole personalizzare rischi solo per la propria "Saldatore" deve creare una mansione custom. Pattern da documentare. -
atecoCodes.selectableβ semantica. Chi puΓ² deselezionare? Sono ATECO pre-popolati dal sistema o l'utente puΓ² aggiungerne?
π Vedi ancheβ
- Schema DB
- Componenti UI
- Logica applicativa
- Aggiungere un campo
- Guida utente: Panoramica lavoratori (docs-site-user)
- Dominio
reg: logica applicativa β cascadeCompaniesQueryModifierinvoca job - Dominio
edu: logica applicativa β usa rischi job per calcolare compliance