Passa al contenuto principale

Schema DB inv

🎯 Cosa fa​

Definisce il persistence layer della fatturazione: 11 tabelle e 1 funzione nel schema inv del progetto TrainingHub.Database. Deploy tramite DACPAC via pipeline Azure.

πŸ—ΊοΈ Tabelle​

TabellaRuolo
inv.issuersEmittente fattura (cedente/prestatore) con credenziali Aruba
inv.invoicesIntestazione fattura
inv.invoiceLinesRighe di dettaglio di una fattura
inv.invoicePaymentsPiano rate di una fattura
inv.invoiceAppointmentsAssociazione N:N fattura ↔ appuntamenti formativi
inv.invoiceStatusHistoryStorico cambi di stato di una fattura
inv.bankAccountsAnagrafica conti bancari per emittente
inv.vatCodesCodici IVA riutilizzabili (aliquota + natura)
inv.paymentMethodsLookup codici metodo di pagamento SDI (MP01–MP23)
inv.documentTypesLookup codici tipo documento SDI (TD01–TD28)
inv.conventionsConvenzioni commerciali (sconto per categoria corso)
inv.companyConventionsAssociazione N:N azienda ↔ convenzione

πŸ”— Relazioni​

Referenze esterne: reg.companies, reg.organizers, edu.categories, edu.appointments.

πŸ—‚οΈ Dettaglio tabelle​

inv.issuers​

Emittente fattura. Una ragione sociale 3SD con credenziali Aruba dedicate.

  • PK: id (GUID, default newid())
  • FK: organizerSlug β†’ reg.organizers(slug)
  • Campi chiave:
    • companyName, vatCode, fiscalCode, address, city, zipCode, province, country (default 'IT') β€” anagrafica legale
    • pec, reaOffice, reaNumber, shareCapital β€” extra FatturaPA
    • arubaUsername, arubaPassword (in chiaro), arubaEnvironment ('demo' | 'production', default 'demo')
  • Check: CHK_issuers_arubaEnvironment su arubaEnvironment

inv.invoices​

