Passa al contenuto principale

Tutorial — aggiungere un campo a una fattura

Guida end-to-end per aggiungere un nuovo campo a una tabella inv e propagarlo automaticamente fino alla UI. L'esempio aggiunge il campo customerPurchaseOrder (riferimento ordine cliente) alla tabella inv.invoices.

🎯 Cosa fa

Il tutorial copre il flusso completo:

  1. Modifica schema DB (file SQL)
  2. Sincronizzazione DB locale
  3. Rigenerazione CRUD (tabella + grid + form)
  4. Ritocchi su conf.json per visibilità e posizione
  5. Eventuale logica custom nel code-behind
  6. Verifica finale

⚠️ Prerequisiti

  • Ambiente di sviluppo con TrainingHub.Database apribile in Visual Studio / SSDT.
  • Database locale collegato alla BackOffice.
  • Accesso al server MCP 3sd-generator configurato in Claude Code (vedi .claude/settings.json).
  • Branch feature dedicato (no modifiche dirette su master / continuous-integration).

👣 Passi

1. Modifica inv.invoices nel progetto DB

File: TrainingHub.Database/inv/Tables/invoices.sql.

Aggiungi la colonna all'interno della CREATE TABLE e gli extended properties corrispondenti:

CREATE TABLE [inv].[invoices] (
[id] UNIQUEIDENTIFIER CONSTRAINT [DEFAULT_invoices_id] DEFAULT (newid()) NOT NULL,
-- ... (righe esistenti) ...
[customerPurchaseOrder] NVARCHAR (64) NULL,
[createdAt] DATETIME CONSTRAINT [DEFAULT_invoices_createdAt] DEFAULT (getdate()) NOT NULL,
[updatedAt] DATETIME NULL,
-- ... (constraints esistenti) ...
);

GO
EXECUTE sp_addextendedproperty @name = N'MS_Description',
@value = N'riferimento ordine cliente (opzionale)',
@level0type = N'SCHEMA', @level0name = N'inv',
@level1type = N'TABLE', @level1name = N'invoices',
@level2type = N'COLUMN', @level2name = N'customerPurchaseOrder';
GO

Salva e fai build del progetto TrainingHub.Database per verificare che il .sqlproj compili.

2. Sincronizza il DB locale

Regola di progetto: prima di rigenerare i CRUD, la modifica DB deve essere già applicata allo schema locale (il generatore legge dallo schema vivo, non dai file .sql).

Opzioni per sincronizzare:

  • Publish DACPAC via Visual StudioTrainingHub.DatabasePublish → scegli connection string dev → Publish.
  • sqlpackage.exe da riga di comando:
    sqlpackage /Action:Publish \
    /SourceFile:"TrainingHub.Database/bin/Debug/TrainingHub.Database.dacpac" \
    /TargetConnectionString:"Server=...;Database=TrainingHub_Dev;Integrated Security=true;TrustServerCertificate=true"
  • Script manuale — estrai lo ALTER TABLE dal diff DACPAC e applicalo a mano (sconsigliato in dev).

Dopo l'apply, connetti SSMS al DB e verifica che inv.invoices abbia la nuova colonna.

3. Esegui la rigenerazione CRUD

Nel file di configurazione conf.json il campo viene incluso automaticamente se non è esplicitamente filtrato. Invoca il tool MCP 3sd-generator da Claude Code:

  • mcp__3sd-generator__generator_list_settings — elenca le configurazioni disponibili
  • mcp__3sd-generator__generator_run — esegui per inv.invoices

Dopo la rigenerazione controlla con git status: devono essere modificati i file auto-generati per Invoice:

modified: TrainingHub.BackOffice/Components/CRUD/inv/Invoice.razor
modified: TrainingHub.BackOffice/Components/CRUD/inv/Invoice.razor.tt.cs
modified: TrainingHub.BackOffice/Components/CRUD/inv/Forms/InvoiceForm.razor
modified: TrainingHub.BackOffice/Components/CRUD/inv/Forms/InvoiceForm.razor.tt.cs

I file *.razor.cs (code-behind custom) non devono cambiare.

4. Ritocca conf.json per visibilità / posizione

File: TrainingHub.BackOffice/Components/CRUD/inv/_conf/invoices.dxgrid.conf.json.

Tipicamente vuoi controllare:

  • Visibilità in grid — sezione Columns, proprietà Visible
  • Visibilità in form per insert/update — sezione Columns, proprietà FormInsertVisibility / FormUpdateVisibility (valori: editable | readonly | hidden)
  • Ordine in form — aggiungi il nome campo all'array FormOrder nel punto desiderato

Esempio: voglio il campo visibile in grid come colonna opzionale (nascosta di default) e editabile in form sempre:

{
"Columns": {
"customerPurchaseOrder": {
"Visible": false,
"FormInsertVisibility": "editable",
"FormUpdateVisibility": "editable"
}
},
"FormOrder": [
"issuerId",
"companyId",
"issueDate",
"customerPurchaseOrder",
"notes"
]
}

