Report engine
Website canonical copy: website/guides/build/report-engine.md mirrors guides/build/report-engine.html. Prefer editing the website HTML, then
make docs-mirror.
CSV/PDF download and CSV bulk import for workspace views. The engine lives only in sumeru/ (core/report/, core/sdk/report.go, web handlers, render + JS). Addons enable features with XML on views — no export/import Go code in sumeru_addons/ or sumeru_custom_addons/.
Reporting is opt-in per view. If a view has no report XML or widgets, no Report toolbar appears.
Architecture
flowchart TB
subgraph addon [Addon XML only]
ViewXML["view XML: report tag / attrs / widgets"]
end
subgraph sumeru_core [sumeru core]
Parser["parser → CapabilitiesFromView"]
Render["render: Report toolbar + JS"]
Web["web handlers"]
Engine["core/report"]
BaseWizard["base: sys.bulk.import views"]
SDK["core/sdk/report.go"]
Engine --> SDK
Web --> Engine
Render --> Web
BaseWizard --> Engine
end
ViewXML --> Parser
Parser --> Render
| Layer | Location | Role |
|---|---|---|
| Engine | sumeru/core/report/ | Export CSV/PDF, bulk template, staging, mapping preview, import execution |
| SDK | sumeru/core/sdk/report.go | Stable facade for core and optional addon formatters |
| Web | sumeru/core/server/web/ | Session routes for export, template, upload, confirm, cancel |
| Render | sumeru/core/engine/render/ | Toolbar on list/form/kanban when capabilities enabled |
| Frontend | core/engine/assets/js/ui/report-exchange.js | Field picker, download, upload → redirect |
| Wizard shell | sumeru/addons/base/ | sys.bulk.import model + mapping form XML only |
Enable on a view (XML)
Three equivalent ways to declare capabilities. The parser merges them into one capability set.
Option 1 — child (recommended)
<view id="view_crm_lead_list" model="crm.lead" type="list">
<report download="csv,pdf" upload="bulk" pdf_sizes="a4,legal,letter" modes="create,upsert"/>
<field name="name"/>
...
</view>
Option 2 — View attributes
<view type="list" model="product.product"
report_download="csv,pdf"
bulk_upload="1"
pdf_sizes="a4,legal,letter"
bulk_modes="create,upsert">
<field name="name"/>
</view>
Option 3 — Header widgets (form or list)
<view type="form" model="crm.lead">
<header>
<widget type="report_download" formats="csv,pdf" pdf_sizes="a4,legal,letter"/>
<widget type="bulk_upload" modes="create,upsert"/>
</header>
...
</view>
XML reference
| Attribute / element | Values | Meaning |
|---|---|---|
download / report_download / formats | csv, pdf (comma-separated) | Enable report download formats |
upload / bulk_upload | bulk, 1, true, yes, on | Enable bulk CSV upload |
pdf_sizes | a4, legal, letter | Allowed PDF page sizes (default: all three) |
modes / bulk_modes | create, upsert | Import modes offered in UI (default: both) |
Nested arch roots (, without wrapping ) also support and the view attributes above.
Supported view types
| View type | Download scope | Bulk upload |
|---|---|---|
| list | Rows matching the window action domain (max 500) | Yes |
| kanban | Same as list | Yes |
| form | Current record as a single-row export | Yes (single-row template) |
| pivot | Not supported in v1 | No |
Download flow
- User opens a view with download enabled.
- Clicks Report in the toolbar → chooses Download CSV or Download PDF.
- Field picker opens (defaults = visible view columns); user selects fields.
- For PDF, user picks page size (A4 / Legal / Letter).
- Browser downloads the file.
List/kanban exports respect the window action domain (same rows as the current list, capped at 500). Form export uses the open record only.
Bulk upload flow
Bulk import reuses the same engine as export (field whitelist, coercion, ACL). There is no silent direct import — every upload goes through mapping and preview before data is written.
sequenceDiagram
participant User
participant View as EnabledView
participant Engine as core_report
participant MapForm as sys_bulk_import
User->>View: Report → Bulk upload
User->>View: Select fields + import mode
User->>View: Upload CSV
View->>Engine: Stage file + create batch
Engine->>MapForm: Open mapping form
User->>MapForm: Map columns + Preview tab
User->>MapForm: Confirm import
MapForm->>Engine: ExecuteBulkImport
Engine->>View: Redirect with result message
Step details
| Step | What happens |
|---|---|
| Field selection | User picks model fields (same picker as download) |
| Template | Download import template → CSV with headers only for selected fields |
| Upload | POST /web/bulk/upload — file staged in sys.attachment, batch row sys.bulk.import created |
| Mapping | Standard form: Column mapping tab — CSV header → model field (or Skip) |
| Preview | Preview tab — first 10 rows validated after mapping |
| Confirm | Confirm import object action → create-only or upsert per batch mode |
| Result | Redirect back to source view with created / updated / skipped counts |
Create only — new rows only. Create or update (upsert) — updates when id is mapped and valid; otherwise creates.
Limits and security
| Limit | Value |
|---|---|
| Export row cap | 500 rows |
| Upload file size | 8 MB |
| Preview validation | First 10 rows (full validation); remainder counted in total |
| Staging | CSV in sys.attachment; batch on sys.bulk.import; cleaned up after confirm/cancel |
| Check | Requirement |
|---|---|
| Authentication | Signed-in session on all routes |
| POST requests | CSRF token required |
| Export / preview | Read ACL on target model |
| Import | Create (and write for upsert) ACL on target model |
| Fields | Whitelist from registered model metadata — only declared fields can be exported or imported |
What addons may and may not do
| Allowed | Forbidden |
|---|---|
Add or widgets to view XML | Custom export/bulk HTTP handlers in addons |
sdk.RegisterReportCellFormatter(model, fn) in init() | PDF/CSV libraries in addon go.mod |
| Business data in normal models | Second bulk-upload implementation or copied mapping UI |
To enable CRM lead export, add one line to the list view XML and update the module — no Go changes in the addon.
Optional cell formatting for exports:
import "sumeru/core/sdk"
func init() {
sdk.RegisterReportCellFormatter("crm.lead", func(model, field string, raw interface{}) string {
// return display text for export cells
return sdk.AsString(raw)
})
}
Bulk import wizard (sys.bulk.import)
Registered in the base addon:
- Model:
sumeru/addons/base/models/sys_bulk_import.go(thin registration only) - Views:
sumeru/addons/base/views/sys_bulk_import_views.xml - Logic:
core/report/(preview, execute, object actionsaction_confirm_import,action_cancel_import)
v1 limitations
- Pivot / graph export not supported
- No XLSX, scheduled reports, or email delivery
- No direct import without the mapping view
- Invoice HTML print routes remain separate from this engine
See also
- Enable report on a view — addon recipe
- Export and import (business users) — end-user steps
- HTTP routes — Report exchange
- SDK API — Report
- Views, actions, and menus — short XML summary