Passa al contenuto principale

Logica applicativa β€” dominio job

🎯 Cosa fa​

La logica applicativa del dominio job gira intorno al cascade dei rischi β€” meccanismo che propaga il livello di rischio da ATECO β†’ azienda β†’ mansione β†’ lavoratore, con possibilitΓ  di override a ogni livello. Tre punti di ancoraggio:

  1. IRiskInheritanceService + worker.UpdateRiskLevel extension (in TrainingHub.Shared) β€” logica di calcolo
  2. CompaniesRisksQueryModifier (in BackOffice) β€” hook su cambi rischio azienda
  3. Code-behind WorkerForm.razor.cs β€” auto-calcolo da CF, cascade combobox
  4. WorkerEffectiveRisk.razor β€” vista calcolata

πŸ”§ IRiskInheritanceService + extension methods​

// In TrainingHub.Shared.Services
public interface IRiskInheritanceService
{
// (API pubblica β€” signature dettagliata da verificare nel file)
}

// Extension methods in TrainingHub.DataLayer.job.worker (static)
public static async Task UpdateRiskLevel(
ISimpleCRUDService db,
Guid? companyId = null);

Uso tipico:

  • reg.CompaniesQueryModifier.PostExecutionQuery dopo update azienda β†’ worker.UpdateRiskLevel(db, companyId).
  • job.CompaniesRisksQueryModifier dopo update rischi aziendali.
  • Potenzialmente anche hook su job.jobs, job.jobsRisks, job.workersRisks per coerenza β€” da ispezionare.

Cosa fa internamente:

  • Legge ATECO azienda β†’ livello suggerito
  • Legge override livello azienda (companies.riskLevelId)
  • Per ogni lavoratore collegato, determina:
    • Mansioni attive (via workersJobs)
    • Livello mansione (jobs.riskLevelId) o fallback azienda
    • Override lavoratore (workersRisks con esclusioni)
  • Materializza il risultato in workers.riskLevelId

🧩 CompaniesRisksQueryModifier​

Path: TrainingHub.BackOffice/Services/QueryModifiers/job/CompaniesRisksQueryModifier.cs.

Hook su operazioni CRUD di companiesRisks:

  • Insert / update / delete di un rischio aziendale β†’ refresh rischi effettivi dei lavoratori delle aziende coinvolte.

Effetto: modificare i rischi di un'azienda propaga coerentemente a tutti i lavoratori di quell'azienda, senza intervento esplicito dell'utente.

🧩 Code-behind WorkerForm.razor.cs​

Contiene logica UI-specifica non centralizzata nel service:

Cascade FK filter​

Al cambio companyId, filtra:

  • companyLocations disponibili (solo quelle dell'azienda selezionata)
  • departments disponibili (solo quelli dell'azienda selezionata)

Auto-calcolo da codice fiscale​

Se il CF Γ¨ valido (16 caratteri, checksum OK):

  • Calcola data di nascita e sesso dal CF
  • Eventualmente luogo di nascita (se il codice catastale Γ¨ riconosciuto nel catalogo italiano)

Implementazione tipica in un helper statico condiviso.

Validazione CF​

Checksum del codice fiscale italiano Γ¨ deterministico. Implementato lato C# con regex e algoritmo Luhn-like. Da verificare se il form valida in tempo reale o solo al salvataggio.

🧩 Vista WorkerEffectiveRisk​

Componente Razor che mostra la lista dei rischi finali del lavoratore applicando il cascade. Architettura tipica:

// Pseudocodice
List<EffectiveRisk> ComputeEffectiveRisks(Guid workerId)
{
var worker = LoadWorker(workerId);
var company = LoadCompany(worker.companyId);
var jobs = LoadWorkerJobs(workerId);
var companyRisks = LoadCompanyRisks(company.id);
var jobsRisks = jobs.SelectMany(j => LoadJobRisks(j.id));
var workerOverrides = LoadWorkerRisks(workerId);

// Accumula rischi con provenienza
var effective = new Dictionary<Guid, EffectiveRisk>();
foreach (var cr in companyRisks)
effective[cr.riskId] = new(cr.riskId, cr.riskLevelId, "azienda");

foreach (var jr in jobsRisks)
if (!effective.ContainsKey(jr.riskId))
effective[jr.riskId] = new(jr.riskId, jr.riskLevelId, "mansione");

foreach (var wr in workerOverrides)
{
if (wr.excluded)
effective.Remove(wr.riskId);
else
effective[wr.riskId] = new(wr.riskId, wr.riskLevelId, "override");
}

return effective.Values.ToList();
}

(Implementazione reale: vedi WorkerEffectiveRisk.razor + code-behind).

🧩 Staticizzazione workers.riskLevelId​

Il campo riskLevelId su workers Γ¨ materializzato: viene calcolato e scritto al posto di essere derivato a ogni query. Trade-off:

Pro:

  • Query su "lavoratori con rischio alto" sono immediate (filtro su colonna, index-friendly).
  • Join con formazione/compliance piΓΉ efficienti.

Contro:

  • Coerenza dipende da puntualitΓ  dei refresh.
  • Se un refresh viene saltato (errore, path non coperto dal QueryModifier), il dato Γ¨ fuori sync.

Mitigazioni:

  • QueryModifier chiave invocati da piΓΉ percorsi (azienda, rischi azienda).
  • Possibile batch job di rebuild massivo su endDateWork IS NULL.

πŸ“¦ Dipendenze​

  • Brighela.SimpleCRUD β€” CRUD base e hook
  • TrainingHub.Shared.Services.IRiskInheritanceService + TrainingHub.Shared.Services.ITrainingExpirationService (quest'ultimo consuma output job)
  • Cross-dominio:
    • reg.companies, reg.companyLocations, reg.academicQualifications β€” FK in uscita da workers
    • reg.companySubcategories β€” FK in uscita da jobSubcategories
    • edu.* β€” consumatori di workers.riskLevelId (via ITrainingExpirationService)

πŸ“ File chiave​

  • TrainingHub.Shared/Services/IRiskInheritanceService.cs + RiskInheritanceService.cs
  • TrainingHub.DataLayer/job/worker.Extensions.cs (o simile) β€” UpdateRiskLevel static method
  • TrainingHub.BackOffice/Services/QueryModifiers/job/CompaniesRisksQueryModifier.cs
  • TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor.cs
  • TrainingHub.BackOffice/Components/CRUD/job/WorkerEffectiveRisk.razor (+ eventuale code-behind)

⚠️ Debito tecnico​

  • Staticizzazione riskLevelId β€” coerenza. Non esiste job di rebuild massivo documentato. Se un bug del QueryModifier lascia dati fuori sync, non c'Γ¨ strumento diagnostico rapido.
  • Regola calcolo rischio finale lavoratore. Quando esistono piΓΉ mansioni con livelli diversi: quale prevale? (Max? Ultimo inserito? Manuale?) Non documentato β€” logica da ispezionare nel codice.
  • Validazione CF italiano. Se implementata solo lato UI, import da Excel o API bypassano il check. Valutare validazione a livello DB (check constraint con UDF) o service centrale.
  • Cascade combobox in WorkerForm. Logica cascade FK Γ¨ duplicata tra form (qui, e in companies reg). Helper condiviso farebbe bene.
  • workerJobHistory β€” trigger autmatico. Al cambio mansione, chi inserisce il record in workerJobHistory? Form custom? Hook DB? Da chiarire.
  • Performance UpdateRiskLevel globale. Chiamata senza companyId ricalcola TUTTI i workers: costoso su dataset multi-tenant grande.

πŸ”— Vedi anche​