Friday, September 11, 2026
5 changes · 19.0
Enhancements to existing features
Invoice sections that hide their detailed composition now handle taxes consistently across printed PDFs and exported XML files. This improves accuracy and alignment with Sales behavior by grouping collapsed sections by tax and keeping tax details clearer when prices are hidden.
Original PR description
Before this commit, collapsing composition in Accounting did not behave the same as in Sales. In Accounting PDFs, when a section was created and its composition was hidden, taxes were removed from…
Before this commit, collapsing composition in Accounting did not behave the same as in Sales. In Accounting PDFs, when a section was created and its composition was hidden, taxes were removed from individual lines and displayed on the section line as a comma-separated list. This commit aligns the behavior with the Sales app: - When collapsing composition, sections are grouped by tax. - When hiding prices, taxes are removed from the section line and kept on each individual line. Also, hiding composition in sections wasn't taken into account while exporting the XML. This commit introduces this behaviour for the XML invoices : - when we export section that hides the composition, we first group the sections by tax, then we create an invoice line with the section's name, with the right tax and amounts calculations. NOTE: This commit does not introduce price hiding in XML exports. task-6101592 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
External tax calculations now avoid an unnecessary clearing step before applying updated tax values. This can significantly reduce processing time for large invoices using external tax services, improving responsiveness without changing the tax calculation outcome.
Original PR description
[PERF] account_external_tax: avoid redundant tax clearing Avoid unnecessarily clearing `tax_ids` before setting the externally computed taxes, as the field is immediately replaced with the new tax…
[PERF] account_external_tax: avoid redundant tax clearing Avoid unnecessarily clearing `tax_ids` before setting the externally computed taxes, as the field is immediately replaced with the new tax values. Performance testing was performed using an invoice containing 200 lines with externally computed AvaTax taxes. Performance testing: | Metric | Before | After | |--------------------------------|--------|---------| | `_set_external_taxes()` | ~1:13 | ~43.77s | | Total external tax calculation | ~1:16 | ~46.66s | This reduces the execution time of `_set_external_taxes()` by approximately 40% for a 200-line invoice, while preserving the existing functional flow. The optimization is not specific to AvaTax and benefits the common `account.external.tax.mixin` flow when setting externally computed taxes on multiple lines. Profiler comparison: Before: <img width="1548" height="442" alt="image" src="https://github.com/user-attachments/assets/a5f2553c-a926-4324-88b2-743ed9214bf5" /> After: <img width="1429" height="440" alt="image" src="https://github.com/user-attachments/assets/c9c9c673-4654-4e11-b5f2-ffa4ecc87038" /> **opw-6472692**
When Mexican electronic invoice documents are marked as cancelled, the system now records cases where the related journal entry cannot be cancelled automatically. This helps support teams investigate cancellation issues faster without changing the business flow.
Original PR description
In the current state, when an edi document is set to cancelled, the journal entry will attempt to cancel. However, if the cancellation throws a `UserError`, the flow simply continues. This PR adds a logger call to record the failure and help investigate the potential causes. Forward-Port-Of: odoo/enterprise#123511
Logs produced during tests that simulate time now show the actual system time again, making runbot and long-running test logs easier to interpret. The simulated time remains available separately when needed, preserving diagnostic information while improving day-to-day log analysis.
Original PR description
When using faketime the logs are not showing the real time but the faketime one which can be confusing when listed in the runbot interface. Before the usage of the JSONFormatter, the logs were using the PostgreSQL time, which is the real time. This restores the previous behaviour. Having the faked time oustide JsonFromater is also painful: long running faketimed tests end up with most of their logs stamped to nearly the same real date, preventing any analysis of execution time based on the logs. One drawback of this change is the inability to identify at first sight whether a log was faketimed or not, but at this point it is just a tradeoff, and the faked_created field on the record still allows getting this info if needed. Forward-Port-Of: odoo/odoo#287455 Forward-Port-Of: odoo/odoo#287159
Point of Sale session closing now limits how much customer credit history it reviews when reconciling pay-later balances. This prevents very large customer account histories from causing excessive memory use or blocking session closure, while keeping the resulting payments and balances the same.
Original PR description
At session close, the pay later receivable lines are reconciled with every unreconciled posted line of the same partners in the PoS journal and PoS invoices, searched with no bound. A customer buying…
At session close, the pay later receivable lines are reconciled with every unreconciled posted line of the same partners in the PoS journal and PoS invoices, searched with no bound. A customer buying on credit accumulates one open line per order and none of them is ever matched until a settlement comes in, so this set only grows. On a database with 48k open items for a single partner, the close hands `_reconcile_plan` 55k lines over 28k moves; `_sync_dynamic_lines` then evaluates `m.line_ids.tax_ids` on every move of the container, prefetching the 262k lines of those moves. That is about 1 GiB: the worker is killed and the session cannot be closed. All of this to reconcile nothing most of the time: reconciliation only pairs opposite signs, and a session that merely adds charges brings no credit to allocate. When there is one, the engine consumes the open items oldest first (`date_maturity` or `date`) and stops when the credit is used up, so anything past that point is loaded for nothing. Look the open items up per partner, account and currency, and only call `_reconcile_plan` when the session's lines or the customer's open credits give something to allocate. Take the open debits in the order the engine consumes them - the PoS lines have no `date_maturity`, so the ordering is done in Python on `date_maturity or date` - and hand over only as many as the credits cover. Both lookups are capped at 2000 lines: a settlement larger than the oldest 2000 open items leaves its remainder as an open credit, allocated by the next close. The partials created are the same as before, only the size of the batch given to `_reconcile_plan` changes. opw-6529792 Forward-Port-Of: odoo/enterprise#130210