Intestazione fattura.

  • PK: id
  • FK: issuerId β†’ inv.issuers(id), companyId β†’ reg.companies(id), documentTypeCode β†’ inv.documentTypes(code)
  • Campi chiave:
    • documentTypeCode (NVARCHAR(8), default 'TD01') β€” codice SDI tipo documento (FatturaPA), es. TD01 fattura, TD04 nota di credito
    • code (es. A/2026/1), invoiceNumber (progressivo), invoiceYear
    • issueDate, status (string, non enum DB β€” mappato a InvoiceStatus lato C#)
    • taxableAmount, vatAmount, totalAmount
    • notes
    • Aruba: arubaTransmissionId, sdiProtocolNumber, arubaStatus, arubaLastCheckAt, arubaNotificationXml, sentXmlContent
    • Audit: createdAt (default getdate()), updatedAt
  • Unique: UQ_invoices_number(issuerId, invoiceYear, invoiceNumber) β€” garantisce unicitΓ  progressivo per emittente/anno

inv.invoiceLines​

Righe di dettaglio fattura.

  • PK: id
  • FK: invoiceId β†’ inv.invoices(id) ON DELETE CASCADE, vatCodeId β†’ inv.vatCodes(id)
  • Campi chiave:
    • lineNumber (progressivo da 1)
    • productServiceCode (opzionale), description, quantity, unitOfMeasure, unitPrice, discountPercentage
    • totalAmount (calcolato lato app: quantity * unitPrice * (1 βˆ’ discount/100))
  • Cascade: elimina riga se fattura eliminata

inv.invoicePayments​

Piano rate di pagamento.

  • PK: id
  • FK: invoiceId β†’ inv.invoices(id) ON DELETE CASCADE, paymentMethodCode β†’ inv.paymentMethods(code), bankAccountId β†’ inv.bankAccounts(id) (nullable)
  • Campi chiave:
    • lineNumber, paymentMethodCode (codice SDI), paymentDueDate, paymentDate (data ricezione, nullable), bankAccountId (nullable; obbligatorio solo se paymentMethods.ibanRequired = 1)
    • percentage (somma rate = 100%), amount (calcolato: totalAmount * percentage / 100)

inv.invoiceAppointments​

Associazione N:N tra fattura e appuntamenti formativi.

  • PK composita: (invoiceId, appointmentId)
  • FK: invoiceId β†’ inv.invoices(id) ON DELETE CASCADE, appointmentId β†’ edu.appointments(id)

inv.invoiceStatusHistory​

Storico cambi di stato di una fattura.

  • PK: id
  • FK: invoiceId β†’ inv.invoices(id) (no cascade β€” audit trail)
  • Campi chiave:
    • status, arubaStatus (nullable), notes, createdAt (default getdate()), createdBy

inv.bankAccounts​

Anagrafica conti bancari per emittente. Riferita da inv.invoicePayments.bankAccountId per il rendering del nodo <IBAN> nell'XML FatturaPA.

  • PK: id
  • FK: issuerId β†’ inv.issuers(id)
  • Campi chiave:
    • label (etichetta breve), iban (NVARCHAR(64), formato SEPA/IT validato lato C# da [ItalianIban]), bankName, bic (NVARCHAR(11)), accountHolder
    • active (bit, default 1) β€” soft-delete: se 0 il conto non Γ¨ selezionabile in nuove rate ma resta legato ai pagamenti storici
  • Indice: IX_bankAccounts_issuerId

inv.vatCodes​

Codici IVA riutilizzabili.

  • PK: id
  • Campi chiave: label, vatRate, vatNature (se aliquota = 0)

inv.paymentMethods​

Lookup dei codici metodo di pagamento SDI (FatturaPA, MP01–MP23). Riferito da inv.invoicePayments.paymentMethodCode.

  • PK: code (NVARCHAR(8), es. MP05)
  • Campi chiave:
    • label (es. Bonifico), description
    • ibanRequired (bit) β€” se 1, l'IBAN diventa obbligatorio sulla rata che usa questo metodo
    • active (bit, default 1) β€” flag soft-delete
  • Seed: Scripts/seed-inv-paymentMethods.sql (MERGE idempotente)

inv.documentTypes​

Lookup dei codici tipo documento SDI (FatturaPA, TD01–TD28). Riferito da inv.invoices.documentTypeCode.

  • PK: code (NVARCHAR(8), es. TD01)
  • Campi chiave:
    • label (es. Fattura, Nota di credito), description
    • active (bit, default 1) β€” flag soft-delete; i tipi non attivi non sono selezionabili in nuove fatture
  • Seed: Scripts/seed-inv-documentTypes.sql (MERGE idempotente)

inv.conventions​

Convenzioni commerciali.

  • PK: id
  • FK: categoryId β†’ edu.categories(id), organizerSlug β†’ reg.organizers(slug) (entrambe nullable)
  • Campi chiave:
    • label, discountPercentage
    • validFrom (default '1900-01-01'), validTo (nullable = illimitata)

inv.companyConventions​

Associazione N:N azienda ↔ convenzione, con override validitΓ  opzionale.

  • PK: id
  • FK: companyId β†’ reg.companies(id), conventionId β†’ inv.conventions(id)
  • Unique: UQ_companyConventions(companyId, conventionId)
  • Campi chiave: validFrom, validTo (entrambi nullable; NULL = usa le date della convenzione)

πŸ”§ Funzioni​

inv.fn_activeConventions(@companyId, @issueDate)​

TVF che restituisce le convenzioni attive per un'azienda a una data data. Verifica entrambe le validitΓ  (convenzione e associazione):

SELECT c.id AS conventionId, c.categoryId, c.discountPercentage
FROM inv.conventions c
INNER JOIN inv.companyConventions cc ON cc.conventionId = c.id
WHERE cc.companyId = @companyId
AND @issueDate >= c.validFrom
AND (c.validTo IS NULL OR @issueDate <= c.validTo)
AND (cc.validFrom IS NULL OR @issueDate >= cc.validFrom)
AND (cc.validTo IS NULL OR @issueDate <= cc.validTo);

Usata da InvoiceService / wizard fattura per applicare lo sconto in automatico.

πŸ“ File chiave​

  • TrainingHub.Database/inv/Tables/*.sql β€” tabelle
  • TrainingHub.Database/inv/Functions/fn_activeConventions.sql β€” funzione TVF
  • TrainingHub.Database/TrainingHub.Database.sqlproj β€” progetto SQL

⚠️ Debito tecnico​

  • invoices.status come stringa anzichΓ© FK a una lookup o CHECK esplicito: oggi validato solo lato C# con Enum.Parse. Valori errati scritti in DB via altri path non sono bloccati. Valutare check constraint o tabella di lookup.
  • arubaPassword in chiaro. Decisione esplicita a design (back- office interno con accesso controllato), ma da rivedere per compliance.
  • Indici espliciti minimi. Esiste unique su invoices.number, companyConventions(companyId, conventionId) e index su bankAccounts.issuerId. Valutare indici aggiuntivi su FK frequentemente usate in join (es. invoiceLines.invoiceId, invoicePayments.invoiceId, invoiceStatusHistory.invoiceId) se query lente emergono in profilo.

πŸ”— Vedi anche​