Files
SmartReports/CLAUDE.md

8.1 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project

SmartReports / CertReports.Syncfusion — ASP.NET Core 8 REST API che genera PDF di certificati finanziari strutturati, sostituendo il vecchio progetto WebForms DevExpress. Usa Syncfusion PDF (Community License) e SkiaSharp per i grafici.

Progetto unico nella solution: CertReports.Syncfusion/.

Build & Run

# Restore + build
dotnet restore
dotnet build

# Run locale → https://localhost:{porta}/api/report/by-isin/{ISIN}
dotnet run --project CertReports.Syncfusion

# Docker
docker-compose up --build
# → http://localhost:5080/api/report/by-isin/{ISIN}

Non esistono test automatici al momento.

Architettura

Il flusso di generazione è orchestrato da ReportOrchestrator, che sceglie il template in base al campo Stato del certificato:

HTTP (ISIN) → ReportController → ReportOrchestrator
    ├── CertificateDataService  (4 SP → anagrafica, sottostanti, eventi, scenario)
    │
    ├── [Stato == "Quotazione"] — report certificati in quotazione (4 sezioni)
    │   ├── AnagraficaSectionRenderer       → PdfDocument (Sezione 1)
    │   ├── EventiSectionRenderer           → PdfDocument (Sezione 2)
    │   ├── ScenarioSectionRenderer         → PdfDocument (Sezione 3, opzionale)
    │   └── ChartSectionRenderer            → PdfDocument (Sezione 4)
    │
    ├── [Stato != "Quotazione"] — report certificati non in quotazione (3 sezioni)
    │   ├── ExpiredAnagraficaSectionRenderer → PdfDocument (Sezione 1)
    │   ├── EventiSectionRenderer            → PdfDocument (Sezione 2, colonne adattate)
    │   └── ChartSectionRenderer             → PdfDocument (Sezione 3)
    │
    ├── ChartDataService       (3 SP → dati grafico)
    ├── SkiaChartRenderer      → PNG in memoria
    └── PdfMergerService       → byte[] → Response

Stato certificato: il campo Stato viene dalla SP rpt_Master_CFT_ISIN. Valori: "Quotazione" (report attivo), "Scaduto" / "Rimborsato" / "Revocato" (report expired). Se Stato è vuoto, viene usato il flusso attivo come fallback.

Aggiungere una nuova sezione PDF al flusso attivo: implementare IPdfSectionRenderer, impostare SectionName e Order, registrare in Program.cs come AddScoped<IPdfSectionRenderer, NuovaSezione>(). L'orchestratore la include automaticamente ordinandola per Order.

ExpiredAnagraficaSectionRenderer: registrata come AddScoped<ExpiredAnagraficaSectionRenderer>() (senza interfaccia) e iniettata direttamente nell'orchestratore — non partecipa al ciclo IEnumerable<IPdfSectionRenderer>.

Tema: tutto in PdfTheme.cs — colori, font, layout, brush/pen. Modificare lì per aggiornare tutti i renderer.

Palette colori principale (stile "Ibrido elegante"):

  • AccentBlue #1565C0 — titoli, header tabelle, valori chiave
  • NegativeRed #CC0000 — valori negativi (performance, rendimento)
  • PositiveGreen #2E7D32 — valori positivi (performance)
  • Brush corrispondenti: AccentBlueBrush, NegativeRedBrush, PositiveGreenBrush, TableHeaderBrush (alias di AccentBlueBrush)

Configurazione chiave

  • appsettings.json: connection string (CertDb), Syncfusion license key, CryptoSettings passphrase, cache TTL
  • appsettings.Development.json: non deve contenere ConnectionStrings, altrimenti sovrascrive appsettings.json
  • Connection string: usare Data Source + Initial Catalog + Encrypt=False; per Microsoft.Data.SqlClient v5
  • Se la connessione fallisce via Named Pipes, aggiungere prefisso tcp: all'indirizzo IP

Gotcha Syncfusion v33 & SkiaSharp

