Compare commits

..

4 Commits

4 changed files with 277 additions and 0 deletions

View File

@@ -31,6 +31,7 @@ public static class PdfTheme
public static readonly Color SeparatorLine = Color.FromArgb(255, 221, 221, 221); // #DDDDDD
public static readonly Color TableBorder = Color.FromArgb(255, 187, 187, 187); // #BBBBBB
public static readonly Color TableAltRow = Color.FromArgb(255, 247, 249, 252); // #F7F9FC
public static readonly Color GrayedOut = Color.FromArgb(255, 170, 170, 170); // #AAAAAA
// TableHeaderBg is AccentBlue — use AccentBlueBrush / TableHeaderBrush directly
// Colori box header pagina 1
@@ -98,6 +99,7 @@ public static class PdfTheme
public static PdfBrush BoxGrayAccentBrush => new PdfSolidBrush(BoxGrayAccent);
public static PdfBrush BoxGrayLabelBrush => new PdfSolidBrush(BoxGrayLabel);
public static PdfBrush BoxGrayValueBrush => new PdfSolidBrush(BoxGrayValue);
public static PdfBrush GrayedOutBrush => new PdfSolidBrush(GrayedOut);
// ─── Utility ───────────────────────────────────────────────────────
public static PdfBrush ValueBrush(decimal? value)

View File

@@ -5,6 +5,7 @@ using Syncfusion.Drawing;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Grid;
using System.Globalization;
namespace CertReports.Syncfusion.Services.Implementations;
@@ -86,6 +87,15 @@ public class EventiSectionRenderer : IPdfSectionRenderer
hr.Cells[i].StringFormat = new PdfStringFormat(PdfTextAlignment.Center);
}
// 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;
}
// Righe dati
for (int i = 0; i < data.Eventi.Count; i++)
{
@@ -133,6 +143,16 @@ public class EventiSectionRenderer : IPdfSectionRenderer
// Evidenzia "SI" nella colonna Pagato
if (evt.Paid == "SI")
row.Cells[paidColIndex].Style.TextBrush = PdfTheme.PositiveBrush as PdfBrush;
// 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<PdfGridCell>())
cell.Style.TextBrush = PdfTheme.GrayedOutBrush;
}
}
// Larghezze colonne (landscape A4 ~ 757 punti utili)

View File

@@ -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<PdfGridCell>())
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`) |

View File

@@ -0,0 +1,94 @@
# Design: Righe non raggiungibili in Lista Eventi
**Data:** 2026-03-24
**Scope:** Solo certificati non in quotazione (Stato ≠ "Quotazione")
**Feature:** Evidenziare con testo grigio dimmed le righe della Lista Eventi con Data Osservazione > Data Rimborso
---
## Contesto
Nei report per certificati non in quotazione (Scaduto/Rimborsato/Revocato), la tabella Lista
Eventi può contenere righe con date di osservazione successive alla data di rimborso effettivo. Questi eventi non sono mai stati raggiungibili perché il certificato ha rimborsato anticipatamente. È utile evidenziarli visivamente per distinguerli dagli eventi effettivamente avvenuti.
## Requisito
Per i soli certificati non in quotazione (`isExpired = true`):
- Le righe della Lista Eventi dove `ObservationDate > DataRimborso` devono essere visualizzate con **testo grigio dimmed** (`#AAAAAA`)
- Il colore di sfondo alternato (bianco / azzurro chiaro) rimane invariato
- Tutto il testo della riga viene dimmed, inclusa la colonna Pagato (nessuna eccezione per il verde "SI")
## Design approvato: Opzione A
Testo grigio dimmed su sfondo invariato. Pattern zebra della tabella non viene alterato.
---
## Modifiche
### 1. `CertReports.Syncfusion/Helpers/PdfTheme.cs`
Aggiungere due costanti statiche:
```csharp
public static readonly PdfColor GrayedOut = Color.FromArgb(255, 170, 170, 170);
public static readonly PdfBrush GrayedOutBrush = new PdfSolidBrush(GrayedOut);
```
### 2. `CertReports.Syncfusion/Services/Implementations/EventiSectionRenderer.cs`
Nel metodo `Render()`, nel branch `isExpired`, aggiungere:
**Prima del loop righe** — parse una volta `DataRimborso`:
```csharp
DateTime? rimborsoDate = null;
if (!string.IsNullOrEmpty(data.Info.DataRimborso))
DateTime.TryParseExact(data.Info.DataRimborso, "dd/MM/yyyy",
CultureInfo.InvariantCulture, DateTimeStyles.None, out var d)
.Let(_ => rimborsoDate = d); // o: if (parsed) rimborsoDate = d;
```
**Nel loop righe, dopo assegnazione celle** — determinare se la riga è non raggiungibile e applicare il brush:
```csharp
bool isUnreachable = false;
if (isExpired && rimborsoDate.HasValue &&
DateTime.TryParseExact(evt.ObservationDate, "dd/MM/yyyy",
CultureInfo.InvariantCulture, DateTimeStyles.None, out var obsDate))
{
isUnreachable = obsDate > rimborsoDate.Value;
}
if (isUnreachable)
foreach (var cell in row.Cells.OfType<PdfGridCell>())
cell.Style.TextBrush = PdfTheme.GrayedOutBrush as PdfBrush;
```
Questo blocco va applicato **dopo** la logica delle righe alternate e **dopo** l'evidenziazione verde di Pagato = "SI", sovrascrivendo entrambi sul TextBrush.
---
## Edge Cases
| Caso | Comportamento |
|------|--------------|
| `DataRimborso` vuota o non parseable | Nessuna riga viene dimmed (skip sicuro) |
| `ObservationDate` non parseable | Quella riga non viene dimmed (skip per sicurezza) |
| Certificato in quotazione (`!isExpired`) | Logica completamente ignorata |
| `ObservationDate == DataRimborso` | Non dimmed (la data uguale è ancora raggiungibile) |
---
## File non modificati
- `CertificateModels.cs` — nessun flag aggiuntivo nel modello
- Stored procedure — nessuna modifica DB
- `ExpiredAnagraficaSectionRenderer.cs`, `ChartSectionRenderer.cs` — non coinvolti
---
## Dipendenze
- `System.Globalization` già disponibile nel progetto
- Nessuna nuova dipendenza NuGet