diff --git a/docs/superpowers/plans/2026-03-24-eventi-righe-non-raggiungibili.md b/docs/superpowers/plans/2026-03-24-eventi-righe-non-raggiungibili.md new file mode 100644 index 0000000..62729bb --- /dev/null +++ b/docs/superpowers/plans/2026-03-24-eventi-righe-non-raggiungibili.md @@ -0,0 +1,161 @@ +# Righe Non Raggiungibili in Lista Eventi — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Per i certificati non in quotazione, visualizzare in grigio dimmed le righe della Lista Eventi con Data Osservazione > Data Rimborso, per evidenziare che non sono più raggiungibili. + +**Architecture:** Aggiunta di un brush `GrayedOutBrush` in `PdfTheme.cs` e logica di confronto date in `EventiSectionRenderer.cs`. Il parse di `DataRimborso` avviene una volta prima del loop; per ogni riga si effettua il parse di `ObservationDate` e si applica il brush grigio se la riga è non raggiungibile. Nessuna modifica al modello dati né alle SP. + +**Tech Stack:** C# / ASP.NET Core 8, Syncfusion PDF v33, `System.Globalization.CultureInfo` + +**Spec:** `docs/superpowers/specs/2026-03-24-eventi-righe-non-raggiungibili-design.md` + +--- + +## File Map + +| File | Azione | Responsabilità | +|------|--------|---------------| +| `CertReports.Syncfusion/Helpers/PdfTheme.cs` | Modify | Aggiungere colore e brush `GrayedOut` | +| `CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs` | Modify | Logica confronto date + applicazione brush grigio | + +--- + +## Task 1: Aggiungere GrayedOutBrush in PdfTheme + +**Files:** +- Modify: `CertReports.Syncfusion/Helpers/PdfTheme.cs` + +- [ ] **Step 1: Aggiungere il colore GrayedOut nella sezione colori** + + Aprire `PdfTheme.cs`. Trovare il blocco "Nuovi colori stile Ibrido elegante" (attorno a riga 27). + Aggiungere dopo la riga con `TableAltRow`: + + ```csharp + public static readonly Color GrayedOut = Color.FromArgb(255, 170, 170, 170); // #AAAAAA + ``` + + > `Color` (non `PdfColor`) è il tipo corretto: tutti i colori esistenti in `PdfTheme.cs` usano già `Color` (es. riga 16: `public static readonly Color HeaderBackground = ...`). + +- [ ] **Step 2: Aggiungere il brush corrispondente nella sezione brush** + + Trovare il blocco dei brush statici (cercare `AccentBlueBrush`, `NegativeRedBrush`, ecc.). + Aggiungere nella stessa sezione: + + ```csharp + public static readonly PdfBrush GrayedOutBrush = new PdfSolidBrush(GrayedOut); + ``` + + > Nota: `PdfSolidBrush` non è IDisposable in Syncfusion v33 — non usare `using`. + +- [ ] **Step 3: Build per verificare nessun errore** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: `Build succeeded. 0 Error(s)` + +- [ ] **Step 4: Commit** + + ```bash + git add CertReports.Syncfusion/Helpers/PdfTheme.cs + git commit -m "feat: add GrayedOutBrush to PdfTheme for dimmed event rows" + ``` + +--- + +## Task 2: Logica righe non raggiungibili in EventiSectionRenderer + +**Files:** +- Modify: `CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs` + +- [ ] **Step 1: Aggiungere using System.Globalization** + + In cima al file `EventiSectionRenderer.cs`, aggiungere dopo gli altri `using`: + + ```csharp + using System.Globalization; + ``` + +- [ ] **Step 2: Parse DataRimborso prima del loop righe** + + Nel metodo `Render()`, trovare il commento `// Righe dati` (attorno alla riga 90, prima di `for (int i = 0; ...)`). + Aggiungere immediatamente prima del `for`: + + ```csharp + // Pre-parse DataRimborso per confronto righe non raggiungibili (solo expired) + DateTime? rimborsoDate = null; + if (isExpired && !string.IsNullOrEmpty(data.Info.DataRimborso) && + DateTime.TryParseExact(data.Info.DataRimborso, "dd/MM/yyyy", + CultureInfo.InvariantCulture, DateTimeStyles.None, out var rd)) + { + rimborsoDate = rd; + } + ``` + +- [ ] **Step 3: Applicare il brush grigio sulle righe non raggiungibili** + + Sempre nel loop righe, trovare il blocco dell'evidenziazione verde "SI" (attorno a riga 134): + + ```csharp + // Evidenzia "SI" nella colonna Pagato + if (evt.Paid == "SI") + row.Cells[paidColIndex].Style.TextBrush = PdfTheme.PositiveBrush as PdfBrush; + ``` + + Aggiungere **dopo** questo blocco (non prima — il grigio deve sovrascrivere il verde): + + ```csharp + // Righe non raggiungibili: testo grigio dimmed (ObservationDate > DataRimborso) + if (rimborsoDate.HasValue && + DateTime.TryParseExact(evt.ObservationDate, "dd/MM/yyyy", + CultureInfo.InvariantCulture, DateTimeStyles.None, out var obsDate) && + obsDate > rimborsoDate.Value) + { + foreach (var cell in row.Cells.OfType()) + cell.Style.TextBrush = PdfTheme.GrayedOutBrush; + } + ``` + + > Il grigio va applicato **dopo** l'evidenziazione verde così la sovrascrive. Il cast `as PdfBrush` non serve perché `GrayedOutBrush` è già dichiarato come `PdfBrush` in `PdfTheme`. + +- [ ] **Step 4: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: `Build succeeded. 0 Error(s)` + +- [ ] **Step 5: Test manuale** + + Avviare il progetto e richiedere un report per un certificato non in quotazione (Stato = "Scaduto"/"Rimborsato"/"Revocato") che abbia una `DataRimborso` valorizzata e almeno una riga eventi con `ObservationDate > DataRimborso`. + + ```bash + dotnet run --project CertReports.Syncfusion + # GET http://localhost:{porta}/api/report/by-isin/{ISIN_SCADUTO} + ``` + + Verificare nel PDF generato: + - Le righe con `ObservationDate <= DataRimborso` → testo nero normale + - Le righe con `ObservationDate > DataRimborso` → testo grigio `#AAAAAA` + - Il pattern zebra (sfondo bianco/azzurro) rimane invariato su tutte le righe + - Le righe raggiungibili (`ObservationDate <= DataRimborso`) con `Paid = "SI"` mantengono il testo verde — il grigio si applica solo alle righe non raggiungibili, dove il verde viene sovrascritto + +- [ ] **Step 6: Commit** + + ```bash + git add CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs + git commit -m "feat: dim unreachable event rows in expired certificate reports" + ``` + +--- + +## Edge Cases (già gestiti dall'implementazione) + +| Caso | Comportamento atteso | +|------|---------------------| +| `DataRimborso` vuota | `rimborsoDate` resta `null`, nessuna riga dimmed | +| `DataRimborso` non parseable | stessa cosa | +| `ObservationDate` non parseable | `TryParseExact` ritorna `false`, quella riga non viene dimmed | +| `ObservationDate == DataRimborso` | `obsDate > rimborsoDate` è `false` → non dimmed | +| Certificato in quotazione (`!isExpired`) | `rimborsoDate` non viene mai inizializzato (condizione `isExpired`) |