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:
243
docs/superpowers/specs/2026-06-08-fund-report-design.md
Normal file
243
docs/superpowers/specs/2026-06-08-fund-report-design.md
Normal 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)
|
||||
Reference in New Issue
Block a user