Report Export Enhancements
Agent: complete tasks in order. Mark
[x]when done. Each task is self-contained.
Task 1: Summary Statistics Block ⭐ Quick Win
Complexity: 2/5 | Files: 2
Add optional summary row showing sum/average/count for numeric columns.
Implementation
[MODIFY] report.py
- In
report_export(), compute aggregates foramountcolumn fromrows:python stats = { "sum": sum(r.get("amount", 0) for r in rows), "avg": sum(...) / len(rows) if rows else 0, "count": len(rows) } - Pass
statsto template context
[MODIFY] export.html
- Add toggle checkbox in
#pdf-controls: "Show summary" - Add summary row below table in both CSV/PDF views:
```html
Total: €{{ stats.sum|format_currency }}
```
Verification
- [ ] Export CSV with 5+ rows, verify sum row appears
- [ ] Toggle summary off, verify row hidden in export
Task 2: Date Range Auto-Label ⭐ Quick Win
Complexity: 2/5 | Files: 2
Auto-populate "Period: Jan 1 – Dec 31, 2025" from active filter state.
Implementation
[MODIFY] report.py
- In
report_export(), read filter state from session or query params - Extract
date_fromanddate_to, format as human-readable string - Pass
date_range_labelto template
[MODIFY] export.html
- Add editable input below subtitle in PDF header:
html <input id="report-period" value="{{ date_range_label }}" placeholder="Period..." /> - Include in PDF meta line
Verification
- [ ] Apply date filter on
/report, go to export, verify period auto-filled - [ ] Edit the period text, verify it exports with custom text
Task 3: Aggregate Total Row ⭐ Quick Win
Complexity: 1/5 | Files: 1
Show "Total: €X,XXX.XX" as final row in export table.
Implementation
[MODIFY] export.html
- After
{% endfor %}in tbody, add: ```html
Total {{ stats.sum|format_currency }}
``` - Use JS to dynamically recalculate if cells are edited
[MODIFY] report-export.js
- Add
recalculateTotal()function triggered on cell blur - Update total row value
Verification
- [ ] Export shows correct sum
- [ ] Edit an amount cell, verify total updates
Task 4: Excel XLSX Export
Complexity: 3/5 | Files: 2
Add native Excel export with formatting (SheetJS library).
Implementation
[MODIFY] export.html
- Add XLSX option to mode toggle:
html <input type="radio" name="export-mode" value="xlsx" aria-label="Excel" /> - Include SheetJS CDN:
https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js
[MODIFY] report-export.js
- Add
exportXlsx()function:javascript function exportXlsx() { const headers = getCsvHeaderInputs().map(i => i.value); const data = [headers, ...getRowsAsArrays()]; const ws = XLSX.utils.aoa_to_sheet(data); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Report"); XLSX.writeFile(wb, getFilename() + ".xlsx"); } - Wire up to export button when mode is "xlsx"
Verification
- [ ] Select XLSX mode, export, open in Excel
- [ ] Verify headers and data match edited values
Task 5: Template Presets (Save/Load)
Complexity: 4/5 | Files: 3
Save column config, title patterns, theme as reusable preset.
Implementation
[NEW] Preset data model
- Store in
data/users/{uuid}/report_presets.json:json [ { "id": "tax-export", "name": "Tax Export", "columns": ["booking_date", "merchant", "amount", "cl_category"], "title": "Tax Deductions {{year}}", "theme": "blue" } ]
[MODIFY] report.py
- Add endpoints:
GET /report/presets– list presetsPOST /report/presets– save new presetDELETE /report/presets/{id}– delete preset
[MODIFY] export.html
- Add preset dropdown + save button in header controls
- On select, apply preset values to inputs
Verification
- [ ] Create preset "Tax Export", reload page, verify it appears in dropdown
- [ ] Select preset, verify columns/title/theme applied
- [ ] Delete preset, verify removed
Task 6: Custom Header Fields
Complexity: 2/5 | Files: 1
Add editable fields: Preparer, Client, Account Number.
Implementation
[MODIFY] export.html
- Add fields after subtitle in
#pdf-header: ```html
``` - Include these in PDF render (hide in CSV mode)
Verification
- [ ] Fill custom fields, export PDF, verify visible
- [ ] CSV export should NOT include these fields
Task 7: Category Pie Chart in PDF
Complexity: 4/5 | Files: 2
Embed a mini pie chart showing spend by category.
Implementation
[MODIFY] report.py
- Compute category breakdown from selected rows:
python breakdown = {} for row in rows: cat = row.get("cl_category", "Unknown") breakdown[cat] = breakdown.get(cat, 0) + abs(row.get("amount", 0)) - Pass to template
[MODIFY] export.html
- Add Chart.js CDN
- Add canvas element in PDF preview (toggle-able)
- Render doughnut chart from breakdown data
- In
exportPdf(), convert canvas to image before html2pdf
Verification
- [ ] Select rows with multiple categories, verify chart renders
- [ ] Export PDF, verify chart appears as image
Task 8: Logo Upload/Branding
Complexity: 3/5 | Files: 3
Allow user to upload logo for PDF header.
Implementation
[NEW] Logo storage
- Store at
data/users/{uuid}/logo.png - Max size: 200KB, resize on upload
[MODIFY] report.py
- Add
POST /report/logo– save uploaded logo - Add
GET /report/logo– serve user's logo - Pass
has_logoboolean to export template
[MODIFY] export.html
- Add logo upload button in PDF controls
- Display logo in PDF header if exists:
html {% if has_logo %} <img src="/report/logo" class="h-12" /> {% endif %}
Verification
- [ ] Upload logo, verify displayed in preview
- [ ] Export PDF, verify logo appears
- [ ] Remove logo, verify header returns to text-only
Task 9: Merchant Grouping
Complexity: 3/5 | Files: 2
Collapse transactions by merchant into summary rows.
Implementation
[MODIFY] report.py
- Add query param
?group_by=merchant - Group rows and compute per-merchant totals:
python grouped = {} for row in rows: m = row.get("merchant", "Unknown") if m not in grouped: grouped[m] = {"count": 0, "total": 0} grouped[m]["count"] += 1 grouped[m]["total"] += row.get("amount", 0)
[MODIFY] export.html
- Add toggle: "Group by merchant"
- When active, show grouped view with count + total columns
Verification
- [ ] Enable grouping, verify merchants collapsed
- [ ] Export CSV with grouped view
Task 10: Print-Optimized PDF
Complexity: 2/5 | Files: 2
Better page breaks, repeat headers on each page.
Implementation
[MODIFY] export-theme-blue.css
- Add print media styles:
css @media print { thead { display: table-header-group; } tr { page-break-inside: avoid; } }
[MODIFY] report-export.js
- In
exportPdf(), configure html2pdf options:javascript pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
Verification
- [ ] Export 50+ row PDF, verify headers repeat
- [ ] Verify no rows split across pages
Task 11: Custom Footer Rows (Sum + Free-Text)
Complexity: 3/5 | Files: 2
Allow users to add custom rows after the data table: auto-calculated sum row, and/or free-text annotation rows.
Use Cases
- Sum row: "Total Expenses: €1,234.56"
- Subtotals: Group by category with subtotal per group
- Free-text: "All amounts exclude VAT" or "Approved by: _____"
- Signature line: "Signature: ___ Date: _____"
Implementation
[MODIFY] export.html
- Add "Add Row" button below PDF table with dropdown:
```html
```
- Row templates: ```html
```
[MODIFY] report-export.js
- Add
addCustomRow(type)function:javascript function addCustomRow(type) { const container = document.getElementById('pdf-custom-rows'); const template = document.querySelector(`[data-row-template="${type}"]`); const clone = template.content.cloneNode(true); container.appendChild(clone); if (type === 'sum') recalculateSum(); } - Ensure custom rows are included in PDF render
- Add drag-to-reorder capability (optional)
Behavior
| Row Type | Content | Editable |
|---|---|---|
| Sum | Auto-calculated from amount column | Label only |
| Text | Free-form input | ✅ Full |
| Signature | "Signature: ___ Date: ___" | Label only |
Verification
- [ ] Add sum row, verify it shows correct total
- [ ] Add text row, enter custom note, export PDF
- [ ] Add multiple rows, verify order preserved in export
- [ ] Remove a row, verify it disappears
- [ ] Edit cell values, verify sum row updates
Priority Order
| # | Task | Effort | Impact | Recommended |
|---|---|---|---|---|
| 1 | Summary Stats | Low | High | ✅ First |
| 3 | Total Row | Low | High | ✅ First |
| 11 | Custom Footer Rows | Medium | High | ✅ First |
| 2 | Date Range Label | Low | Medium | ✅ Second |
| 4 | Excel Export | Medium | High | ✅ Second |
| 10 | Print-Optimized | Low | Medium | ✅ Third |
| 6 | Custom Headers | Low | Medium | Third |
| 5 | Template Presets | Medium | High | Fourth |
| 9 | Merchant Grouping | Medium | Medium | Later |
| 7 | Category Pie | Medium | Medium | Later |
| 8 | Logo Upload | Medium | Medium | Later |