feat: add ExpiredAnagraficaSectionRenderer for non-quoted certificates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
using CertReports.Syncfusion.Helpers;
|
||||
using CertReports.Syncfusion.Models;
|
||||
using CertReports.Syncfusion.Services.Interfaces;
|
||||
using Syncfusion.Drawing;
|
||||
using Syncfusion.Pdf;
|
||||
using Syncfusion.Pdf.Graphics;
|
||||
|
||||
namespace CertReports.Syncfusion.Services.Implementations;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class ExpiredAnagraficaSectionRenderer : IPdfSectionRenderer
|
||||
{
|
||||
public string SectionName => "ExpiredAnagrafica";
|
||||
public int Order => 1;
|
||||
|
||||
private const float PageW = 595f - 2 * PdfTheme.PageMargin;
|
||||
private const float PageH = 842f - 2 * PdfTheme.PageMargin - PdfTheme.FooterHeight;
|
||||
private const float ColGap = 10f;
|
||||
private const float ColW = (PageW - ColGap) / 2f;
|
||||
private const float SectionGap = 10f;
|
||||
|
||||
public PdfDocument Render(CertificateReportData data)
|
||||
{
|
||||
var doc = new PdfDocument();
|
||||
var page = PdfTheme.AddA4Page(doc);
|
||||
var g = page.Graphics;
|
||||
var info = data.Info;
|
||||
float y = 0f;
|
||||
|
||||
// ── TITOLO ────────────────────────────────────────────────────
|
||||
y = DrawTitle(g, info, PageW, y);
|
||||
|
||||
// ── SEZIONE A: CARATTERISTICHE PRODOTTO ───────────────────────
|
||||
y = DrawSectionLabel(g, "Caratteristiche Prodotto", y);
|
||||
y = DrawCaratteristiche(g, info, y);
|
||||
y += SectionGap;
|
||||
|
||||
// ── SEZIONE B: ANALISI ────────────────────────────────────────
|
||||
y = DrawSectionLabel(g, "Analisi", y);
|
||||
DrawAnalisi(g, info, y);
|
||||
|
||||
PdfTheme.DrawFooter(g, PageW, PageH, 1, data.ShowBranding);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// TITOLO — Solo Tipologia box (niente Data/Bid/Ask)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private float DrawTitle(PdfGraphics g, CertificateInfo info, float w, float y)
|
||||
{
|
||||
g.DrawString($"Scheda Prodotto {info.Isin}",
|
||||
PdfTheme.SectionTitleFont,
|
||||
new PdfSolidBrush(PdfTheme.AccentBlue),
|
||||
new RectangleF(0, y, w, 20f),
|
||||
new PdfStringFormat(PdfTextAlignment.Center));
|
||||
y += 24f;
|
||||
|
||||
g.DrawLine(PdfTheme.AccentBluePen, 0, y, w, y);
|
||||
y += 8f;
|
||||
|
||||
// Solo box Tipologia, niente Data/Bid/Ask
|
||||
const float boxH = 28f;
|
||||
if (!string.IsNullOrEmpty(info.Categoria))
|
||||
{
|
||||
DrawInfoBox(g, 0, y, w, boxH,
|
||||
"TIPOLOGIA", info.Categoria,
|
||||
PdfTheme.BoxLightBlueBgBrush, PdfTheme.AccentBlueBrush,
|
||||
PdfTheme.AccentBlueBrush, PdfTheme.AccentBlueDarkBrush,
|
||||
PdfTheme.Bold);
|
||||
}
|
||||
|
||||
y += boxH + 10f;
|
||||
return y;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// INTESTAZIONE DI SEZIONE
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private float DrawSectionLabel(PdfGraphics g, string title, float y)
|
||||
{
|
||||
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(0, y, 3f, 14f));
|
||||
g.DrawString(title, PdfTheme.Bold,
|
||||
new PdfSolidBrush(PdfTheme.AccentBlue),
|
||||
new RectangleF(6f, y, PageW, 14f));
|
||||
return y + 16f;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// INFO BOX
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void DrawInfoBox(
|
||||
PdfGraphics g,
|
||||
float x, float y, float w, float boxH,
|
||||
string label, string value,
|
||||
PdfBrush bgBrush, PdfBrush accentBrush,
|
||||
PdfBrush labelBrush, PdfBrush valueBrush,
|
||||
PdfFont valueFont)
|
||||
{
|
||||
g.DrawRectangle(bgBrush, new RectangleF(x, y, w, boxH));
|
||||
g.DrawRectangle(accentBrush, new RectangleF(x, y, 4f, boxH));
|
||||
float innerX = x + 4f + 6f;
|
||||
float innerW = w - 4f - 6f - 4f;
|
||||
g.DrawString(label, PdfTheme.Small, labelBrush,
|
||||
new RectangleF(innerX, y + 4f, innerW, 10f));
|
||||
g.DrawString(value, valueFont, valueBrush,
|
||||
new RectangleF(innerX, y + 14f, innerW, 14f));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// SEZIONE A: CARATTERISTICHE PRODOTTO
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private float DrawCaratteristiche(PdfGraphics g, CertificateInfo info, float y)
|
||||
{
|
||||
return DrawEmittenteTable(g, info, 0, ColW, y);
|
||||
}
|
||||
|
||||
private float DrawEmittenteTable(PdfGraphics g, CertificateInfo info, float x, float w, float y)
|
||||
{
|
||||
float rh = PdfTheme.RowHeight;
|
||||
float pad = PdfTheme.CellPadding;
|
||||
|
||||
// Header blu
|
||||
g.DrawRectangle(PdfTheme.TableHeaderBrush, new RectangleF(x, y, w, rh));
|
||||
string headerText = string.IsNullOrEmpty(info.Emittente)
|
||||
? "EMITTENTE"
|
||||
: $"EMITTENTE {info.Emittente.ToUpper()}";
|
||||
g.DrawString(headerText, PdfTheme.TableBold,
|
||||
PdfTheme.HeaderTextBrush,
|
||||
new RectangleF(x + pad, y + 3f, w - pad * 2, rh));
|
||||
y += rh;
|
||||
|
||||
var rows = new[]
|
||||
{
|
||||
("ISIN", info.Isin),
|
||||
("Mercato", info.Mercato),
|
||||
("Valuta", info.Valuta),
|
||||
("Data Emissione", info.DataEmissione),
|
||||
("Valore Rimborso", info.ValoreRimborso),
|
||||
("Data Rimborso", info.DataRimborso),
|
||||
};
|
||||
|
||||
for (int i = 0; i < rows.Length; i++)
|
||||
{
|
||||
var (label, value) = rows[i];
|
||||
if (string.IsNullOrWhiteSpace(value)) continue;
|
||||
|
||||
bool alt = i % 2 == 1;
|
||||
if (alt)
|
||||
g.DrawRectangle(new PdfSolidBrush(PdfTheme.TableAltRow),
|
||||
new RectangleF(x, y, w, rh));
|
||||
|
||||
g.DrawLine(PdfTheme.TableBorderPen, x, y, x + w, y);
|
||||
|
||||
g.DrawString(label, PdfTheme.TableFont,
|
||||
new PdfSolidBrush(PdfTheme.TextSecondary),
|
||||
new RectangleF(x + pad, y + 2f, w * 0.5f, rh));
|
||||
|
||||
var valueBrush = label == "ISIN"
|
||||
? new PdfSolidBrush(PdfTheme.AccentBlue)
|
||||
: new PdfSolidBrush(PdfTheme.TextPrimary);
|
||||
|
||||
g.DrawString(value, PdfTheme.TableBold, valueBrush,
|
||||
new RectangleF(x + w * 0.5f, y + 2f, w * 0.5f - pad, rh),
|
||||
new PdfStringFormat(PdfTextAlignment.Right));
|
||||
|
||||
y += rh;
|
||||
}
|
||||
|
||||
g.DrawLine(PdfTheme.TableBorderPen, x, y, x + w, y);
|
||||
return y + 2f;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// SEZIONE B: ANALISI — KV list colonna destra
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private float DrawAnalisi(PdfGraphics g, CertificateInfo info, float y)
|
||||
{
|
||||
var items = new (string Label, string Value)[]
|
||||
{
|
||||
("Importo Cedola (p.a.)", info.NominalAnnualYield),
|
||||
("Frequenza Cedola", info.FrequenzaCedole),
|
||||
("Valore Nominale", info.NominalValue?.ToString("N0") ?? string.Empty),
|
||||
("Memoria Cedola", info.Memory),
|
||||
("Tipo Barriera", info.BarrierType),
|
||||
("Tipo Basket", info.BasketType),
|
||||
("Rendimento Totale", info.RendimentoTotale),
|
||||
};
|
||||
|
||||
return DrawKVList(g, items, ColW + ColGap, ColW, y);
|
||||
}
|
||||
|
||||
private float DrawKVList(PdfGraphics g, (string Label, string Value)[] items, float x, float w, float y)
|
||||
{
|
||||
float rh = PdfTheme.RowHeight;
|
||||
float pad = PdfTheme.CellPadding;
|
||||
float labelW = w * 0.58f;
|
||||
float valueW = w * 0.42f;
|
||||
|
||||
foreach (var (label, value) in items)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) continue;
|
||||
|
||||
g.DrawString(label, PdfTheme.TableFont,
|
||||
new PdfSolidBrush(PdfTheme.TextSecondary),
|
||||
new RectangleF(x + pad, y + 1f, labelW, rh));
|
||||
|
||||
bool isNeg = value.TrimStart().StartsWith('-');
|
||||
bool isKey = label == "Rendimento Totale";
|
||||
|
||||
var brush = isNeg ? new PdfSolidBrush(PdfTheme.NegativeRed)
|
||||
: isKey ? new PdfSolidBrush(PdfTheme.AccentBlue)
|
||||
: new PdfSolidBrush(PdfTheme.TextPrimary);
|
||||
|
||||
g.DrawString(value, PdfTheme.TableBold, brush,
|
||||
new RectangleF(x + labelW, y + 1f, valueW - pad, rh),
|
||||
new PdfStringFormat(PdfTextAlignment.Right));
|
||||
|
||||
g.DrawLine(new PdfPen(PdfTheme.SeparatorLine, 0.3f),
|
||||
x, y + rh, x + w, y + rh);
|
||||
|
||||
y += rh;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user