diff --git a/CertReports.Syncfusion/CertReports.Syncfusion.csproj b/CertReports.Syncfusion/CertReports.Syncfusion.csproj
index 5ff333e..40ceab0 100644
--- a/CertReports.Syncfusion/CertReports.Syncfusion.csproj
+++ b/CertReports.Syncfusion/CertReports.Syncfusion.csproj
@@ -7,6 +7,13 @@
CertReports.Syncfusion
+
+
+
+
+
+
+
diff --git a/CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs b/CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs
index d5d1e05..06076c1 100644
--- a/CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs
+++ b/CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs
@@ -4,13 +4,14 @@ using CertReports.Syncfusion.Services.Interfaces;
using Syncfusion.Drawing;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
+using Syncfusion.Pdf.Grid;
namespace CertReports.Syncfusion.Services.Implementations;
///
/// Sezione 1 — Prima pagina del report per certificati non in quotazione
-/// (Scaduto, Rimborsato, Revocato). Struttura semplificata rispetto al report attivo:
-/// solo Titolo + Caratteristiche Prodotto + Analisi. Niente Bid/Ask, niente Sottostanti.
+/// (Scaduto, Rimborsato, Revocato). Struttura: Titolo + Caratteristiche Prodotto + Analisi + Sottostanti.
+/// Niente Bid/Ask nel titolo.
///
public class ExpiredAnagraficaSectionRenderer : IPdfSectionRenderer
{
@@ -42,8 +43,24 @@ public class ExpiredAnagraficaSectionRenderer : IPdfSectionRenderer
// ── SEZIONE B: ANALISI ────────────────────────────────────────
y = DrawSectionLabel(g, "Analisi", y);
y = DrawAnalisi(g, info, y);
+ y += SectionGap;
- PdfTheme.DrawFooter(g, PageW, PageH, 1, data.ShowBranding);
+ // ── SEZIONE C: SOTTOSTANTI ────────────────────────────────────
+ if (info.Sottostanti.Count > 0)
+ {
+ if (y > PageH - 80f)
+ {
+ PdfTheme.DrawFooter(g, PageW, PageH, 1, data.ShowBranding);
+ page = doc.Pages.Add();
+ g = page.Graphics;
+ y = 0f;
+ }
+
+ y = DrawSectionLabel(g, "Sottostanti", y);
+ DrawSottostanti(g, info.Sottostanti, PageW, y);
+ }
+
+ PdfTheme.DrawFooter(g, PageW, PageH, doc.Pages.Count, data.ShowBranding);
return doc;
}
@@ -201,6 +218,90 @@ public class ExpiredAnagraficaSectionRenderer : IPdfSectionRenderer
return DrawKVList(g, items, 0, ColW, y);
}
+ // ═══════════════════════════════════════════════════════════════
+ // SEZIONE C: SOTTOSTANTI (9 colonne, font 7.5pt)
+ // ═══════════════════════════════════════════════════════════════
+
+ private void DrawSottostanti(PdfGraphics g, List sottostanti, float w, float y)
+ {
+ var grid = new PdfGrid();
+ grid.Style.CellPadding = new PdfPaddings(2, 2, 2, 2);
+
+ string[] headers = { "Nome", "Strike", "Last", "% Perf.", "Barriera Capitale",
+ "Buffer Capitale", "Trigger Cedola", "Buffer Cedola", "Trigger Autocall" };
+
+ foreach (var _ in headers) grid.Columns.Add();
+
+ var hr = grid.Headers.Add(1)[0];
+ for (int i = 0; i < headers.Length; i++)
+ {
+ hr.Cells[i].Value = headers[i];
+ hr.Cells[i].Style.Font = PdfTheme.TableBold;
+ hr.Cells[i].Style.BackgroundBrush = PdfTheme.TableHeaderBrush;
+ hr.Cells[i].Style.TextBrush = PdfTheme.HeaderTextBrush;
+ hr.Cells[i].StringFormat = new PdfStringFormat(
+ i == 0 ? PdfTextAlignment.Left : PdfTextAlignment.Center);
+ }
+
+ for (int i = 0; i < sottostanti.Count; i++)
+ {
+ var s = sottostanti[i];
+ var row = grid.Rows.Add();
+
+ row.Cells[0].Value = s.Nome;
+ row.Cells[1].Value = s.Strike;
+ row.Cells[2].Value = s.LastPrice;
+ row.Cells[3].Value = s.Performance;
+ row.Cells[4].Value = s.CapitalBarrier;
+ row.Cells[5].Value = s.ULCapitalBarrierBuffer;
+ row.Cells[6].Value = s.CouponBarrier;
+ row.Cells[7].Value = s.ULCouponBarrierBuffer;
+ row.Cells[8].Value = s.TriggerAutocall;
+
+ for (int c = 0; c < headers.Length; c++)
+ {
+ row.Cells[c].Style.Font = PdfTheme.TableFont;
+ row.Cells[c].StringFormat = new PdfStringFormat(
+ c == 0 ? PdfTextAlignment.Left : PdfTextAlignment.Right);
+ }
+
+ ColorPerformanceCell(row.Cells[3], s.Performance);
+ ColorPerformanceCell(row.Cells[5], s.ULCapitalBarrierBuffer);
+ ColorPerformanceCell(row.Cells[7], s.ULCouponBarrierBuffer);
+
+ if (i % 2 == 1)
+ for (int c = 0; c < headers.Length; c++)
+ row.Cells[c].Style.BackgroundBrush = PdfTheme.TableAltRowBrush;
+ }
+
+ float[] cw = { 90f, 52f, 52f, 46f, 52f, 46f, 52f, 46f, 46f };
+ float total = cw.Sum();
+ float scale = w / total;
+ for (int i = 0; i < cw.Length; i++)
+ grid.Columns[i].Width = cw[i] * scale;
+
+ PdfTheme.ApplyThinBorders(grid);
+ grid.Draw(g, new PointF(0, y));
+ }
+
+ // ═══════════════════════════════════════════════════════════════
+ // Helper
+ // ═══════════════════════════════════════════════════════════════
+
+ private static bool IsNegativeValue(string? value)
+ {
+ if (string.IsNullOrWhiteSpace(value)) return false;
+ return value.TrimStart().StartsWith('-');
+ }
+
+ private static void ColorPerformanceCell(PdfGridCell cell, string? value)
+ {
+ if (IsNegativeValue(value))
+ cell.Style.TextBrush = new PdfSolidBrush(PdfTheme.NegativeRed);
+ else if (!string.IsNullOrWhiteSpace(value) && value != "-" && value != "—")
+ cell.Style.TextBrush = new PdfSolidBrush(PdfTheme.PositiveGreen);
+ }
+
private float DrawKVList(PdfGraphics g, (string Label, string Value)[] items, float x, float w, float y)
{
float rh = PdfTheme.RowHeight;
diff --git a/README.md b/README.md
index 02021a5..914555c 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ Generatore di report PDF per certificati finanziari strutturati, sviluppato in A
| Sezione | Contenuto |
|---------|-----------|
-| **1 — Anagrafica (semplificata)** | Header con solo Tipologia, caratteristiche con Valore/Data Rimborso, analisi KV senza Sottostanti né Bid/Ask |
+| **1 — Anagrafica (semplificata)** | Header con solo Tipologia, caratteristiche con Valore/Data Rimborso, analisi KV, tabella sottostanti (9 colonne, senza Bid/Ask) |
| **2 — Eventi** | Lista eventi con colonne adattate (Barriera Cedola, Soglia Rimborso, Barriera Capitale, Rimborso Capitale) |
| **3 — Grafico** | Performance storica dei sottostanti |