Files
SmartReports/docs/superpowers/specs/2026-03-21-expired-certificate-report-design.md

7.5 KiB

Design Spec: Report Certificati Non in Quotazione (Expired)

Data: 2026-03-21 Progetto: SmartReports / CertReports.Syncfusion Autore: Brainstorming session


Obiettivo

Implementare un secondo template di report PDF per certificati non più in quotazione (Stato: Revocato, Scaduto, Rimborsato). Il report mantiene lo stesso stile grafico del report attivo ma mostra meno informazioni: 3 pagine totali (Anagrafica semplificata + Lista Eventi + Grafico). Niente Scenario. Il rilevamento del tipo è automatico tramite il campo Stato restituito dalla SP esistente rpt_Master_CFT_ISIN.


Struttura del Report Expired (3 pagine)

Pagina Renderer Contenuto
1 ExpiredAnagraficaSectionRenderer (nuovo) Titolo + Caratteristiche + Analisi
2 EventiSectionRenderer (riusato) Lista eventi (identica all'attuale)
3 ChartSectionRenderer (riusato) Grafico performance (identico all'attuale)

Modifiche al Modello

CertificateInfo — aggiungere un campo

public string Stato { get; set; } = string.Empty;
// Valori possibili: "Quotazione" | "Revocato" | "Scaduto" | "Rimborsato"

CertificateDataService — mappare il nuovo campo

Nel metodo che chiama rpt_Master_CFT_ISIN, aggiungere la mappatura:

Stato = reader["Stato"]?.ToString() ?? string.Empty

Nessuna altra modifica alle query o agli altri servizi dati.


Nuovo Renderer: ExpiredAnagraficaSectionRenderer

File: CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs Interfaccia: IPdfSectionRenderer Order: 1 SectionName: "ExpiredAnagrafica"

Layout Pagina 1 (Portrait A4)

Blocco Titolo

  • "Scheda Prodotto {ISIN}" — stesso stile del titolo attuale (font grande, colore AccentBlue)
  • Riga sotto: "Tipologia: {Categoria}" — font normale
  • Niente Bid/Ask, niente LastPriceDate
  • Linea separatrice orizzontale blu (come attuale)

Blocco Caratteristiche Prodotto (tabella sinistra, stessa grafica attuale)

Label Campo modello
EMITTENTE Info.Emittente
ISIN Info.Isin
Mercato Info.Mercato
Valuta Info.Valuta
Data Emissione Info.DataEmissione
Valore Rimborso Info.ValoreRimborso
Data Rimborso Info.DataRimborso

Blocco Analisi (KV, stessa grafica attuale)

Label Campo modello
Importo Cedola (p.a.) Info.NominalAnnualYield
Frequenza Cedola Info.FrequenzaCedole
Valore Nominale Info.NominalValue
Memoria Cedola Info.Memory
Tipo Barriera Info.BarrierType
Tipo Basket Info.BasketType
Rendimento Totale Info.RendimentoTotale

Escluso dalla pagina 1

  • Sezione Bid/Ask/LastPriceDate
  • Tabella Sottostanti (sezione C del report attivo)
  • Sezione cedole (CpnPagati/DaPagare/InMemoria)
  • Sezione Analisi (8+9 KV del report attivo)

PdfTheme.DrawFooter(g, pageWidth, pageHeight, pageNumber, showBranding) — identico a tutti gli altri renderer, supporta il parametro branding.


Modifiche all'Orchestratore

ReportOrchestrator.cs

Dopo il caricamento dei dati (CertificateDataService), leggere data.Info.Stato per scegliere il flusso:

Strategia isExpired — denylist: il check usa != "Quotazione" (denylist), così qualsiasi nuovo stato non previsto viene trattato come expired piuttosto che come attivo. Se in futuro si aggiungono stati con report differenti, si sostituirà con un allowlist esplicito.

bool isExpired = !string.IsNullOrEmpty(data.Info.Stato) && data.Info.Stato != "Quotazione";
// Copre: "Revocato", "Scaduto", "Rimborsato" — e qualsiasi stato futuro non-attivo

if (isExpired)
{
    // Flusso expired: ExpiredAnagrafica + Eventi + Chart
    var eventiRenderer = _sectionRenderers.First(r => r.SectionName == "Eventi");
    pdfSections.Add(_expiredAnagraficaRenderer.Render(data));
    pdfSections.Add(eventiRenderer.Render(data));
    // Chart: await _chartRenderer.RenderAsync(data.Info.Isin) — aggiunto se non null
}
else
{
    // Flusso attuale: foreach _sectionRenderers.OrderBy(r => r.Order) — logica esistente invariata
}

Come si ottiene EventiSectionRenderer nel flusso expired: tramite _sectionRenderers.First(r => r.SectionName == "Eventi"). È già registrato come IPdfSectionRenderer e presente nella collection iniettata.

Firma ChartRenderer: await _chartRenderer.RenderAsync(data.Info.Isin) — stessa chiamata del flusso attivo, restituisce PdfDocument? (null se il grafico non è disponibile).

Il renderer ExpiredAnagraficaSectionRenderer è iniettato direttamente nell'orchestratore (non via IEnumerable<IPdfSectionRenderer>) per evitare che venga incluso nel ciclo del flusso normale.

Cache — chiavi separate

Le chiavi sono passate a _cache.Get/Set a livello orchestratore; PdfCacheService.CacheKey() aggiunge poi il prefisso report_pdf_ internamente (comportamento invariato per entrambi i flussi).

Scenario Chiave passata all'orchestratore Chiave effettiva in memoria
Attivo, no branding {isin} report_pdf_{isin}
Attivo, branding {isin}:branded report_pdf_{isin}:branded
Expired, no branding {isin}:expired report_pdf_{isin}:expired
Expired, branding {isin}:expired:branded report_pdf_{isin}:expired:branded

Branding

Il parametro ?branding=true funziona identicamente al report attivo:

  • Propagato come CertificateReportData.ShowBranding
  • Passato a PdfTheme.DrawFooter(..., showBranding) in ogni pagina del report expired
  • Footer sinistra: "Powered by " + hyperlink "Smart Roots" → https://www.smart-roots.net
  • Footer destra: numero pagina
  • Cache usa chiave {isin}:expired:branded vs {isin}:expired

Dependency Injection — Program.cs

// Registrazione diretta (non come IPdfSectionRenderer) per evitare inclusione nel ciclo normale
builder.Services.AddScoped<ExpiredAnagraficaSectionRenderer>();

Gli altri renderer esistenti rimangono invariati.


Endpoint

Nessun nuovo endpoint. Il rilevamento del tipo report è trasparente al chiamante. Tutti gli endpoint esistenti funzionano per entrambi i tipi:

Endpoint Comportamento
GET /api/report?p={encrypted} Auto-rileva da Stato
GET /api/report?alias={id} Auto-rileva da Stato
GET /api/report/by-isin/{isin} Auto-rileva da Stato
GET /api/report/download?p={...} Auto-rileva da Stato
GET /api/report/download?alias={...} Auto-rileva da Stato
Tutti Supportano ?branding=true

File Modificati / Creati

File Tipo modifica
Models/CertificateModels.cs Aggiunta campo Stato a CertificateInfo
Services/Implementations/CertificateDataService.cs Mappatura campo Stato dalla SP
Services/Implementations/ExpiredAnagraficaSectionRenderer.cs Nuovo file
Services/Implementations/ReportOrchestrator.cs Logica branching expired/attivo + cache keys
Program.cs Registrazione ExpiredAnagraficaSectionRenderer

Non modificati

  • AnagraficaSectionRenderer.cs — invariato
  • EventiSectionRenderer.cs — riusato as-is
  • ScenarioSectionRenderer.cs — invariato (semplicemente non incluso nel flusso expired)
  • ChartSectionRenderer.cs — riusato as-is
  • PdfTheme.cs — invariato
  • ReportController.cs — invariato
  • Tutti gli endpoint — invariati