docs: update expired report spec after review (cache keys, orchestrator logic, chart signature)

This commit is contained in:
2026-03-21 10:21:20 +01:00
parent 71d6a4c32d
commit cdbdfeede1

View File

@@ -16,7 +16,7 @@ Implementare un secondo template di report PDF per certificati non più in quota
| Pagina | Renderer | Contenuto |
|--------|----------|-----------|
| 1 | `ExpiredAnagraficaSectionRenderer` | Titolo + Caratteristiche + Analisi |
| 1 | `ExpiredAnagraficaSectionRenderer` (nuovo) | Titolo + Caratteristiche + Analisi |
| 2 | `EventiSectionRenderer` (riusato) | Lista eventi (identica all'attuale) |
| 3 | `ChartSectionRenderer` (riusato) | Grafico performance (identico all'attuale) |
@@ -98,37 +98,42 @@ Nessuna altra modifica alle query o agli altri servizi dati.
Dopo il caricamento dei dati (`CertificateDataService`), leggere `data.Info.Stato` per scegliere il flusso:
```csharp
bool isExpired = data.Info.Stato != "Quotazione" && !string.IsNullOrEmpty(data.Info.Stato);
**Strategia isExpired — denylist:** il check usa `!= "Quotazione"` (denylist), così qualsiasi nuovo stato non previsto viene trattato come expired piuttosto che come attivo. Se in futuro si aggiungono stati con report differenti, si sostituirà con un allowlist esplicito.
```csharp
bool isExpired = !string.IsNullOrEmpty(data.Info.Stato) && data.Info.Stato != "Quotazione";
// Copre: "Revocato", "Scaduto", "Rimborsato" — e qualsiasi stato futuro non-attivo
List<PdfDocument> sections;
if (isExpired)
{
// Flusso expired: ExpiredAnagrafica + Eventi + Chart
sections = new List<PdfDocument>
{
_expiredAnagraficaRenderer.Render(data),
eventiRenderer.Render(data),
// chart (async, può essere null)
};
var eventiRenderer = _sectionRenderers.First(r => r.SectionName == "Eventi");
pdfSections.Add(_expiredAnagraficaRenderer.Render(data));
pdfSections.Add(eventiRenderer.Render(data));
// Chart: await _chartRenderer.RenderAsync(data.Info.Isin) — aggiunto se non null
}
else
{
// Flusso attuale: Anagrafica + Eventi + Scenario (condizionale) + Chart
// logica esistente invariata
// Flusso attuale: foreach _sectionRenderers.OrderBy(r => r.Order) — logica esistente invariata
}
```
**Come si ottiene EventiSectionRenderer nel flusso expired:** tramite `_sectionRenderers.First(r => r.SectionName == "Eventi")`. È già registrato come `IPdfSectionRenderer` e presente nella collection iniettata.
**Firma ChartRenderer:** `await _chartRenderer.RenderAsync(data.Info.Isin)` — stessa chiamata del flusso attivo, restituisce `PdfDocument?` (null se il grafico non è disponibile).
Il renderer `ExpiredAnagraficaSectionRenderer` è iniettato direttamente nell'orchestratore (non via `IEnumerable<IPdfSectionRenderer>`) per evitare che venga incluso nel ciclo del flusso normale.
### Cache — chiavi separate
| Scenario | Chiave cache |
|----------|-------------|
| Attivo, no branding | `{isin}` |
| Attivo, branding | `{isin}:branded` |
| Expired, no branding | `{isin}:expired` |
| Expired, branding | `{isin}:expired:branded` |
Le chiavi sono passate a `_cache.Get/Set` a livello orchestratore; `PdfCacheService.CacheKey()` aggiunge poi il prefisso `report_pdf_` internamente (comportamento invariato per entrambi i flussi).
| Scenario | Chiave passata all'orchestratore | Chiave effettiva in memoria |
|----------|----------------------------------|-----------------------------|
| Attivo, no branding | `{isin}` | `report_pdf_{isin}` |
| Attivo, branding | `{isin}:branded` | `report_pdf_{isin}:branded` |
| Expired, no branding | `{isin}:expired` | `report_pdf_{isin}:expired` |
| Expired, branding | `{isin}:expired:branded` | `report_pdf_{isin}:expired:branded` |
---