Dopo la modifica ri-esegui generator_run per applicare il nuovo posizionamento.

5. (Opzionale) logica custom nel code-behind

Se il nuovo campo richiede logica non generabile (es. validazione custom, pre-popolamento, lookup da altra entità), aggiungi il codice in:

  • Invoice.razor.cs per handler a livello grid
  • Forms/InvoiceForm.razor.cs per logica a livello form

Esempio di pre-popolamento al caricamento form:

// Forms/InvoiceForm.razor.cs (partial class)
public partial class InvoiceForm
{
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (isEditingNewRow && string.IsNullOrEmpty(dataItem.customerPurchaseOrder))
{
dataItem.customerPurchaseOrder = await GetNextOrderRefAsync();
}
}
}

6. Aggiorna XML FatturaPA (se il campo va inviato al SDI)

Se il campo deve comparire nell'XML trasmesso al SDI:

  1. Modifica IFatturaPaXmlGenerator.Generate per includere il tag appropriato (es. <DatiOrdineAcquisto> per i riferimenti ordine).
  2. Aggiungi test in TrainingHub.Tests/Services/Invoicing/FatturaPaXmlGeneratorTests.cs.
  3. Valida l'XML contro lo schema XSD FatturaPA 1.2.

7. Build e test

dotnet build TrainingHub.sln
dotnet test TrainingHub.Tests

Avvia la BackOffice e verifica manualmente:

  • Il campo è presente nel form di nuova fattura
  • Il valore viene salvato / riletto correttamente
  • Il wizard non è rotto (tutti i 5 step funzionano)
  • Se editato nell'XML: il file generato contiene il nuovo tag

8. Commit

Convenzione di progetto: commit separati per DB e rigenerazione.

# Commit 1 — schema DB
git add TrainingHub.Database/inv/Tables/invoices.sql
git commit -m "feat(db): add customerPurchaseOrder to inv.invoices"

# Commit 2 — conf.json
git add TrainingHub.BackOffice/Components/CRUD/inv/_conf/invoices.dxgrid.conf.json
git commit -m "feat(inv): show customerPurchaseOrder in invoice form"

# Commit 3 — regen
git add TrainingHub.BackOffice/Components/CRUD/inv/Invoice.razor \
TrainingHub.BackOffice/Components/CRUD/inv/Invoice.razor.tt.cs \
TrainingHub.BackOffice/Components/CRUD/inv/Forms/InvoiceForm.razor \
TrainingHub.BackOffice/Components/CRUD/inv/Forms/InvoiceForm.razor.tt.cs
git commit -m "regen inv.invoices"

# (Eventuale) Commit 4 — code-behind custom / XML generator
git add TrainingHub.BackOffice/Components/CRUD/inv/Forms/InvoiceForm.razor.cs
git commit -m "feat(inv): prefill customerPurchaseOrder on new invoice"

Commit piccoli e specifici facilitano review e rollback.

⚡ Casi particolari

  • Campo NOT NULL senza default. Alla publish del DACPAC SQL Server lancia errore se la tabella ha già righe. Aggiungi un DEFAULT ('...') o marca come NULL in prima battuta, poi stringi in una migration successiva.
  • FK su tabella di altro schema. Aggiungi il FK constraint nello stesso CREATE TABLE (o ALTER via post-deploy script). Assicurati che la tabella referenziata sia in una referenze project (reg, edu, ecc.) collegata al .sqlproj.
  • Rinominare un campo esistente. Non supportato direttamente. Crea il nuovo campo, migra i dati via post-deploy script, poi in un commit successivo rimuovi il vecchio campo. Due step per evitare perdita dati.
  • Campi con encoding particolare (JSON, XML). Usa NVARCHAR(MAX) e gestisci serializzazione lato C# (il generatore non crea validatori custom).
  • Effetti sul wizard fattura. Se il campo va nello step 1 del wizard, può richiedere aggiornamenti in InvoiceFormPopup.razor.cs/SaveInvoiceDraft se deve essere validato o inizializzato.

⚠️ Domande aperte

  • Script di sync DB automatizzato. Oggi la sincronizzazione DB pre-regen è manuale. Valutare script npm / PowerShell che incapsuli sqlpackage /Action:Publish con connection string da appsettings.Development.json.
  • Verifica post-regen automatica. Dopo generator_run non c'è un check automatico che tutti i file attesi siano stati aggiornati. Valutare un assert in CI che confronti git diff --stat con un elenco atteso.
  • Rollback rigenerazione. Se la regen sbaglia (es. config errata), git checkout <files> ripristina ma i file .razor.cs custom vanno conservati. Documentare procedura di rollback pulita.

🔗 Vedi anche

  • Panoramica dominio — mappa del dominio
  • Schema DB — tabelle e relazioni
  • Componenti UI — pattern CRUD e triade file
  • Servizi — logica InvoiceService e XML FatturaPA
  • CLAUDE.md (root repo) — regole di generazione CRUD e progetto DB