Compare commits
4 Commits
76145ad0d2
...
5e9598e9dd
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e9598e9dd | |||
| 8675c6a19e | |||
| 1a4e3538b5 | |||
| 5e90446cbe |
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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`) |
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user