Problema Soluzione
Color(r,g,b) rimosso Color.FromArgb(255, r, g, b)
Syncfusion.Drawing.Net.Core non esiste Inglobato in Syncfusion.Pdf.Net.Core, rimuovere dal .csproj
PdfStandardFont non è IDisposable Non usare using sulla dichiarazione
grid.Headers non iterabile con foreach for (int r = 0; r < grid.Headers.Count; r++)
PdfTextWebLink non trovato Aggiungere using Syncfusion.Pdf.Interactive;
RectangleF non supporta named arguments new RectangleF(x, y, w, h) — no startY: o simili
grid.Draw() restituisce void Stimare altezza con (rows + 1) * RowHeight
PdfMergerService: stream chiuso prima del Save() Tenere tutti gli stream aperti fino al salvataggio finale, poi cleanup in finally
SkiaSharp DrawText obsoleto SKFont + canvas.DrawText(text, x, y, SKTextAlign, font, paint)
Namespace conflict CertReports.Syncfusion.Pdf Aggiungere using Syncfusion.Pdf; esplicito nel file

Database

Tutte le stored procedure sono su FirstSolutionDB. I dati tornano già formattati come stringhe dalle SP (es. FORMAT(value,'P2','it-IT')): i modelli C# usano string per questi campi. Solo NominalValue, PrezzoEmissione, CpnPagati, CpnDaPagare, CpnInMemoria sono decimal? perché servono come valori numerici nel rendering.

API Endpoints

  • GET /api/report?p={isin_cifrato} / ?alias={id} / /by-isin/{isin} — PDF inline
  • GET /api/report/download?p={...} / ?alias={...} — PDF come allegato
  • Tutti gli endpoint report accettano ?branding=true (default false) per abilitare il footer "Powered by Smart Roots"
  • Tutti gli endpoint report accettano ?dividend=true (default false) per aggiungere la pagina Sottostanti+Dividendi
  • Tutti gli endpoint report accettano ?natixis=true (default false) per mostrare info.Nome nel box Tipologia invece di info.Categoria
  • GET /api/chart/{isin}[?format=png|pdf&width=&height=] — grafico standalone
  • GET /health — health check DB + chart service

Il parametro ?branding=true aggiunge al footer di ogni pagina del report:

  • Sinistra: "Powered by " + hyperlink PDF cliccabile "Smart Roots"https://www.smart-roots.net
  • Destra: numero pagina

Con branding=false (default): solo numero pagina centrato.

La cache usa chiavi separate: {isin}:branded vs {isin}. Il flag è propagato come CertificateReportData.ShowBranding e passato a PdfTheme.DrawFooter(g, pageWidth, pageHeight, pageNumber, showBranding). L'hyperlink usa PdfTextWebLink (richiede using Syncfusion.Pdf.Interactive;).

Parametri query report

Tutti i flag booleani hanno default false e seguono lo stesso pattern di propagazione: ReportControllerIReportOrchestrator.GenerateReportAsyncCertificateReportData → renderer.

Parametro Flag su CertificateReportData Effetto
?branding=true ShowBranding Footer con "Powered by Smart Roots" + link
?dividend=true ShowDividend Aggiunge pagina Sottostanti+Dividendi; nasconde tabella Sottostanti dalla Sezione 1
?natixis=true ShowNatixis Box Tipologia mostra info.Nome invece di info.Categoria

Cache key pattern (Approccio A — suffissi concatenati):

  • Suffissi: :branded, :dividend, :natixis — aggiunti in questo ordine se il flag è true
  • Chiave base: {isin}[suffissi]
  • Chiave expired: {isin}:expired[suffissi]
  • Esempio: ?branding=true&natixis=true → chiave {isin}:branded:natixis

Sezione 1 — AnagraficaSectionRenderer

Struttura a 3 sezioni verticali in una singola pagina A4:

Sezione Contenuto
Titolo "Scheda Prodotto {ISIN}" blu + Bid/Ask a destra + linea separatrice blu
A — Caratteristiche Prodotto Tabella emittente a sinistra (ISIN, Mercato, Valuta, Date, Autocall) + tabella cedole a destra (CpnPagati/DaPagare/InMemoria, RendimentoAttuale)
B — Analisi 9 KV a sinistra + 9 KV a destra. Leva/Memoria/FattoreAirbag/TriggerOneStar mostrati come "—" anche se vuoti
C — Sottostanti PdfGrid 9 colonne: Nome, Strike, Last, % Perf., Barr.K, Buffer K, Trig.CPN, Buf.CPN, Trig.AC (Dist.AC rimossa)

Se la tabella Sottostanti non entra (y > PageH-80pt), si crea una nuova pagina. Il footer viene disegnato su ogni pagina con PdfTheme.DrawFooter.