Passa al contenuto principale

Tutorial - aggiungere un campo a job.workers

Esempio job-specifico del pattern di estensione CRUD. Per i passi comuni (sync DB, MCP generator, rigenerazione) vedi Aggiungere un campo su inv.invoices.

Questa pagina mostra le specificità del dominio job.

🎯 Cosa fa​

Esempio: aggiungere un campo emergencyContactPhone (numero di un familiare o amico contattabile in emergenza) alla tabella job.workers.

⚠️ Prerequisiti​

Stessi del tutorial generale: ambiente sviluppo + DB locale + MCP generator + branch dedicato.

👣 Passi​

1. Modifica job.workers​

File: TrainingHub.Database/job/Tables/workers.sql

Aggiungi il campo accanto a phone:

CREATE TABLE [job].[workers] (
-- ... campi esistenti ...
[phone] NVARCHAR (1024) NULL,
[emergencyContactPhone] NVARCHAR (128) NULL,
-- ... altri campi ...
);

GO
EXECUTE sp_addextendedproperty @name = N'MS_Description',
@value = N'Numero telefonico da contattare in caso di emergenza',
@level0type = N'SCHEMA', @level0name = N'job',
@level1type = N'TABLE', @level1name = N'workers',
@level2type = N'COLUMN', @level2name = N'emergencyContactPhone';
GO

Considerazioni job-specifiche:

  • Non toccare i campi chiave del cascade: companyId, companyLocationId, roleId, riskLevelId, departmentId, academicQualificationId. Modifiche a questi triggerano cascade QueryModifier.
  • Non interferire con la computed column label (derivata da firstName + lastName).
  • Indici: il nuovo campo non deve pesare sugli indici filtered esistenti (IX_workers_fiscalCode, IX_workers_active). Se il campo diventa frequentemente filtrato, valutare aggiunta indice dedicato.

2. Sincronizza il DB​

Come altri tutorial.

3. conf.json - considerazioni job​

File: TrainingHub.BackOffice/Components/CRUD/job/_conf/workers.dxgrid.conf.json

Per un campo contatto opzionale:

{
"Columns": {
"emergencyContactPhone": {
"Visible": false,
"FormInsertVisibility": "editable",
"FormUpdateVisibility": "editable"
}
},
"FormOrder": [
// ... altri campi ...
"phone",
"emergencyContactPhone",
// ...
]
}

Nascondi in grid di default (campi contatto spesso ingombranti) ma lascia editabile nel form.

4. Rigenera​

Invoca mcp__3sd-generator__generator_run per workers. File rigenerati:

TrainingHub.BackOffice/Components/CRUD/job/Worker.razor
TrainingHub.BackOffice/Components/CRUD/job/Worker.razor.tt.cs
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor.tt.cs

Non devono cambiare, perché sono gli escape hatch scritti a mano:

  • Worker.razor.cs - code-behind della pagina: inietta IRiskInheritanceService e ITrainingRequirementsService per i pannelli rischi e formazione. (Il cascade FK azienda → sede/reparto non è qui: sta in Forms/WorkerForm.razor.cs:14-31.)
  • Forms/WorkerForm.razor.cs - cascade FK e derivazione del sesso dal CF.

Attenzione, invece, a queste tre: sono CRUD generati su vista, con il proprio .razor.tt.cs e il proprio conf. Un generator_run le riscrive, quindi non vanno modificate a mano ma rigenerate:

ComponenteVista sorgenteConf
WorkerEffectiveRisk.razorjob.vw_workerEffectiveRisks_conf/vw_workerEffectiveRisks.dxgrid.conf.json
WorkersData.razorjob.vw_workersData_conf/vw_workersData.dxgrid.conf.json
WorkerJobHistory.razorjob.workerJobHistory_conf/workerJobHistory.dxgrid.conf.json

5. Aggiorna le viste analitiche (se rilevante)​

WorkersData.razor è CRUD generato sulla vista job.vw_workersData: il campo non si aggiunge al componente, si aggiunge alla vista SQL e poi si rigenera (generator_run). Modificare il .razor a mano lo espone a essere clobberato al primo rigenerato.

Per emergencyContactPhone non è rilevante (dato individuale), ma lo sarebbe per campi aggregabili (es. anniDiServizio).

