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

9.2 KiB
Raw Permalink 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

SP: sfih_GetChartPrices @ISIN VARCHAR(20)
Restituisce Px_Close (decimal) e Px_Date (date) ordinati ASC, solo valori non NULL.

FundChartRenderer (pagina 2 nel PDF report):

  • Singola linea nera sul close price storico
  • Asse X: date (intervalli mensili adattivi come chart V2)
  • Asse Y: scala automatica su min/max dei prezzi
  • Titolo: "Andamento Prezzo — {str}" + sottotitolo con valuta
  • Se la SP restituisce < 2 punti, la pagina mostra un messaggio "Dati insufficienti"

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

  • Stessa logica di rendering, output PNG/JPEG/PDF
  • Supporta ?format=png|jpg|jpeg|pdf, ?width=, ?height=, ?save=true
  • ?save=true salva JPEG in ChartSettings:SavePath\{isin}_fund.jpg
  • Nessuna logica WorstOf/barriere/label complesse (renderer dedicato, non V2)

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)

  • Parametri ?natixis, ?dividend — non applicabili ai fondi
  • Test automatici (nessun test nel progetto al momento)