| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Invoicing (account)
• Discuss (mail) |
| Community Apps Dependencies | Show |
| Lines of code | 6839 |
| Technical Name |
eh_account_dynamic_reports |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Invoicing (account)
• Discuss (mail) |
| Community Apps Dependencies | Show |
| Lines of code | 6839 |
| Technical Name |
eh_account_dynamic_reports |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
Dynamic Account Reports
Ten accountant reports through one OWL viewer, on a SQL-direct engine that stays responsive past 100k posted entries.
Why this module
Dynamic Account Reports
SQL, not the ORM
The hot path is a parameter-bound, posted-only, company-scoped query against account.move.line. No ORM overhead per row, so reports stay responsive past 100k posted journal items where slower engines stall.
A cache that cannot go stale
Results are keyed by an atomic per-company move-version counter that bumps only on a real account.move state change, with strict company-scope equality. You never see a stale number and never see one company's result leak into another's scope.
A trail you cannot edit
Every render writes one audit row, cache hits included, each pointing back at the compute it was served from. The model rejects edits and blocks deletes, so compliance keeps one immutable row per real user request.
Day in the life
An accountant pulls a P&L on a 100k-entry company, then proves it later.
The Profit and Loss kicks off the SQL builder directly against account.move.line. Posted-only filter, period bounds, multi-company scope and hierarchical account.group expand and collapse are all baked into the WHERE clause. The result hits the per-company cache keyed by the move-version counter, so subsequent renders return from cache until the next move state change bumps it. The accountant adds a prior-year comparative column, folds the operating section, and drills from a data line straight to the underlying journal items with the period and account filter preserved. The XLSX export carries the same currency number-format as the screen, cell for cell. Weeks later an auditor asks how that number was produced; the audit log still holds one immutable row for that render, pointing back at the exact compute that served it.
Edge cases
The cases most modules quietly ignore.
In the shipped code today, each one a place where a cheaper module silently does the wrong thing.
A cache hit still writes its own audit row pointing back at the source compute, so compliance keeps one row per real user request, not just the first render.
The audit log is append-only at the model level: write rejects any field outside an internal whitelist and unlink is blocked, so audit rows cannot be edited or deleted even by an administrator.
A render for companies one and two will not pick up a result cached for company one alone, because the cache requires exact sorted company-set equality. A naive m2m IN would have leaked across overlapping scopes.
Cache invalidation increments the version counter with a direct SQL column = column + 1 update, not a read-modify-write, so two concurrent posts cannot lose a bump. The counter also bumps only when a move state actually changes value, avoiding spurious invalidation on routine recomputes.
The General Ledger computes a true opening balance per account plus a running closing balance across the period, not just period movements.
Variance percentage is divide-by-zero safe: a zero prior against a non-zero current reports a 100 percent overrun rather than crashing. A Feb 29 window is shifted to Feb 28 of the prior year instead of raising.
Analytic filtering runs against the Odoo 19 jsonb analytic_distribution using the key-overlap operator, not a legacy many-to-many, so it matches the current data model.
What is inside
Built to do the job, end to end.
- Ten reports, one viewer. Profit and Loss, Balance Sheet, Trial Balance, General Ledger, Partner Ledger, Aged Receivable, Aged Payable, Cash Flow (direct and indirect), plus Customer and Vendor Statements and an Analytic Balance, all rendered through a single OWL viewer.
- Drill down to the ledger. Data lines (accounts, partners, journal items) click through to the underlying journal items or the journal entry form. Section headers, subtotals and computed rows are aggregates and are not drillable.
- Comparatives. Prior period and prior year columns are native, with divide-by-zero-safe variance percentages and leap-day-safe prior-year date shifting.
- Currency-aware export. Every monetary cell renders with the company currency symbol; the XLSX writer builds a matching Excel number-format string so the spreadsheet matches the screen cell for cell. PDF export ships a universal template with a brand footer.
- Hierarchy with memory. Sectioned reports nest by account.group with caret expand and collapse, and the fold state persists per user between sessions.
- Fixed aging buckets. Aged Receivable and Aged Payable share one base with a fixed five-tier layout: Not Due, 0 to 30, 31 to 60, 61 to 90, and 91 plus days overdue.
- Statements from the partner form. Customer and vendor statements print from a one-click button on the partner form. The customer statement also appears on the customer portal home for self-service when the Customer Portal Extra module is installed.
Honest about the edges
What this does not do, so nothing surprises you.
- No Tax Report is included in this module; the set is the ten reports listed above.
- No periodic snapshot tables or materialised views. The cache is an in-process per-execution result keyed by options hash and move version, not a scheduled snapshot.
- Comparatives cover prior period and prior year only. There is no custom-range comparator and no vs-budget comparison in this module.
- The replayable execution ID lives in the audit log and the in-memory payload, not printed in the exported file; the XLSX and PDF carry a generated-at stamp, not the execution ID.
- The custom report builder, saved views and scheduled email delivery live in the separate Dynamic Reports Pro module that builds on this one.
Odoo 19 dynamic financial reports, Odoo Community profit and loss report, Odoo balance sheet trial balance report, Odoo general ledger partner ledger, aged receivable aged payable Odoo, cash flow statement Odoo Community, drill down journal entries report, Odoo accounting report XLSX PDF export, fast accounting reports large database Odoo, report audit log reproducible Odoo
Please log in to comment on this module
Good!
Good!
Thank you.