diff --git a/docs/superpowers/plans/2026-03-26-memoria-natixis.md b/docs/superpowers/plans/2026-03-26-memoria-natixis.md new file mode 100644 index 0000000..c6e5d51 --- /dev/null +++ b/docs/superpowers/plans/2026-03-26-memoria-natixis.md @@ -0,0 +1,521 @@ +# Memoria Field + Natixis Parameter — 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:** Add a "Memoria" KV field in the Analisi section of all reports, and a `?natixis` query param that switches the Tipologia source from `info.Categoria` to `info.Nome`. + +**Architecture:** The `ShowNatixis` bool follows the existing `ShowBranding`/`ShowDividend` pattern: added to `CertificateReportData`, propagated from controller → orchestrator → renderers. Cache keys follow the existing suffix-concatenation pattern. The `DrawTitle` private method in both renderers gains a `bool showNatixis` parameter (Option A from spec). + +**Tech Stack:** ASP.NET Core 8, Syncfusion PDF, C# 12 + +--- + +## Important Pre-Read + +Before starting, note these facts about the existing code: + +- `CertificateInfo.Memory` (string) **already exists** in `CertificateModels.cs:22` — no model/SP changes needed for Feature 1. +- `ExpiredAnagraficaSectionRenderer.DrawAnalisi` **already contains** `("Memoria Cedola", info.Memory)` at line 211 — it only needs to be **renamed** to `"Memoria"`. +- `AnagraficaSectionRenderer.DrawAnalisi` does **not** have the field — it needs to be **added** after `("Leva", ...)` at line 327. +- No automated tests exist in this project. Each task ends with `dotnet build` as verification. + +--- + +## File Map + +| File | Change | +|------|--------| +| `Models/CertificateModels.cs` | Add `bool ShowNatixis` to `CertificateReportData` | +| `Services/Interfaces/IServices.cs` | Add `showNatixis` param to `GenerateReportAsync` | +| `Controllers/ReportController.cs` | Add `[FromQuery] natixis` to all 3 endpoints + helper | +| `Services/Implementations/ReportOrchestrator.cs` | Add param, propagate flag, update cache key logic | +| `Services/Implementations/AnagraficaSectionRenderer.cs` | Add Memoria item in `DrawAnalisi`; add `showNatixis` to `DrawTitle` | +| `Services/Implementations/ExpiredAnagraficaSectionRenderer.cs` | Rename "Memoria Cedola"→"Memoria"; add `showNatixis` to `DrawTitle` | + +--- + +## Task 1: Add `ShowNatixis` to `CertificateReportData` + +**Files:** +- Modify: `CertReports.Syncfusion/Models/CertificateModels.cs` (around line 162) + +- [ ] **Step 1: Add the property** + + In `CertificateModels.cs`, find the `CertificateReportData` class: + ```csharp + public class CertificateReportData + { + public CertificateInfo Info { get; set; } = new(); + public List Eventi { get; set; } = new(); + public ScenarioAnalysis Scenario { get; set; } = new(); + public byte[]? ChartImage { get; set; } + public bool ShowBranding { get; set; } = false; + public bool ShowDividend { get; set; } = false; + } + ``` + Add `ShowNatixis` after `ShowDividend`: + ```csharp + public bool ShowDividend { get; set; } = false; + public bool ShowNatixis { get; set; } = false; + ``` + +- [ ] **Step 2: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: Build succeeded, 0 errors. + +- [ ] **Step 3: Commit** + + ```bash + git add CertReports.Syncfusion/Models/CertificateModels.cs + git commit -m "feat: add ShowNatixis flag to CertificateReportData" + ``` + +--- + +## Task 2: Update `IReportOrchestrator` interface + +**Files:** +- Modify: `CertReports.Syncfusion/Services/Interfaces/IServices.cs` (line 49) + +- [ ] **Step 1: Update the signature** + + Find: + ```csharp + Task GenerateReportAsync(string isin, bool showBranding = false, bool showDividend = false); + ``` + Replace with: + ```csharp + Task GenerateReportAsync(string isin, bool showBranding = false, bool showDividend = false, bool showNatixis = false); + ``` + +- [ ] **Step 2: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: **Build fails** with CS0535 ("ReportOrchestrator does not implement interface member GenerateReportAsync") — this is intentional and will be resolved in Task 4. + +- [ ] **Step 3: Commit** + + ```bash + git add CertReports.Syncfusion/Services/Interfaces/IServices.cs + git commit -m "feat: add showNatixis param to IReportOrchestrator interface" + ``` + +--- + +## Task 3: Update `ReportController` + +**Files:** +- Modify: `CertReports.Syncfusion/Controllers/ReportController.cs` + +- [ ] **Step 1: Update `GenerateReport` endpoint (line 43)** + + Find: + ```csharp + public async Task GenerateReport( + [FromQuery(Name = "p")] string? encryptedIsin = null, + [FromQuery(Name = "alias")] string? aliasId = null, + [FromQuery(Name = "branding")] bool showBranding = false, + [FromQuery(Name = "dividend")] bool showDividend = false) + ``` + Replace with: + ```csharp + public async Task GenerateReport( + [FromQuery(Name = "p")] string? encryptedIsin = null, + [FromQuery(Name = "alias")] string? aliasId = null, + [FromQuery(Name = "branding")] bool showBranding = false, + [FromQuery(Name = "dividend")] bool showDividend = false, + [FromQuery(Name = "natixis")] bool showNatixis = false) + ``` + +- [ ] **Step 2: Update the call to `GenerateAndReturnPdf` inside `GenerateReport` (line 74)** + + Find: + ```csharp + return await GenerateAndReturnPdf(isin, showBranding, showDividend); + ``` + Replace with: + ```csharp + return await GenerateAndReturnPdf(isin, showBranding, showDividend, showNatixis); + ``` + +- [ ] **Step 3: Update `GenerateReportByIsin` endpoint (line 82)** + + Find: + ```csharp + public async Task GenerateReportByIsin( + string isin, + [FromQuery(Name = "branding")] bool showBranding = false, + [FromQuery(Name = "dividend")] bool showDividend = false) + ``` + Replace with: + ```csharp + public async Task GenerateReportByIsin( + string isin, + [FromQuery(Name = "branding")] bool showBranding = false, + [FromQuery(Name = "dividend")] bool showDividend = false, + [FromQuery(Name = "natixis")] bool showNatixis = false) + ``` + +- [ ] **Step 4: Update the call to `GenerateAndReturnPdf` inside `GenerateReportByIsin` (line 92)** + + Find: + ```csharp + return await GenerateAndReturnPdf(isin, showBranding, showDividend); + ``` + Replace with: + ```csharp + return await GenerateAndReturnPdf(isin, showBranding, showDividend, showNatixis); + ``` + Note: there are two identical `return await GenerateAndReturnPdf(isin, showBranding, showDividend);` lines — make sure you update the one inside `GenerateReportByIsin` (around line 92). + +- [ ] **Step 5: Update `DownloadReport` endpoint (line 99)** + + Find: + ```csharp + public async Task DownloadReport( + [FromQuery(Name = "p")] string? encryptedIsin = null, + [FromQuery(Name = "alias")] string? aliasId = null, + [FromQuery(Name = "branding")] bool showBranding = false, + [FromQuery(Name = "dividend")] bool showDividend = false) + ``` + Replace with: + ```csharp + public async Task DownloadReport( + [FromQuery(Name = "p")] string? encryptedIsin = null, + [FromQuery(Name = "alias")] string? aliasId = null, + [FromQuery(Name = "branding")] bool showBranding = false, + [FromQuery(Name = "dividend")] bool showDividend = false, + [FromQuery(Name = "natixis")] bool showNatixis = false) + ``` + +- [ ] **Step 6: Update the `_orchestrator.GenerateReportAsync` call inside `DownloadReport` (line 117)** + + Find: + ```csharp + var pdfBytes = await _orchestrator.GenerateReportAsync(isin, showBranding, showDividend); + ``` + Replace with: + ```csharp + var pdfBytes = await _orchestrator.GenerateReportAsync(isin, showBranding, showDividend, showNatixis); + ``` + +- [ ] **Step 7: Update `GenerateAndReturnPdf` helper signature (line 129)** + + Find: + ```csharp + private async Task GenerateAndReturnPdf(string isin, bool showBranding = false, bool showDividend = false) + ``` + Replace with: + ```csharp + private async Task GenerateAndReturnPdf(string isin, bool showBranding = false, bool showDividend = false, bool showNatixis = false) + ``` + +- [ ] **Step 8: Update the `_orchestrator.GenerateReportAsync` call inside `GenerateAndReturnPdf` (line 134)** + + Find: + ```csharp + var pdfBytes = await _orchestrator.GenerateReportAsync(isin, showBranding, showDividend); + ``` + Replace with: + ```csharp + var pdfBytes = await _orchestrator.GenerateReportAsync(isin, showBranding, showDividend, showNatixis); + ``` + +- [ ] **Step 9: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: Build succeeded. + +- [ ] **Step 10: Commit** + + ```bash + git add CertReports.Syncfusion/Controllers/ReportController.cs + git commit -m "feat: add natixis query param to all ReportController endpoints" + ``` + +--- + +## Task 4: Update `ReportOrchestrator` + +**Files:** +- Modify: `CertReports.Syncfusion/Services/Implementations/ReportOrchestrator.cs` + +- [ ] **Step 1: Update method signature (line 47)** + + Find: + ```csharp + public async Task GenerateReportAsync(string isin, bool showBranding = false, bool showDividend = false) + ``` + Replace with: + ```csharp + public async Task GenerateReportAsync(string isin, bool showBranding = false, bool showDividend = false, bool showNatixis = false) + ``` + +- [ ] **Step 2: Update cache key logic (lines 50-52)** + + Find: + ```csharp + var dividendSuffix = showDividend ? ":dividend" : ""; + var baseCacheKey = showBranding ? $"{isin}:branded{dividendSuffix}" : $"{isin}{dividendSuffix}"; + var expiredCacheKey = showBranding ? $"{isin}:expired:branded{dividendSuffix}" : $"{isin}:expired{dividendSuffix}"; + ``` + Replace with: + ```csharp + var dividendSuffix = showDividend ? ":dividend" : ""; + var natixisSuffix = showNatixis ? ":natixis" : ""; + var baseCacheKey = showBranding + ? $"{isin}:branded{dividendSuffix}{natixisSuffix}" + : $"{isin}{dividendSuffix}{natixisSuffix}"; + var expiredCacheKey = showBranding + ? $"{isin}:expired:branded{dividendSuffix}{natixisSuffix}" + : $"{isin}:expired{dividendSuffix}{natixisSuffix}"; + ``` + +- [ ] **Step 3: Propagate `ShowNatixis` into `CertificateReportData` (lines 64-71)** + + Find: + ```csharp + var reportData = new CertificateReportData + { + Info = await _dataService.GetCertificateInfoAsync(isin), + Eventi = await _dataService.GetCertificateEventsAsync(isin), + Scenario = await _dataService.GetScenarioAnalysisAsync(isin), + ShowBranding = showBranding, + ShowDividend = showDividend, + }; + ``` + Replace with: + ```csharp + var reportData = new CertificateReportData + { + Info = await _dataService.GetCertificateInfoAsync(isin), + Eventi = await _dataService.GetCertificateEventsAsync(isin), + Scenario = await _dataService.GetScenarioAnalysisAsync(isin), + ShowBranding = showBranding, + ShowDividend = showDividend, + ShowNatixis = showNatixis, + }; + ``` + +- [ ] **Step 4: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: Build succeeded, 0 errors. + +- [ ] **Step 5: Commit** + + ```bash + git add CertReports.Syncfusion/Services/Implementations/ReportOrchestrator.cs + git commit -m "feat: propagate showNatixis through orchestrator and cache keys" + ``` + +--- + +## Task 5: Update `AnagraficaSectionRenderer` (active certs) + +Two changes: (A) add Memoria to `DrawAnalisi`, (B) support natixis in `DrawTitle`. + +**Files:** +- Modify: `CertReports.Syncfusion/Services/Implementations/AnagraficaSectionRenderer.cs` + +### Change A — Add Memoria to DrawAnalisi + +- [ ] **Step 1: Add Memoria item after Leva (line 327)** + + Find (this is the closing `};` of the `leftItems` array — NOT the `rightItems` array that follows a few lines later): + ```csharp + ("Leva", string.IsNullOrWhiteSpace(info.Leva) ? "—" : info.Leva), + }; + ``` + Replace with: + ```csharp + ("Leva", string.IsNullOrWhiteSpace(info.Leva) ? "—" : info.Leva), + ("Memoria", string.IsNullOrWhiteSpace(info.Memory) ? "—" : info.Memory), + }; + ``` + +### Change B — Support Natixis in DrawTitle + +- [ ] **Step 2: Update the `DrawTitle` call site in `Render` (line 37)** + + Find: + ```csharp + y = DrawTitle(g, info, PageW, y); + ``` + Replace with: + ```csharp + y = DrawTitle(g, info, PageW, y, data.ShowNatixis); + ``` + +- [ ] **Step 3: Update `DrawTitle` signature (line 74)** + + Find: + ```csharp + private float DrawTitle(PdfGraphics g, CertificateInfo info, float w, float y) + ``` + Replace with: + ```csharp + private float DrawTitle(PdfGraphics g, CertificateInfo info, float w, float y, bool showNatixis) + ``` + +- [ ] **Step 4: Update Tipologia resolution in `DrawTitle` (line 95)** + + Find: + ```csharp + bool showTip = !string.IsNullOrEmpty(info.Categoria); + ``` + Replace with: + ```csharp + string tipologia = showNatixis ? info.Nome : info.Categoria; + bool showTip = !string.IsNullOrEmpty(tipologia); + ``` + +- [ ] **Step 5: Replace `info.Categoria` with `tipologia` in the `DrawInfoBox` call (line 110)** + + Find: + ```csharp + "TIPOLOGIA", info.Categoria, + ``` + Replace with: + ```csharp + "TIPOLOGIA", tipologia, + ``` + +- [ ] **Step 6: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: Build succeeded, 0 errors. + +- [ ] **Step 7: Commit** + + ```bash + git add CertReports.Syncfusion/Services/Implementations/AnagraficaSectionRenderer.cs + git commit -m "feat: add Memoria field to Analisi and natixis support to AnagraficaSectionRenderer" + ``` + +--- + +## Task 6: Update `ExpiredAnagraficaSectionRenderer` (expired certs) + +Two changes: (A) rename "Memoria Cedola" → "Memoria" in `DrawAnalisi`, (B) support natixis in `DrawTitle`. + +**Files:** +- Modify: `CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs` + +### Change A — Rename label in DrawAnalisi + +- [ ] **Step 1: Rename "Memoria Cedola" to "Memoria" (line 211)** + + Find: + ```csharp + ("Memoria Cedola", info.Memory), + ``` + Replace with: + ```csharp + ("Memoria", info.Memory), + ``` + +### Change B — Support Natixis in DrawTitle + +- [ ] **Step 2: Update the `DrawTitle` call site in `Render` (line 36)** + + Find: + ```csharp + y = DrawTitle(g, info, PageW, y); + ``` + Replace with: + ```csharp + y = DrawTitle(g, info, PageW, y, data.ShowNatixis); + ``` + +- [ ] **Step 3: Update `DrawTitle` signature (line 72)** + + Find: + ```csharp + private float DrawTitle(PdfGraphics g, CertificateInfo info, float w, float y) + ``` + Replace with: + ```csharp + private float DrawTitle(PdfGraphics g, CertificateInfo info, float w, float y, bool showNatixis) + ``` + +- [ ] **Step 4: Update Tipologia resolution in `DrawTitle` (line 86)** + + Find: + ```csharp + if (!string.IsNullOrEmpty(info.Categoria)) + { + DrawInfoBox(g, 0, y, w, boxH, + "TIPOLOGIA", info.Categoria, + ``` + Replace with: + ```csharp + string tipologia = showNatixis ? info.Nome : info.Categoria; + if (!string.IsNullOrEmpty(tipologia)) + { + DrawInfoBox(g, 0, y, w, boxH, + "TIPOLOGIA", tipologia, + ``` + +- [ ] **Step 5: Build** + + ```bash + dotnet build CertReports.Syncfusion + ``` + Expected: Build succeeded, 0 errors. + +- [ ] **Step 6: Commit** + + ```bash + git add CertReports.Syncfusion/Services/Implementations/ExpiredAnagraficaSectionRenderer.cs + git commit -m "feat: rename Memoria Cedola and add natixis support to ExpiredAnagraficaSectionRenderer" + ``` + +--- + +## Task 7: Manual Verification + +- [ ] **Step 1: Run the application** + + ```bash + dotnet run --project CertReports.Syncfusion + ``` + +- [ ] **Step 2: Verify Memoria field (active cert)** + + Open: `https://localhost:{port}/api/report/by-isin/{ISIN_ATTIVO}` + + Expected: In the Analisi section (left column), "Memoria" appears below "Leva" as the 9th item. If `info.Memory` is empty, shows "—". + +- [ ] **Step 3: Verify Memoria field (expired cert)** + + Open: `https://localhost:{port}/api/report/by-isin/{ISIN_SCADUTO}` + + Expected: In the Analisi section, the field is labeled "Memoria" (not "Memoria Cedola"). + +- [ ] **Step 4: Verify Natixis=false (default)** + + Open: `https://localhost:{port}/api/report/by-isin/{ISIN}` (no natixis param) + + Expected: Tipologia box shows `info.Categoria` — same as before. + +- [ ] **Step 5: Verify Natixis=true** + + Open: `https://localhost:{port}/api/report/by-isin/{ISIN}?natixis=true` + + Expected: Tipologia box shows `info.Nome` instead of `info.Categoria`. + +- [ ] **Step 6: Verify cache keys are independent** + + Open the same ISIN first without natixis, then with `?natixis=true`. Both should generate fresh PDFs with the correct Tipologia value (not the cached one).