Files
SmartReports/docs/superpowers/specs/2026-06-08-fund-report-design.md
2026-06-08 17:10:47 +02:00

9.1 KiB
Raw Blame History

Design Spec — Report ETF/Fondi

Data: 2026-06-08
Scope: Nuovo report PDF a 2 pagine per ETF/fondi, con endpoint dedicati /api/report/fund/ e /api/chart/fund/. Indipendente dal flusso certificati.


1. Endpoints

Report PDF

Metodo Route Comportamento
GET /api/report/fund/by-isin/{isin} PDF inline
GET /api/report/fund?p={isin_cifrato} PDF inline (ISIN cifrato, stesso CryptoHelper)
GET /api/report/fund?alias={id} PDF inline (alias → ISIN via FindIsinByAliasIdAsync)
GET /api/report/fund/download?p={...} PDF come allegato (Content-Disposition: attachment)

Grafico standalone

Metodo Route Comportamento
GET /api/chart/fund/{isin} PNG/PDF inline (vedi §4)

Parametri opzionali (tutti gli endpoint report)

Parametro Default Effetto
?branding=true false Footer "Powered by Smart Roots" — riusa PdfTheme.DrawFooter già esistente

Parametri opzionali (endpoint chart)

Parametro Default Effetto
?format=png|pdf|jpg|jpeg png Formato output
?save=true false Salva JPEG su disco (ChartSettings:SavePath)

2. Architettura

HTTP → FundReportController
    ├── FundDataService          sfih_GetOptDettagli @ISIN → FundInfo
    ├── FundReportOrchestrator
    │   ├── FundAnagraficaRenderer   → PdfDocument (Pagina 1)
    │   ├── FundChartRenderer        → PdfDocument (Pagina 2, placeholder)
    │   └── PdfMergerService         → byte[] (riusato)
    └── PdfCacheService              → cache chiave "fund:{isin}[:branded]"

HTTP → FundChartController
    ├── FundChartDataService     SP da definire → FundChartData
    └── FundSkiaChartRenderer    → PNG/JPEG/PDF

Riuso componenti esistenti: PdfMergerService, PdfCacheService, PdfTheme (incluso DrawFooter), CryptoHelper.

Nessuna modifica ai controller/renderer/service certificati esistenti.


3. Pagina 1 — Anagrafica (Layout C)

Struttura visiva

┌─────────────────────────────────────────────────────────┐
│  [BLUE BAR] {typ} — {str} — {isn}                       │
├──────────────────────────────────────────────────────────┤
│  [RANK strip]  rnk  │  [PREZZO strip]  prz val  │  dpz  │
├──────────────────────────────────────────────────────────┤
│  Dati Anagrafici  │  ESG Score  │  Perf·Vol·R/R grid    │
│  (flex 1.3)       │  (flex 0.9) │  (flex 1.4)           │
└──────────────────────────────────────────────────────────┘
│  footer: [Powered by Smart Roots]?    pagina N          │

Mapping campi SP → sezioni

Titolo:

  • {typ} — {str} — {isn} (es. ETF — WisdomTree … — IE00BDVPNG13)

Strip Rank/Prezzo:

  • Rank: rnk
  • Prezzo: prz + val
  • Data aggiornamento: dpz (formato dd/MM/yyyy)

Dati Anagrafici (tabella label/valore):

