Dominio inv β Panoramica sviluppatore
π― Cosa faβ
Il dominio inv gestisce la fatturazione elettronica FatturaPA:
emissione, trasmissione via Aruba al Sistema di Interscambio,
tracciamento stati, piano rate e convenzioni commerciali.
πΊοΈ Mappa moduliβ
Database β TrainingHub.Database/inv/β
| File | Ruolo |
|---|---|
Tables/issuers.sql | Emittenti (cedente/prestatore + credenziali Aruba) |
Tables/bankAccounts.sql | Anagrafica conti bancari per emittente |
Tables/invoices.sql | Intestazione fattura |
Tables/invoiceLines.sql | Righe di dettaglio |
Tables/invoicePayments.sql | Piano rate |
Tables/invoiceAppointments.sql | Associazione N:N fattura β appuntamenti formativi |
Tables/invoiceStatusHistory.sql | Storico cambi di stato |
Tables/conventions.sql | Convenzioni (sconto per categoria corso) |
Tables/companyConventions.sql | Associazione N:N azienda β convenzione |
Tables/vatCodes.sql | Codici IVA riutilizzabili |
Tables/paymentMethods.sql | Lookup codici metodo pagamento SDI (MP01βMP23) |
Tables/documentTypes.sql | Lookup codici tipo documento SDI (TD01βTD28) |
Tables/invoiceCounters.sql | Numerazioni progressive per (issuerId, documentTypeCode, year) |
Tables/invoiceDocuments.sql | Allegati della fattura |
Tables/reminders.sql | Promemoria fatturabili (sorgente di riga) |
Tables/companyPaymentTerms.sql | Piani di pagamento per azienda, con default globale (companyId IS NULL) |
Functions/fn_activeConventions.sql | TVF: convenzioni attive per (companyId, issueDate) |
Views/vw_invoicesData.sql | Vista dati fatture (CRUD generato) |
Views/vw_billableItems.sql, vw_billableAppointments.sql, vw_billableReminders.sql | Il fatturabile, con pagine dedicate |
In tutto: 16 tabelle, 4 viste e 1 funzione.
Service layer β TrainingHub.BackOffice/Services/Invoicing/β
23 file. I principali:
| File | Ruolo |
|---|---|
IInvoiceService.cs / InvoiceService.cs | Orchestrazione fattura + integrazione Aruba |
IInvoiceBillingService.cs / InvoiceBillingService.cs | Righe da appuntamenti e promemoria |
IArubaSdiService.cs / ArubaSdiService.cs | HTTP verso Aruba, con retry |
IFatturaPaXmlGenerator.cs / FatturaPaXmlGenerator.cs | XML FatturaPA 1.2 |
PaymentTermsService.cs + PaymentTermsCalculator.cs | Piani di pagamento |
InvoiceCourtesyCopyService.cs | Copia di cortesia |
InvoiceStatusRefreshWorker.cs | Refresh stati ogni 10 minuti |
Elenco completo in Servizi.
UI CRUD β TrainingHub.BackOffice/Components/CRUD/inv/β
19 pagine CRUD auto-generate. Pattern per ciascuna:
<Entity>.razor+.razor.tt.cs(grid autogenerata)<Entity>.razor.cs(code-behind custom)Forms/<Entity>Form.razor+.razor.cs+.razor.tt.csFormPopups/<Entity>FormPopup.razor+.razor.cs+.razor.tt.cs
EntitΓ presenti:
Invoice(con wizard a 5 step inFormPopups/InvoiceFormPopup.razor+ i partialInvoiceFormPopup.razor.Step*.cs)InvoiceLine,InvoicePayment,InvoiceAppointment,InvoiceStatusHistory,InvoiceDocument,InvoiceCounterReminder,CompanyPaymentTermIssuer,VatCode,PaymentMethod,DocumentTypeBankAccountConvention,CompanyConvention- Generati su vista:
InvoicesData,BillableItem,BillableAppointment,BillableReminder
Configurazione CRUD β _conf/*.dxgrid.conf.jsonβ
Un file JSON per entitΓ che guida la generazione. Modificare qui per
cambi persistenti; i file .razor e .razor.tt.cs vengono rigenerati
dal tool MCP 3sd-generator.
Test β TrainingHub.UnitTests/Services/Invoicing/β
21 file di test sull'area. Fra i principali: InvoiceServiceTests.cs
(numerazione, invio, storico, piano rate),
InvoiceStatusExtensionsTests.cs (mapping notifiche SDI),
ArubaSdiServiceRetryTests.cs e ArubaSdiServiceRateLimitTests.cs
(retry e 429), PaymentTermsServiceTests.cs,
InvoiceCourtesyCopyServiceTests.cs,
InvoiceStatusRefreshWorkerTests.cs.
π§ API pubblica β IInvoiceServiceβ
Task<invoice> AssignNumberAsync(Guid invoiceId, CancellationToken ct);
Task<string> GeneratePreviewXmlAsync(Guid invoiceId, CancellationToken ct = default);
Task SendToArubaAsync(Guid invoiceId, CancellationToken ct);
Task RefreshStatusFromArubaAsync(Guid invoiceId, CancellationToken ct);
Task AddStatusHistoryAsync(Guid invoiceId, InvoiceStatus status, string? arubaStatus, string? notes, string? createdBy, CancellationToken ct);
Task<IEnumerable<invoiceStatusHistory>> GetStatusHistoryAsync(Guid invoiceId, CancellationToken ct);
Task<IEnumerable<invoicePayment>> GetPaymentsAsync(Guid invoiceId, CancellationToken ct);
Task SavePaymentsAsync(Guid invoiceId, IEnumerable<invoicePayment> payments, CancellationToken ct);
Registrato in Program.cs nella sezione DI.
π§© Pattern chiaveβ
Wizard fatturaβ
Il form di edit della fattura Γ¨ un wizard a 5 step realizzato in
FormPopups/InvoiceFormPopup.razor + .razor.cs, usando
Tabiot.Blazor.Wizard.Wizard. Sequenza step e hook OnStepChange:
| Step | FormId | Titolo | Hook |
|---|---|---|---|
| 1 | invoice_data | Dati fattura | SaveInvoiceDraft |
| 2 | invoice_lines | Righe fattura | sorgenti di fatturazione aggiunte da popup (appuntamenti, promemoria) |
| 3 | payments | Pagamenti | SavePayments (valida somma = 100%) |
| 4 | summary | Riepilogo | LoadSummary (totali + storico + pulsanti SDI) |
| 5 | documents | Documenti | allegati della fattura |
Generazione CRUDβ
I file .razor e .razor.tt.cs sono rigenerati dal tool MCP
3sd-generator a partire dallo schema DB e dal conf.json. Modifiche
dirette in questi file vanno perse alla rigenerazione: per rendere
persistenti le modifiche aggiornare il conf.json e rigenerare.
Il code-behind .razor.cs contiene il codice custom e non viene
toccato dalla generazione.
Applicazione convenzioniβ
Al momento dell'emissione fattura, CreateLinesFromAppointments (in
InvoiceFormPopup.razor.cs) usa inv.fn_activeConventions(companyId, issueDate) per recuperare lo sconto attivo e pre-popolare la riga con
il discountPercentage corretto.
Numerazioneβ
La bozza non Γ¨ numerata. All'emissione (SendToArubaAsync), AssignNumberAsync
incrementa in transazione il contatore inv.invoiceCounters della serie
(issuerId, documentTypeCode, year) β serie dedicata al tipo documento se esiste,
altrimenti la serie di default (documentTypeCode NULL) condivisa da tutti i tipi.
Il code Γ¨ generato con un template Handlebars (templatingExpression, default
{{year}}/{{progressive}}); roll-over annuale con terminated, retry su collisione.
L'unicitΓ (issuerId, documentTypeCode, invoiceYear, invoiceNumber) Γ¨ garantita
dall'indice filtrato UQ_invoices_number (WHERE invoiceNumber IS NOT NULL).
π¦ Dipendenzeβ
Brighela.SimpleCRUD.Service.ISimpleCRUDServiceβ CRUD di baseOss.Filtersβ filtri su gridTabiot.Blazor.*β wizard, grid, popup, comboDevExpress.Blazorβ componenti UIMicrosoft.Extensions.Localization.IStringLocalizer<T>β etichette localizzate- Ecosistema 3SD: Scarnas, Brighela, Servel, Ploc, Tabiot, Mulet
β οΈ Domande aperte / debito tecnicoβ
- Credenziali Aruba in chiaro.
inv.issuers.arubaPasswordè memorizzata in chiaro. Decisione esplicita a design (back-office interno), ma valutare cifratura simmetrica con chiave da config per compliance futura. -
Job schedulato di refresh stato fatture inviate.Consegnato.Services/Invoicing/InvoiceStatusRefreshWorker.csΓ¨ unBackgroundServiceconInterval = TimeSpan.FromMinutes(10), registrato inProgram.cs:133(AddHostedService<β¦>()) e governato dal gate di configurazioneInvoiceStatusRefresh:Enabled. Il refresh manuale via pulsante resta come scorciatoia.
π Vedi ancheβ
- Schema DB β dettaglio tabelle, FK, indici
- Componenti UI β struttura Blazor CRUD
- Servizi β logica
InvoiceService - Aggiungere un campo β tutorial
- Guida utente: Panoramica fatturazione (docs-site-user)