6. Considera impatto su cascade rischi​

Per un campo puramente informativo come emergencyContactPhone:

  • Non triggera cascade.
  • Il CompaniesQueryModifier (reg) continuerà a chiamare worker.UpdateRiskLevel al cambio azienda anche se il nuovo campo non è coinvolto, ma non è un problema: l'UPDATE porta il predicato WHERE ISNULL(W.riskLevelId,-1) <> ISNULL(...) (Shared/DataLayer/job/workers.cs:28), quindi se nulla cambia non scrive righe. Costa una scansione, non una scrittura.

Per un campo che invece influenza il cascade (es. un nuovo riskFactorId):

  • Aggiornare la vista job.vw_workerEffectiveRisks (è lì che vive il cascade) e, se serve, worker.UpdateRiskLevel per la colonna materializzata. Non IRiskInheritanceService, che è di sola lettura.
  • Aggiungere QueryModifier dedicato se serve hook su cambi.
  • Ricalcolare workers.riskLevelId materializzato.

7. Considera impatto su workerJobHistory​

Se il campo deve essere storicizzato (es. un dato che cambia nel tempo e serve audit):

  • Aggiungi il campo anche a workerJobHistory con lo stesso tipo.
  • Aggiorna la logica di snapshot che popola workerJobHistory al cambio mansione per copiare anche il nuovo campo.

Per emergencyContactPhone non serve.

8. Considera impatto su import Excel​

TrainingHub.Import importa anche workers. Il nuovo campo:

  • Non viene popolato automaticamente - l'Excel deve avere la colonna e il mapping va aggiornato.
  • Campi ottimali nullable (come questo) non rompono l'import esistente.

9. Build, test, commit​

dotnet build TrainingHub.slnx
dotnet test TrainingHub.UnitTests

git add TrainingHub.Database/job/Tables/workers.sql
git commit -m "feat(db): add emergencyContactPhone to job.workers"

git add TrainingHub.BackOffice/Components/CRUD/job/_conf/workers.dxgrid.conf.json
git commit -m "feat(job): show emergencyContactPhone in worker form"

git add TrainingHub.BackOffice/Components/CRUD/job/Worker.razor \
TrainingHub.BackOffice/Components/CRUD/job/Worker.razor.tt.cs \
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor \
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor.tt.cs
git commit -m "regen job.workers"

⚡ Casi particolari job​

  • Nuovo campo cascade-sensitive. Se il campo ha impatto su riskLevelId del lavoratore (tipicamente improbabile - il cascade gira su rischi e ATECO, non su campi worker), aggiorna la vista vw_workerEffectiveRisks e worker.UpdateRiskLevel, poi valida il ricalcolo.
  • Nuovo campo su jobs (mansione). Se tocca riskLevelId o inheritsCompanyRisks, servirà un refresh massivo di workers.riskLevelId: costoso su dataset grandi.
  • Nuovo rischio. Aggiungere una voce a risks è banale (label + id). Impatta i flussi di assegnazione e può richiedere aggiornamento di eventuali viste pre-calcolate su compliance.
  • Nuovo codice ATECO. ATECO ha parentCode (gerarchia): inserire come figlio di un codice esistente, con un riskLevelId coerente con quello dei nodi vicini (ogni livello è assegnabile alle aziende).
  • Nuovo livello di rischio. riskLevel.id è INT. Aggiungere un 4° livello (es. "Critico"): potrebbe richiedere aggiornamenti a tabelle con riskLevelId nullable (oggi interpretato come "eredita parent") e a UI che mostrano livelli.

⚠️ Domande aperte​

  • Impatto su import Excel. Il mapping TrainingHub.Import va aggiornato manualmente. Checklist dei mapping da toccare per ogni tabella modificata.
  • Discriminazione cascade per campo. Problema ricorrente (già in reg): modificare un campo non cascade-sensitive (es. note) triggera comunque refresh globale di workers.riskLevelId. Ottimizzazione futura.
  • workerJobHistory sincronizzato automaticamente. Se aggiungi un campo storicizzabile a workers, la logica di snapshot va aggiornata in più punti (form custom, hook DB, trigger). Manca pattern centralizzato.

🔗 Vedi anche​