Label Campo SP
Società soc
Categoria MS msc
Tipo typ
Valuta val
Hedged hed (→ "sì" / "no")
Benchmark bmk
Spese correnti spc (→ formato 0.##%)
Catalogo itr
Proventi prv
Data lancio daf (formato dd/MM/yyyy)
Patrimonio pat (formato #,##0 EUR)

ESG Score (4 card verdi verticali):

  • Sustainability: sus
  • Environmental: env
  • Social: ssc
  • Governance: gov
  • Se un singolo valore è 0 (AMC/fallback), viene mostrato come "—". La sezione ESG è sempre visibile.

Griglia Performance/Volatilità/RendRisk (3×2):

Periodo Perf Vol R/R
3 Mesi p3M v3M r3M
6 Mesi p6M v6M r6M
Da inizio anno pYD vYD rYD
1 Anno p1Y v1Y r1Y
3 Anni p3Y v3Y r3Y
5 Anni p5Y v5Y r5Y

Colori: Perf positiva → PositiveGreenBrush, negativa → NegativeRedBrush, Vol e R/R → testo normale.


4. Pagina 2 — Grafico (placeholder)

Il rendering della pagina 2 è un placeholder in questa fase. La SP del grafico verrà fornita in seguito.

FundChartRenderer restituisce per ora una pagina con il testo "Grafico non disponibile" oppure viene omessa se la SP non è configurata. La struttura è pronta per accogliere la SP senza modifiche all'orchestratore.

FundSkiaChartRenderer (endpoint /api/chart/fund/{isin}):

  • Singola linea: close price storico (Px_Close da SP da definire)
  • Supporta ?format=png|jpg|jpeg|pdf e ?save=true
  • Nessuna logica WorstOf/barriere/label complesse

5. Modelli

File: Models/FundModels.cs

// Mapping 1:1 con result set di sfih_GetOptDettagli
public class FundInfo
{
    public string Isin { get; set; }        // isn
    public string Strumento { get; set; }   // str
    public string Tipo { get; set; }        // typ
    public string Societa { get; set; }     // soc
    public string CategoriaMorningstar { get; set; } // msc
    public string Valuta { get; set; }      // val
    public string Hedged { get; set; }      // hed
    public string Benchmark { get; set; }   // bmk
    public string Catalogo { get; set; }    // itr
    public string Proventi { get; set; }    // prv
    public DateTime? DataLancio { get; set; } // daf
    public decimal? Patrimonio { get; set; }  // pat
    public decimal? SpeseCorrenti { get; set; } // spc
    public decimal? Prezzo { get; set; }    // prz
    public DateTime? DataPrezzo { get; set; } // dpz
    public decimal? Rank { get; set; }      // rnk
    // Performance
    public decimal? P3M { get; set; } public decimal? P6M { get; set; }
    public decimal? PYD { get; set; } public decimal? P1Y { get; set; }
    public decimal? P3Y { get; set; } public decimal? P5Y { get; set; }
    // Volatilità
    public decimal? V3M { get; set; } public decimal? V6M { get; set; }
    public decimal? VYD { get; set; } public decimal? V1Y { get; set; }
    public decimal? V3Y { get; set; } public decimal? V5Y { get; set; }
    // RendRisk
    public decimal? R3M { get; set; } public decimal? R6M { get; set; }
    public decimal? RYD { get; set; } public decimal? R1Y { get; set; }
    public decimal? R3Y { get; set; } public decimal? R5Y { get; set; }
    // ESG
    public decimal? Sustainability { get; set; } // sus
    public decimal? Environmental { get; set; }  // env
    public decimal? Social { get; set; }         // ssc
    public decimal? Governance { get; set; }     // gov
}

public class FundReportData
{
    public FundInfo Info { get; set; }
    public bool ShowBranding { get; set; }
}

6. Interfacce

// IFundDataService
Task<FundInfo?> GetFundInfoAsync(string isin);
Task<string?> FindIsinByAliasIdAsync(string aliasId); // riusa stessa logica

// IFundReportOrchestrator
Task<byte[]> GenerateReportAsync(string isin, bool showBranding = false);

7. Registrazioni Program.cs

builder.Services.AddScoped<IFundDataService, FundDataService>();
builder.Services.AddScoped<IFundReportOrchestrator, FundReportOrchestrator>();
builder.Services.AddScoped<FundAnagraficaRenderer>();
builder.Services.AddScoped<FundChartRenderer>();      // placeholder
builder.Services.AddScoped<FundSkiaChartRenderer>();  // per /api/chart/fund/

8. Cache

Chiave pattern: fund:{isin} + suffisso :branded se showBranding=true.
Riusa il PdfCacheService esistente (stesso TTL da appsettings.json).


9. File da creare

File Descrizione
Models/FundModels.cs FundInfo, FundReportData, FundChartData (placeholder)
Services/Interfaces/IFundServices.cs IFundDataService, IFundReportOrchestrator
Services/Implementations/FundDataService.cs Chiama sfih_GetOptDettagli
Services/Implementations/FundAnagraficaRenderer.cs Pagina 1 layout C
Services/Implementations/FundChartRenderer.cs Pagina 2 placeholder
Services/Implementations/FundReportOrchestrator.cs Coordina anagrafica + chart + merge + cache
Services/Implementations/FundSkiaChartRenderer.cs Rendering grafico (placeholder, SP da aggiungere)
Controllers/FundReportController.cs 4 endpoint /api/report/fund/
Controllers/FundChartController.cs 1 endpoint /api/chart/fund/

10. Fuori scope (questa iterazione)

  • SP grafico: FundChartDataService e FundSkiaChartRenderer rimangono placeholder fino alla fornitura della SP
  • Parametri ?natixis, ?dividend — non applicabili ai fondi
  • Test automatici (nessun test nel progetto al momento)