Friday, September 25, 2026
7 changes · master
Resolved issues and error corrections
This fixes a search issue where bills of materials linked to archived products could be missed, even when users explicitly searched archived records. Businesses can now reliably find historical or inactive manufacturing records when filtering by archived products.
Original PR description
When searching for an archived bom (filer archived activated), if the main product is also archived, the search on the produt will not find the bom. Steps to reproduce: ------------------- * Create…
When searching for an archived bom (filer archived activated), if the main product is also archived, the search on the produt will not find the bom.
Steps to reproduce:
-------------------
* Create product AA
* Create a bom for product AA
* Archive product AA
* Products > bom
* Search for "Archived" and Product: "AA" -> It will not find the bom
Observation:
-------------
When doing the search, it will start a web_search_read ['&', ('active', '=', False), ('product_tmpl_id', 'ilike', 'AA')].
While in _search it will decide if we filter active elements: https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/models.py#L5363-L5369 In the first level since "active = False" is in the domain (for the bom) it will not add the filter.
The _search will optimize the domain query level by level by calling optimize_full.
First the outher layer :
optimize_full (Domain '[]')> _optimize (DomainNary '&')> _optimize_step (DomainNary '&')
Then the children [1: (active bom), 2: (product template display name)]:
_optimize>optimize_step> ...
While optimizing the second child the domain condition is: [('product_tmpl_id', 'any', [('display_name', 'ilike', 'AA')])]
Since it's a any operation, in optimize_step, it will use the _optimize_any_domain_at_level:
https://github.com/odoo/odoo/blob/16aaaafd7d314c04f39b1f633af3b5e15b46d78b/odoo/orm/domains.py#L970-L972
After doing some checks it will continue to optimize per level with the related comodel:
https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/domains.py#L1389-L1393
Since it's an "any" operator it need to respect the following guidelines : https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/domains.py#L98-L101
this is no done in `_optimize_any_domain_at_level` since it resolves a relational field's 'any' domain by looking up a fresh comodel recordset without adjusting its context.
Which means that for the following level, on the product template, the active_name is not on the domain anymore and the context was not passed corretly, so _search will automaticly add the filter ("active = True").
opw-6514762
Forward-Port-Of: odoo/odoo#290380
Forward-Port-Of: odoo/odoo#285942Managers assigned as an employee's Time Off approver can now access that employee's Time Off button even if they do not have broader Time Off permissions. This ensures approvers can manage requests for their team without needing unnecessary access rights.
Original PR description
Issue: A user without any rights on Time Off should still be able to manage the requests of the users he's manager of. However, the computation for `show_leaves` only considers Time Off Officers/Admins and the employee themselves, while it should also consider the employee's Time Off approver. Fix: Include the employee's Time Off approver when computing `show_leaves`, allowing them to access the employee's Time Off smart button. Reproduction Steps: 1. Set a user (e.g. Marc Demo) to "No" for Time Off. Note: If reproducing in v19, Administrator rights on Employees are also needed to access the private employee form view where the issue occurs. 2. Set that user as another employee's Time Off approver. 3. Log in as that user and open the employee's form view. 4. Observe that the Time Off smart button is not visible. Related Tickets: opw-6445714 Forward-Port-Of: odoo/odoo#290047 Forward-Port-Of: odoo/odoo#281589
Point of Sale now avoids reusing a receipt number while a draft order may still exist locally, such as after a reload or second browser tab opens. This prevents duplicate receipt references on paid orders and helps keep printed receipts and fiscal integrations consistent.
Original PR description
Steps to reproduce: - Add a product to a draft order, do not pay - Open the same PoS in a second tab of the same browser (the first tab is sent to the backend), or reload while a draft is being…
Steps to reproduce: - Add a product to a draft order, do not pay - Open the same PoS in a second tab of the same browser (the first tab is sent to the backend), or reload while a draft is being removed - Pay the restored draft, then make and pay a new order Issue: Both orders carry the same pos_reference (e.g. 264-3-000701). The number is printed on both receipts and sent to fiscal integrations that use it as the unique invoice number. Cause: The receipt number is issued client side by DeviceIdentifierSequence, a per-device counter kept in localStorage that all tabs of a browser share. When a draft is removed, its number is pushed on `unsynced_number_stack` to be reused by the next order. `removeOrder` pushes the number synchronously, then deletes the order from IndexedDB without waiting for the transaction. If the page unloads before it commits (the tab is closed by another tab opening the same PoS, a redirect to the backend, a reload), the next load restores the draft from IndexedDB with its number while the stack hands that same number to the next new order. Both are then created on the server with different uuids and the same pos_reference: the server only deduplicates by uuid. Fix: Recycle the number only once the order is gone from IndexedDB. A new `recycleOrderNumber` helper waits on the deletion of the order before calling `saveUnusedNumber`; `removeOrder` uses it after `localDeleteCascade`. `deleteOrders` awaits the recycling so that a delete followed by a new order still reuses the freed number, as before. opw-6558992 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#290305 Forward-Port-Of: odoo/odoo#287667
This fix ensures the HTML editor toolbar shows the correct font size after users remove formatting from text. It also prevents unnecessary nested formatting and keeps the remove-format option disabled when only default styling is present, making editing behavior clearer and more reliable.
Original PR description
### Steps To Reproduce: - Add this content in the HTML editor: `<h2 class=display-3-fs>heading 2</h2>` - Select the heading text. - Click Remove Format. - Check the font-size input in the toolbar.…
### Steps To Reproduce: - Add this content in the HTML editor: `<h2 class=display-3-fs>heading 2</h2>` - Select the heading text. - Click Remove Format. - Check the font-size input in the toolbar. ### Description of the issue/feature this PR addresses: - The font-size class was removed from the block element, but the font-size input still displayed the value associated with the removed class. - The toolbar did not show the correct font size for default block classes (like `o_default_font_size`). - Applying a new font size inside default block classes created nested spans instead of splitting them. - The "Remove format" button remained enabled even when selection had no custom formatting (only default block classes). ### Desired behavior after PR is merged: - The font-size input is updated after removing the font-size class and correctly displays the font size of the resulting block element. - Update the `getFontSizeDisplayValue` utility to find correct CSS variable dynamically to also find the font size of default block classes. - Add default font size classes (like `o_default_font_size` and headings) to `format_class_predicates` resource in `font_plugin.js`. This allows them to be split and replaced instead of creating nested spans. task-6321166 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#290228 Forward-Port-Of: odoo/odoo#272023
Bank reconciliation partner searches now include contacts owned by a selected branch company's parent company, in addition to global contacts. This fixes a multi-company issue that prevented users at branch companies from selecting the right parent-company contacts during reconciliation.
Original PR description
When setting a partner from the bank reconciliation control panel, the partner lookup domain only considered global contacts and contacts directly linked to the selected company IDs. This caused a multi-company issue for branch companies, where users could not find contacts owned by their parent company. The domain is now updated to include: Global contacts (company_id = false) Contacts whose company is a parent of the selected companies (company_id parent_of companyIds) Forward-Port-Of: odoo/enterprise#132677 Forward-Port-Of: odoo/enterprise#118719
Attachments can no longer be removed from approval requests after they are approved, refused, or cancelled. This preserves the supporting documents tied to finalized decisions and helps keep approval records complete and reliable.
Original PR description
## Problem: When an approval request is in the approved, refused, or cancelled state, users are still able to remove attachments. This occurs because the chatter uses keep_on_messages, which updates the attachment to reassign its parent model rather than performing a deletion, bypassing the existing @api.ondelete check. ## Solution: Override the write method to check if an attachment is being detached from an approval request. If the linked request is in the approved, refused, or cancelled state, raise a UserError to prevent the modification. ## Steps to reproduce (runbot v20): 1. Install approvals. 2. Create and submit an approval request, attaching a file to it. 3. Validate, refuse, or cancel the approval request. 4. View attachment in the chatter and click the X button on the attached file to delete. 5. Notice that the attachment is successfully detached from the approval request without throwing a warning. opw-6596029 Forward-Port-Of: odoo/enterprise#132850
Australian payroll now defaults casual employees to the regular casual tax treatment instead of daily casual. This helps ensure eligible employees can have student loan withholding applied correctly.
Original PR description
Issue: Odoo doesn't provide tax treatments for Daily casual employee. However, it the employement basis is set as casual, it incorrectly defaults to RDXXXX (which is Daily Casual). This prevents the employee from having student loan withhold. This commit defaults it back to regular casual based on the tax free threshold status. Most common case. Daily casual case to be handled in Master. task - 6387496 Forward-Port-Of: odoo/enterprise#131469 Forward-Port-Of: odoo/enterprise#130752