docs: add fund/ETF report design spec (layout C, endpoints, models)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 17:10:47 +02:00
parent 457800160e
commit 86c8c8b3f3

View File

@@ -0,0 +1,243 @@
# 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](https://www.smart-roots.net)" — 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`
```csharp
// 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
```csharp
// 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
```csharp
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)