From 86c8c8b3f34889f3a469070c73258bcf59eb4de0 Mon Sep 17 00:00:00 2001 From: SmartRootsSrl Date: Mon, 8 Jun 2026 17:10:47 +0200 Subject: [PATCH] docs: add fund/ETF report design spec (layout C, endpoints, models) Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../specs/2026-06-08-fund-report-design.md | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-08-fund-report-design.md diff --git a/docs/superpowers/specs/2026-06-08-fund-report-design.md b/docs/superpowers/specs/2026-06-08-fund-report-design.md new file mode 100644 index 0000000..7d8e958 --- /dev/null +++ b/docs/superpowers/specs/2026-06-08-fund-report-design.md @@ -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 GetFundInfoAsync(string isin); +Task FindIsinByAliasIdAsync(string aliasId); // riusa stessa logica + +// IFundReportOrchestrator +Task GenerateReportAsync(string isin, bool showBranding = false); +``` + +--- + +## 7. Registrazioni Program.cs + +```csharp +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); // placeholder +builder.Services.AddScoped(); // 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)