Friday, September 18, 2026
17 changes · saas-19.1
Enhancements to existing features
Searching products by available stock is now much faster in large inventories. This reduces wait times and database load when teams filter or report on free stock quantities.
Original PR description
**Problem:** Searching on free_qty can be slow when there are many stock.move and stock.quant. The search method uses _compute_quantities_dict() to compute the free_qty based on stock.move and…
**Problem:** Searching on free_qty can be slow when there are many stock.move and stock.quant. The search method uses _compute_quantities_dict() to compute the free_qty based on stock.move and stock.quant, which is expensive. **Solution:** A similar field, qty_available, has a faster search method based on stock.quant only. This method can be adapted to support fast searching on free_qty by also aggregating the reserved_qty and subtracting it from the qty_available. **Perf Table:** Record: product.template, with ~20k stock.picking and most products w/o moves. |Record count|Time before|Queries before|Time after|Queries after| |------------|-----------|--------------|----------|-------------| |10k |2.14s |214 |370ms |45 | |50k |6.06s |644 |829ms |50 | |100k |11.42s |1196 |1.05s |46 | opw-6266096 Forward-Port-Of: odoo/odoo#287363 Forward-Port-Of: odoo/odoo#270434
Resolved issues and error corrections
Peppol invoice sending no longer crashes when an invoice line has no product name or label. Users now receive the intended validation message, making it clear what needs to be fixed before sending the invoice.
Original PR description
### Steps to Reproduce: 1. Activate Peppol 2. Create invoice for customer with Peppol 3. Add line without product name or label 4. Send invoice through Peppol and observe the Traceback error ###…
### Steps to Reproduce: 1. Activate Peppol 2. Create invoice for customer with Peppol 3. Add line without product name or label 4. Send invoice through Peppol and observe the Traceback error ### Issue: When generating a Peppol invoice, there will be a Traceback error if an invoice line is missing both a product and a label. The XML builder evaluates the missing `cbc:Name` element as `None` instead of an empty dictionary. Therefore, attempting to directly subscript `['_text']` on it throws a TypeError traceback. This prevents the user from seeing the standard validation warning about the missing item data. ### Solution: We can update the document node constraint check to safely access the dictionary keys using `.get()` with an empty dictionary fallback. This prevents the traceback and ensures that the system will correctly display the intended validation error to the user. opw-6577441 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288713
Fixed an issue where the Point of Sale order screen could go blank when viewing orders for a company that had a delivery address without its own name. The system now uses the parent company name as a fallback, so staff can reliably find and review those orders.
Original PR description
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click…
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click "Orders" on the company Issue: The screen goes blank. Cause: The ticket screen is opened with the company name as search term. The server domain searches on `partner_id.complete_name`, so the orders of the address contact are returned too. They are then fuzzy matched on `getPartnerName()`, which returns `false` for a partner without a name, and `fuzzyLookup` calls a string method on it. The error is raised during rendering, so the whole app is destroyed. Fix: Make `getPartnerName()` always return a string, and fall back on the parent name, like the complete name does. This way the orders of the address contact are still listed when searching on the company name, instead of being filtered out by an empty name. opw-6578541 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288880
This update brings the spreadsheet component to its latest version and fixes issues affecting pivot table totals and demo content. Business users benefit from more accurate spreadsheet reporting, especially when pivot dimensions are hidden or collapsed.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/5661311d55 [REL] 19.1.36 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/5661311d55 [REL] 19.1.36 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/95c129db37 [IMP] demo: debounce xml template build [Task: 6574005](https://www.odoo.com/odoo/2328/tasks/6574005) https://github.com/odoo/o-spreadsheet/commit/b00d61a7f5 [FIX] demo: fix scorecard demo definition [Task: 6573109](https://www.odoo.com/odoo/2328/tasks/6573109) https://github.com/odoo/o-spreadsheet/commit/54396b9649 [FIX] pivot: wrong running total with collapsed/hidden dimensions [Task: 6569607](https://www.odoo.com/odoo/2328/tasks/6569607) https://github.com/odoo/o-spreadsheet/commit/bbf0e6ee34 [FIX] pivot: fix scope of `pivot_html_renderer` css [Task: 6523521](https://www.odoo.com/odoo/2328/tasks/6523521) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
The calendar view now switches correctly between desktop and mobile panels when the browser window is resized. This prevents the calendar legend/sidebar from staying open by default on small screens, improving the mobile user experience.
Original PR description
See commits Forward-Port-Of: odoo/odoo#288329
This fixes a Safari-specific issue where replacing selected text at the start of an editable note could delete the selection without inserting the typed character. Users can now edit notes and other rich text content more reliably in Safari.
Original PR description
When using Safari, if the first character of the editable is selected and a character is pressed, the selection content is removed, but the character is not inserted. It seems that Safari does not trigger the actual `input` event, nor its native behavior, if the initial anchor node is detached from the DOM after `beforeinput`. This commit avoids this issue by preventing Safari from proceeding with the insertion right after the deletion by instead re-triggering the `insertText` command. Steps to reproduce: - Use Safari - Go to a To do note - Select the first word - Press a letter => The first word was deleted but the letter was not inserted. task-6445669 Forward-Port-Of: odoo/odoo#288848 Forward-Port-Of: odoo/odoo#281223
Fixed an accounting issue where batch payments could remain marked as sent even after the related bill and payment were fully paid. This helps finance teams see accurate payment statuses and avoid unnecessary follow-up on already completed payments.
Original PR description
A payment on a journal without outstanding account has no journal entry, so it is matched as soon as it is paid. When the bill it pays is reconciled later on, _compute_state turns it to 'paid', but assigning a field from within a compute doesn't notify the fields depending on it: is_matched stays False and any batch payment holding that payment stays in 'sent' state. Mark is_matched for recomputation explicitly, as 18.0 already does since d8de234. Steps to reproduce: - On the Bank journal, leave the outstanding account empty on the outbound payment method line - Post a vendor bill - Register a payment on it - Put that payment in a batch payment and validate the batch - Create a MISC entry and reconcile the bill with it - Issue: bill and payment are 'Paid' but the batch stays 'Sent'. Forward-Port-Of: odoo/odoo#288460
Fixed an issue that blocked users from splitting an approved expense when it had a receipt attached. The split process can now copy the original receipt to the new expense lines while still preventing regular attachment changes on approved expenses.
Original PR description
Steps to reproduce: ---------------------------------------- 1. Install the Expense module. 2. Create an expense with a receipt/attachment and approve it. 3. Try to split the approved expense using…
Steps to reproduce: ---------------------------------------- 1. Install the Expense module. 2. Create an expense with a receipt/attachment and approve it. 3. Try to split the approved expense using the "Split Expense" button. Observation: ---------------------------------------- An Access Error is raised: "You can't add attachments to an expense once it has been approved." Issue: ---------------------------------------- When splitting an approved expense, Odoo duplicates the original expense into multiple split parts. Since the original expense is already in the `approved` state, the newly created duplicate records also have `state == 'approved'`. During the split process, Odoo copies the attachments from the original expense to the newly created split records: https://github.com/odoo/odoo/blob/e696bc516c97bc7157d52c5dba0b42e7cb8bab48/addons/hr_expense/wizard/hr_expense_split_wizard.py#L59-L61 Because `copied_expense.state` is 'approved', the create method of `ir.attachment` triggers an AccessError via the security validation https://github.com/odoo/odoo/blob/e696bc516c97bc7157d52c5dba0b42e7cb8bab48/addons/hr_expense/models/ir_attachment.py#L23-L27 Solution: ---------------------------------------- Bypass the attachment restriction if the creation is initiated from the split wizard. It only bypasses the validation during the split operation, preserving the security constraints for normal user uploads to approved expenses opw-6373579 Forward-Port-Of: odoo/odoo#276139
Bank synchronization no longer removes payment options that users previously configured on a bank journal. This prevents unexpected loss of incoming and outgoing payment setup while still adding any missing default options.
Original PR description
**Steps to reproduce:** - Install Accounting - Configure "Bank" journal: * Add several incoming payments * Add several outgoing payments - From Accounting dashboard, connect Bank (e.g. Odoo Bank Sync Demo) **Issue:** After bank sync, all the incoming/outgoing payments added on the Bank journal are removed. Only the default ones are re-created. **Solution:** Keep all the existing incoming/outgoing payments (i.e. payment method lines) and only create the default ones for the payment method types that don't exist. opw-6424407 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281658
Fixes an issue where a temporary planning shift could remain after adding it to a rental order failed because of a scheduling conflict. The change keeps the slot creation and rental order update together, so failed updates are fully rolled back and planning data stays clean.
Original PR description
Steps to reproduce: - On the Planning Gantt view setup resources with `role_sync_shift_rental=True`. - Drag to create a new shift. - Click "Add to Last Order" while the target Rental order requires the slot to be rescheduled to different dates, on a resource that has a slot overlapping these dates. - The reschedule creates a conflict with another slot on the same resource ,and the slot created from the drag remains. Before this commmit, The called `planning.slot` would get orphanated due to being created through a different save request by the gantt quick create before 'action_add_last_order` is called. After this commit, A single orm call is used for the `planning.slot` creation and `add_to_last_order`. If an error was to be thrown both will be unrolled with all respective computation, and no residual data remains. task-6389195
This fix prevents Kenyan eTIMS invoice numbering from moving backward after certain failed invoice submissions. It helps avoid duplicate invoice numbers and incorrect receipt details being copied between documents, improving tax filing reliability.
Original PR description
Give an eTIMS invoice number back to the sequence only when the failing call is the one that took it, and only when it is still the last one handed out. When sending a customer invoice fails with anything other than a timeout, the number is given back so that it is not consumed for nothing. current issue: - send an invoice, let it time out, so it keeps number N - send other invoices, so the sequence moves past N - send the first one again and let it fail with a non-timeout error - the sequence drops by one and the next invoice sent reuses a number Both documents then sit under the same number. On its next attempt the one that was never accepted finds the other one's filing through selectInvoiceDetails and copies its receipt details. opw-6502563 Forward-Port-Of: odoo/enterprise#131106 Forward-Port-Of: odoo/enterprise#129994
The Ecuador ATS tax export now consolidates activity from a company and its branches under the shared RUC, matching local filing requirements. This prevents branch invoices and tax totals from being omitted from the exported XML, improving accuracy for tax submissions.
Original PR description
In Ecuador, a company and its branches file under a single RUC, so the ATS is expected to consolidate all of them. However, currently the system exports only the active company report. Steps to…
In Ecuador, a company and its branches file under a single RUC, so the ATS is expected to consolidate all of them. However, currently the system exports only the active company report. Steps to reproduce: - Install the Ecuadorian localization - Create a parent company and a branch, assign the RUC of the parent to the branch, and set the legal name of the branch in Settings - Post a customer invoice with taxes in the parent (e.g. 750) and another one in the branch (e.g. 250) - Activate both companies in the company selector - Go to Accounting > Reporting > Tax return - Select "Report: 104 (EC)", the dashboard shows the consolidated values - Click on the gear icon next to "Tax Return" and select ATS Issue: The exported XML only contains the documents and the totals of the parent company (750). The branch (250) is omitted. Analysis: Currently the ATS export use self.env.company for all searches, which only retrieve the data of the first company set opw-6469807 Forward-Port-Of: odoo/enterprise#128430
Payments in the Mexican electronic invoicing flow now correctly inherit the payment method configured on the selected bank journal. This prevents payment complements from being sent with the wrong default method, improving compliance accuracy for SAT reporting.
Original PR description
Currently, payment way configured on a bank journal is not taken into account, payments will default to "03 Transferencia electrónica de fondos". Steps to reproduce: - Set the "Payment Way" of a bank journal to "04 Tarjeta de Crédito". - Manually create a customer payment in that journal - Look at the "Payment Way" of the payment, then send the payment complement (REP) to the SAT. Issue: The payment way is "03 Transferencia electrónica de fondos" and the REP carries FormaDePagoP="03" The one set on the journal is ignored. Analysis: We should evaluate the journal before the transferencia default so a payment inherits the payment way, and keep transferencia as the last possible value. opw-6493514 Forward-Port-Of: odoo/enterprise#130863 Forward-Port-Of: odoo/enterprise#130133
Accounting predictions now use the most recent previous entries instead of older records. This improves the relevance of suggested accounting values and helps users get more accurate predictive assistance during bookkeeping.
Original PR description
This commit: https://github.com/odoo/enterprise/pull/38830/changes#diff-6f6931855e0903ff0d3f2b39bd5703ceaef9bdf9a516862b83f38d2d5b21a232 change the order of the predictive queries, removing the sorting order by date. Based on the current docstring: https://github.com/odoo/enterprise/blob/66682012145e5116ebec0183102bc5e930c2c343/account_accountant/models/account_move.py#L676 this is not correct, as we expect to retrieve the previous 100 entries, rather than the oldest ones. Correcting the query order ensures that the most recent entries are considered for predictive purposes. opw-6558929 Forward-Port-Of: odoo/enterprise#131776 Forward-Port-Of: odoo/enterprise#131731
This fixes the Colombian Libro Diario report so comparison views no longer trigger a server error. It also ensures journal entries without a partner are included, helping companies keep legally required reports complete and accurate.
Original PR description
## [FIX] l10n_co_reports: fix UNION ALL crash with multiple column groups ### Issue: Any comparison mode (Period, Analytic, etc.) in the Libro Diario raises an Odoo Server Error ### Cause: Each…
## [FIX] l10n_co_reports: fix UNION ALL crash with multiple column groups
### Issue:
Any comparison mode (Period, Analytic, etc.) in the Libro Diario raises an Odoo Server Error
### Cause:
Each sub-query in the `UNION ALL` had its own `ORDER BY` SQL only allows one global `ORDER BY` on a `UNION ALL`, or parentheses around each query — neither was the case
### Steps to reproduce:
- Install `l10n_co_reports` and `accountant`
- Enable Developer Mode in Settings
- Open the Libro Diario report and click the gear icon (top right)
- In the Options tab, enable Period Comparison
- Enable the Comparison for the Previous Period
Before the fix, an error is raised
------------------------------
## [FIX] l10n_co_reports: include partnerless entries in Libro Diario
### Issue:
Journal entries without a partner are excluded from the report but are legally required to appear
### Cause:
`_get_domain` called `super()` which adds `('partner_id', '!=', False)` to the domain, filtering out all partnerless entries
The SQL query also used a `JOIN` instead of `LEFT JOIN` on `res_partner`, excluding lines with no partner at the DB level
### Notes:
`NULL` values for `partner_name` or `line_label` caused the JS to hide the corresponding column headers
`header.js` matches columns to their header by `column_group_index`/`expression_label` and skips `None` values
Fixed by using `COALESCE` to return an empty string instead
### Steps to reproduce:
- Install `l10n_co_reports` and `accountant`
- Create a Journal Entry without a partner or label
- Open the Daily Journal Report
Before the fix, the entry doesn't appear
After the fix, check that PARTNER and LABEL headers are visible
opw-6430728
Forward-Port-Of: odoo/enterprise#131824
Forward-Port-Of: odoo/enterprise#129674Submitting a draft Denmark VAT report no longer fails because the report now receives the required prior report settings when calculating its lines. This helps Danish VAT users complete submissions without being blocked by an unexpected error.
Original PR description
Currently, an error occurs when the user submits the draft Denmark VAT report. ``` TypeError: AccountReport.get_options() missing 1 required positional argument: 'previous_options' ``` When the user submits the draft Denmark VAT report, it gets the calculated lines of the current report by calling get_options method. However, get_options() requires the previous_options argument [1]. Since this argument is not passed [2], it raises the error. This commit ensures that an empty dictionary is passed as the previous_options argument when getting the report lines. [1]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/account_reports/models/account_report.py#L2126 [2]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/l10n_dk_reports/wizard/tax_report_wizard.py#L216 sentry-7380293202 Forward-Port-Of: odoo/enterprise#130320
Long accounting reports now scroll correctly on iPhone and iPad. This prevents the scroll indicator from being hidden behind report content, making mobile report navigation clearer and more reliable for users.
Original PR description
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its…
### Issue: On iPhone and iPad (reduced window), the scrollbar is hidden behind the report on long scrollable pages This affects all browsers on iOS and iPadOS — Apple forces the use of WebKit (its rendering engine) on all of them, including Chrome and Firefox ### Steps to reproduce (on iPhone): - Install `l10n_es` (contains scrollable reports by default) - Switch to the ES company - Open the Tax Report and try to scroll Before the fix, the scrollbar renders behind the report content ### Cause: `o_content` was added unconditionally in commit https://github.com/odoo/enterprise/commit/d60ea6e0f3245e394fd0cee5f22f52aff2a79e2e But its role differs between desktop and mobile, and its presence on mobile triggers a WebKit compositor bug On desktop, `o_content` is required: `.o_action` stays `overflow: hidden`, so only `.o_content` (`overflow: auto`) can scroll the report Per the CSS flexbox spec, a flex item won't shrink below its content size unless its `overflow` is not `visible` Without `o_content`, the div keeps `overflow: visible`, refuses to shrink, overflows `.o_action`, and the excess is silently clipped — content becomes unreachable, not just visually different On mobile, the framework flips scroll responsibility to `.o_action` (`overflow: auto`) and forces `.o_content` back to `overflow: initial` `o_content` is therefore not needed on mobile On WebKit (iOS/iPadOS), keeping `o_content` on mobile is harmful: it sits as a non-scrolling `position: relative` node between the real scroll ancestor (`.o_action`) and descendants that require special compositing — the sticky `thead` and the fixed-position mobile chatter This configuration causes WebKit's compositor to miscalculate the Root layer bounding box This geometry mismatch is the most likely explanation for why the native scroll indicator renders behind the report instead of on top of it Removing `o_content` on mobile avoids this node entirely and restores correct compositor geometry ### Notes: Tested on Android (Blink) with and without `o_content`: no visual difference and identical compositor layer geometry confirmed via Chrome DevTools — no regression introduced The `padding-bottom` on `.o_account_report_scroll_container` is unrelated — it is applied unconditionally and exists for a separate bug (last row clipped on scroll) opw-6191827 Forward-Port-Of: odoo/enterprise#128528