From b60956db9a7b37c7397f8d2d86425a120f1a0f4a Mon Sep 17 00:00:00 2001 From: SmartRootsSrl Date: Sat, 21 Mar 2026 10:38:41 +0100 Subject: [PATCH] feat: add expired certificate report branching in orchestrator Co-Authored-By: Claude Sonnet 4.6 --- .../Implementations/ReportOrchestrator.cs | 83 +++++++++++-------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/CertReports.Syncfusion/Services/Implementations/ReportOrchestrator.cs b/CertReports.Syncfusion/Services/Implementations/ReportOrchestrator.cs index 38b638b..fcdf7d0 100644 --- a/CertReports.Syncfusion/Services/Implementations/ReportOrchestrator.cs +++ b/CertReports.Syncfusion/Services/Implementations/ReportOrchestrator.cs @@ -6,7 +6,7 @@ namespace CertReports.Syncfusion.Services.Implementations; /// /// Orchestratore principale: coordina il flusso di generazione report. -/// +/// /// Flusso: /// 1. Recupera dati dal DB (stored procedures) /// 2. Renderizza le sezioni PDF (anagrafica, eventi, scenario) @@ -21,6 +21,7 @@ public class ReportOrchestrator : IReportOrchestrator private readonly IPdfMergerService _merger; private readonly IPdfCacheService _cache; private readonly ILogger _logger; + private readonly ExpiredAnagraficaSectionRenderer _expiredAnagraficaRenderer; public ReportOrchestrator( ICertificateDataService dataService, @@ -28,7 +29,8 @@ public class ReportOrchestrator : IReportOrchestrator IChartSectionRenderer chartRenderer, IPdfMergerService merger, IPdfCacheService cache, - ILogger logger) + ILogger logger, + ExpiredAnagraficaSectionRenderer expiredAnagraficaRenderer) { _dataService = dataService; _sectionRenderers = sectionRenderers; @@ -36,13 +38,16 @@ public class ReportOrchestrator : IReportOrchestrator _merger = merger; _cache = cache; _logger = logger; + _expiredAnagraficaRenderer = expiredAnagraficaRenderer; } public async Task GenerateReportAsync(string isin, bool showBranding = false) { - // ── Cache check (chiave include branding) ───────────────────────── - var cacheKey = showBranding ? $"{isin}:branded" : isin; - var cached = _cache.Get(cacheKey); + // ── Cache check ──────────────────────────────────────────────── + var baseCacheKey = showBranding ? $"{isin}:branded" : isin; + var expiredCacheKey = showBranding ? $"{isin}:expired:branded" : $"{isin}:expired"; + + var cached = _cache.Get(baseCacheKey) ?? _cache.Get(expiredCacheKey); if (cached != null) { _logger.LogInformation("Report per ISIN {Isin} servito da cache ({Size} bytes)", isin, cached.Length); @@ -60,40 +65,56 @@ public class ReportOrchestrator : IReportOrchestrator ShowBranding = showBranding, }; - // Determina se lo scenario ha dati validi (evita doppia chiamata SP) - bool isScenarioAllowed = reportData.Scenario.Rows.Count > 0; + // ── 2. Determina il tipo di report ──────────────────────────── + bool isExpired = !string.IsNullOrEmpty(reportData.Info.Stato) + && reportData.Info.Stato != "Quotazione"; + + var cacheKey = isExpired ? expiredCacheKey : baseCacheKey; _logger.LogInformation( - "Dati recuperati per {Isin}: {SottostantiCount} sottostanti, {EventiCount} eventi, Scenario: {ScenarioAllowed}", - isin, reportData.Info.Sottostanti.Count, reportData.Eventi.Count, isScenarioAllowed); + "Dati recuperati per {Isin}: Stato={Stato}, isExpired={IsExpired}, {EventiCount} eventi", + isin, reportData.Info.Stato, isExpired, reportData.Eventi.Count); - // ── 2. Genera le sezioni PDF ─────────────────────────────────── + // ── 3. Genera le sezioni PDF ────────────────────────────────── var pdfSections = new List(); - foreach (var renderer in _sectionRenderers.OrderBy(r => r.Order)) + if (isExpired) { - // Salta la sezione scenario se il certificato è Protection - if (renderer.SectionName == "Scenario" && !isScenarioAllowed) - { - _logger.LogInformation("Sezione Scenario saltata per {Isin} (certificato Protection)", isin); - continue; - } + // Flusso expired: ExpiredAnagrafica + Eventi + Chart + pdfSections.Add(_expiredAnagraficaRenderer.Render(reportData)); + _logger.LogInformation("Sezione 'ExpiredAnagrafica' generata per {Isin}", isin); - try + var eventiRenderer = _sectionRenderers.First(r => r.SectionName == "Eventi"); + pdfSections.Add(eventiRenderer.Render(reportData)); + _logger.LogInformation("Sezione 'Eventi' generata per {Isin}", isin); + } + else + { + // Flusso attuale: Anagrafica + Eventi + Scenario (condizionale) + bool isScenarioAllowed = reportData.Scenario.Rows.Count > 0; + + foreach (var renderer in _sectionRenderers.OrderBy(r => r.Order)) { - var sectionPdf = renderer.Render(reportData); - pdfSections.Add(sectionPdf); - _logger.LogInformation("Sezione '{Section}' generata per {Isin}", renderer.SectionName, isin); - } - catch (Exception ex) - { - _logger.LogError(ex, "Errore nella generazione della sezione '{Section}' per {Isin}", - renderer.SectionName, isin); - throw; + if (renderer.SectionName == "Scenario" && !isScenarioAllowed) + { + _logger.LogInformation("Sezione Scenario saltata per {Isin}", isin); + continue; + } + + try + { + pdfSections.Add(renderer.Render(reportData)); + _logger.LogInformation("Sezione '{Section}' generata per {Isin}", renderer.SectionName, isin); + } + catch (Exception ex) + { + _logger.LogError(ex, "Errore nella sezione '{Section}' per {Isin}", renderer.SectionName, isin); + throw; + } } } - // ── 3. Genera/recupera il grafico ────────────────────────────── + // ── 4. Grafico (entrambi i flussi) ──────────────────────────── var chartPdf = await _chartRenderer.RenderAsync(isin); if (chartPdf != null) { @@ -101,20 +122,16 @@ public class ReportOrchestrator : IReportOrchestrator _logger.LogInformation("Sezione Grafico aggiunta per {Isin}", isin); } - // ── 4. Unisci tutto ──────────────────────────────────────────── + // ── 5. Unisci tutto ──────────────────────────────────────────── var finalPdf = _merger.Merge(pdfSections); _logger.LogInformation("Report generato per {Isin}: {Size} bytes, {Sections} sezioni", isin, finalPdf.Length, pdfSections.Count); - // Salva in cache _cache.Set(cacheKey, finalPdf); - // Cleanup foreach (var doc in pdfSections) - { doc.Close(true); - } return finalPdf; }