Monday, November 10, 2025
23 changes · 19.0
Enhancements to existing features
The bank statement validation check was optimized to run much faster by using a more efficient database query. This reduces processing time and database load, which can help the accounting app respond more quickly when checking many statements.
Original PR description
Description ----------- Avoid self-join of `account_bank_statement` that is done with a `Nested Loop` due to the `LATERAL`. Even if correlated, it requires two separate accesses to its index. Replaces it with a window function + `LAG` partitioned by the `journal_id`. This leads to a simpler plan (lower cost) and working in-memory instead of accessing disk pages (lower IO contention). Benchmark --------- On a database with 46k `account_bank_statement`, calling `_get_invalid_statement_ids` for all statements took: | [Before](https://explain.dalibo.com/plan/7605a4ddc42afbcf) | [After](https://explain.dalibo.com/plan/88d6ac1gee37aha3) | Speed-up | |--------|-------|----------| | 146ms | 72ms | 2x | --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#232980
When a website uses the cookie bar, new Google Fonts will now default to not loading directly from Google. This helps websites stay more aligned with GDPR expectations, and the updated help text makes the privacy impact clearer for users setting up fonts.
Original PR description
__Current behavior before commit:__ When the cookies bar is installed, Google fonts are still loaded from Google servers by default, which may violate GDPR requirements. See the PR adding the "Serve from Google"[1] option for more details. __Description of the change:__ If the cookies bar is enabled, it likely means that the website needs to be GDPR compliant. In this case the "Serve from Google" option is disabled by default when adding a new Google Font, preventing it from being served from Google servers. The tooltip and setting help text are also updated to clarify GDPR implications. [1]: https://github.com/odoo/odoo/pull/101129 task-5111612
Discuss now includes a dedicated live chat category for conversations marked as "Looking for Help." Agents also see a star indicator on chats that match their expertise, helping them spot and respond to the most relevant conversations faster.
Original PR description
Add a live chat category in the Discuss app that allows live chat agents to easily identify conversations marked as "Looking for Help." A star icon is added next to conversations that match the agent's expertise, making it easier to assist with conversations related to their area of expertise. part of task-5190237.
Resolved issues and error corrections
This update corrects a DHL field name used for customer references, preventing shipment validation errors when sending export declarations. It also adjusts the planned shipping date/time format to match DHL’s required standard, so return labels and related shipping actions complete successfully.
Original PR description
Currently, when creating a DHL shipment with an export declaration that includes a customer reference, there is a misspelling of the field 'recipientReference' as 'recepientReference'. This causes…
Currently, when creating a DHL shipment with an export declaration that includes a customer reference, there is a misspelling of the field 'recipientReference' as 'recepientReference'. This causes validation errors when communicating with the DHL API. In addition, the datetime format used for the planned shipping date and time does not conform to the expected format specified by DHL. Steps to reproduce spelling issue: 1. Create a Sales Order with a customer reference and a deliverable product. 2. Validate the SO. 3. Go to the delivery, select DHL as carrier, and confirm. → Error: Validation error #/content/exportDeclaration: extraneous key [recepientReference] is not permitted. Steps to reproduce datetime issue: 1. Create a delivery using the DHL carrier. 2. Confirm the delivery. 3. Return the delivery. 4. Click "Print Return Label". → Error: Bad request #/plannedShippingDateAndTime is not well formatted (expected format: '2010-02-11T17:10:09 GMT+01:00'). Official DHL documentation: https://developer.dhl.com/sites/default/files/2025-11/dpdhl-express-api-3.1.1_swagger.yaml opw-5024363 Forward-Port-Of: odoo/enterprise#98981
This fix ensures pricelist rules entered on a product variant are properly saved after you click Save. It prevents users from losing pricing changes, improving reliability when managing product prices in Sales.
Original PR description
steps to reproduce: ------------------- 1. Install sales 2. Enable Product Variants and Pricelists under Sales > Configuration 3. Open a product variant and create a pricelist rule 4. Save it issue:…
steps to reproduce: ------------------- 1. Install sales 2. Enable Product Variants and Pricelists under Sales > Configuration 3. Open a product variant and create a pricelist rule 4. Save it issue: ----- The pricelist rules are not saved. cause: ------ The field `pricelist_rule_ids` on `product.product` is a non-stored One2many. https://github.com/odoo/odoo/blob/b1041951d436b4b6adc90d8b8dd7b8c88cc69a46/addons/product/models/product_product.py#L62-L68 In the ORM, such fields are handled in the else part of [write_real()](https://github.com/odoo/odoo/blob/b1041951d436b4b6adc90d8b8dd7b8c88cc69a46/odoo/orm/fields_relational.py#L1033-L1049), Since this field is non-stored, it has no corresponding column in the database to persist changes. As a result, any updates made to it are lost after saving. solution: --------- Add an inverse method to make the field writable. opw-5097909 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#229521
The invoice synchronization process was using the wrong company in some cases. This update ensures invoices are grouped by company and synced separately, reducing the risk of incorrect synchronization results.
Original PR description
The sync process previously used the wrong company when synchronizing invoices. This fix groups invoices by their respective companies and performs the sync for each company individually. task-5207338 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#233148
This update makes Shiprocket work reliably when local GST tax labels are not available, preventing shipping-related errors in affected setups. It also corrects the test scenario so it uses a consistent tax rate, avoiding flaky test behavior.
Original PR description
Some other test adds a fiscal position with tax mapping. This creates 2 problems 1. The new taxes have a tag, which means we evaluate the right hand of…
Some other test adds a fiscal position with tax mapping. This creates 2 problems
1. The new taxes have a tag, which means we evaluate the right hand of
https://github.com/odoo/enterprise/blob/8d3fe30c627eada1186c15480876fb2e8f7ddb59/delivery_shiprocket/models/shiprocket_request.py#L239
However, since l10n_in is not installed,
`tax.env.ref(f"l10n_in.tax_tag_{gst}gst", False)`
does not return anything (False is not a fallback)
https://github.com/odoo/odoo/blob/1e96a3d127e8f521d3027f7110026ae84e11ed5e/odoo/api.py#L588
So we end up looking for `None` in `tax_tag_ids`, which leads us to compare the `_name` properties, see
https://github.com/odoo/odoo/blob/1e96a3d127e8f521d3027f7110026ae84e11ed5e/odoo/models.py#L6545-L6558
2. The test added in 4d5df93 was assuming the default company 15% tax, which was not always true
Solution
-----
1. Provide a fallback for the `ref` lookup
2. Force a 15% tax in the test
-----
runbot-232692
Forward-Port-Of: odoo/enterprise#99027
Forward-Port-Of: odoo/enterprise#97998When a product with only one non-variant option was added to the cart from the website, it could create a new line every time instead of updating the existing one. This fix makes those products behave like standard single-variant items, so repeat additions are handled correctly and the cart stays accurate.
Original PR description
Versions -------- - 18.0+ Steps ----- 1. Have a product attribute with variant creation set to never; 2. have product using only this attribute, with only a single value; 3. open website editor on…
Versions -------- - 18.0+ Steps ----- 1. Have a product attribute with variant creation set to never; 2. have product using only this attribute, with only a single value; 3. open website editor on /shop; 4. enable add-to-cart button & save; 5. use button on the aforementioned product at least twice. Issue ----- Each time the product gets added, a new line is created. Cause ----- Because there's only a single value, the product configurator doesn't pop up by default when adding the product to the cart. Without the configurator pop-up, the `_cart_update` method doesn't receive any `no_variant_attribute_value_ids`, which are used find a matching line if the product template has any `no_variant` attribute. Solution -------- Instead of checking `product.product_tmpl._has_no_variant_attributes()`, to see if we should attempt to match `no_variant_attribute_value_ids`, check if the added product has any `no_variant` attributes that have more than one value or are optional (multi-checkbox). If not, a `no_variant` product behaves the same as a single-variant product. opw-5093175 opw-5137351 Forward-Port-Of: odoo/odoo#234958 Forward-Port-Of: odoo/odoo#233452
This change fixes an error that could prevent users from saving an overtime rule when it is based on timing. The system now provides a default timing value, avoiding the crash and making rule setup work as expected.
Original PR description
This error occurs when no `timing_type` is set for the timing in the rule.
Steps to reproduce:
---
- Install `hr_attendance` module(without demo)
- Attendance > Configurations > Overtime Rulesets
- Create New Rule > Add an overtime rule in this ruleset with:
- Rule is based on: `Timing`
- Save & Close
- Now Save Rule
Traceback:
---
`KeyError: False`
At [1], this error occurs because the rule has no `timing_type` set, as no default value for field is defined.
[1]- https://github.com/odoo/odoo/blob/6e5146113adbdce3cbe317a7b3ef16be0a244b6a/addons/hr_attendance/models/hr_attendance_overtime_rule.py#L453
sentry-6986530016
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThe Amazon order fulfillment data now includes the carrier code required by some countries. If the carrier cannot be identified, the system uses a safe fallback so Amazon can still process the shipment correctly.
Original PR description
The `POST_ORDER_FULFILLMENT_DATA` feed that is used to push order delivery info to Amazon should follow the `OrderFulfillment` schema (see https://images-na.ssl-images-amazon.com/images/G/01/rainier/help/xsd/release_4_1/OrderFulfillment.xsd), but it was missing the `CarrierCode` element, which is required in some countries. This commit adds the missing element to the payload, with the formatted carrier name as a value. If the carrier name cannot be matched, "Other" is used as a fallback to signal Amazon that they should rely on the `CarrierName` instead. Forward-Port-Of: odoo/enterprise#99026
This update corrects how package-in-package handling is detected in barcode operations so the system keeps selecting the right package without changing the user experience. It also updates an automated test to match the revised order in which package lines are processed, helping prevent incorrect follow-up actions.
Original PR description
Some adaptations based on the community side: - Adapt the compute of `outermost_result_package_id` so nothing changes functionality-wise in barcode. - Adapt a test as the priority of move lines from 'Put in Pack' have changed. Task-5116567
This update fixes several issues around moving and splitting packed goods in stock operations. It makes pack actions more reliable, improves how package choices and labels are shown, and updates printed picking documents so warehouse users see the right package information.
Original PR description
Summary of the fixes for the Put/Pack in pack: - Avoid an error when trying to click on 'Move a Pack' on an unsaved picking. - Apply putaway rules at with multi-level packages, considering…
Summary of the fixes for the Put/Pack in pack: - Avoid an error when trying to click on 'Move a Pack' on an unsaved picking. - Apply putaway rules at with multi-level packages, considering higher-level packages first. - Consider higher-level packages when checking package routes. - Updates the picking operation pdf. - For pickings, no longer display the 'Packages' stat button if no package are set as destination. We keep it for batches however, as it's currently the only way to add entire packs into a batch. - Be more lenient with the 'Remove' button for packages in a picking. - Allow to select the right packages as destination in a picking. - Change priority of move line when using 'Put in Pack', always prioritizing `picked` lines. - Avoid `Unnamed` packages when creating a new package without a name from a list view. - Display the parent packages in the 'Pick from' auto-complete. For more detail about each fix, read the related commit. Task-5116567 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Quality checks created per quantity now keep the correct company instead of using the currently active one. This prevents errors in multi-company environments when confirming receipts that were created under a different company.
Original PR description
When creating quality checks per quantity, a multi-company error occurs if the active company differs from the one defined on the control point. Steps to reproduce: - Create a Quality Control Point for Company B and Product Table for the receipt operation with a per-quantity control. - Create a receipt in Company B for this product but leave it in draft. - Switch to Company A and try to confirm → error. Root cause: The company_id was not set when creating the quality check, causing it to default to `env.company` (A) instead of the stock move line’s company (B). opw-86993 Forward-Port-Of: odoo/enterprise#99076 Forward-Port-Of: odoo/enterprise#98655
This update prevents a crash that could happen when a user was redirected after their session had been refreshed. It ensures session data is saved while the needed request context is still available, so users are less likely to hit errors during normal navigation.
Original PR description
Steps to reproduce ------------------ Login to runbot, wait `odoo.http.SESSION_ROTATION_INTERVAL`s (3h) then access any page, then access any bundle using its id (not its `f'{name}-{id}') e.g.…
Steps to reproduce
------------------
Login to runbot, wait `odoo.http.SESSION_ROTATION_INTERVAL`s (3h) then access any page, then access any bundle using its id (not its `f'{name}-{id}') e.g. `/runbot/bundle/123`. Traceback: cannot use a closed cursor. It occurs when the system tries to save the session.
Traceback, explained
--------------------
1. The previous request session underwent a "soft rotation", its sid changed and it was marked "gc_previous_sessions".
2. The following request matches a route that is website & multilang, it is the case of the runbot bundle route.
3. The session was marked "gc_previous_sessions", it removes *now* the other sessions and removes the mark from the current one, this sets the current session dirty.
4. The request URL is not canonical, `_pre_dispatch` in `http_routing` abords with a redirection to `/runbot/bundle/18.0-123`.
5. The abortion (`werkzeug.exceptions.abort`) is an exception that bubbles up all the way up in the http stack, notably above `_serve_db` where `request.env.cr` is closed.
6. `odoo.http.root.__call__` ultimatelly processes the response received from `_pre_dispatch`: it calls `post_process` which call `_save_session` which sees that the current session is dirty: it attempts to save it.
7. Because there is a `session.uid`, we need to refresh the session token, for that we need an environment and a cursor.
8. Kaboom, the cursor was closed by `_serve_db`.
Problem
-------
When we set/delete items from the session, the changes are only saved in the session object in memory. The modified session is only written once, at the end of the request, if no error occured.
When we save the session, we gotta update the `session_token` value, this token is a SHA over the user's login information: active, email, password, ... and is verified upon loading the session in a future request. Should the user changes its password, a new token would be computed and all the sessions with the old token would become invalid. Should the user uses an old session, he would get redirected to /web/login.
Computing the `session_token` is mandatory when there's a `uid` inside the session, otherwise the session would be invalid. Computing it requires a valid cursor to fetch the user's active, email, password, ...
In our case, the (valid) response contained in `wekzeug.exception.abort` did bubble-up above `_serve_db`: `request.env.cr` is closed.
Solution
--------
Move the exception handling for `werkzeug.exceptions.abort` in both `_serve_nodb` and `_serve_db`. That it is in `_serve_db` makes it possible to call `_post_dispatch` with a cursor and environment that are still alive. This fixes the problem at hand.
With the above fix, comes two sanity things:
The `if session.uid and env` conditional is wrong, it must be only `if session.uid`. When there is a uid stored in the session, updating the `session_token` is mandatory. It MUST crash if `save()` is called for a session with a uid but with no environment.
The `_serve_db` method manages both the request's cursor and the `request.env` attribute. Everytime the cursor is updated (e.g. when changing from a read-only to a read+write one)This change reduces unnecessary database overhead when recalculating salary simulations for job offers. By using one database savepoint for the whole calculation instead of one per offer, it improves performance and responsiveness.
Original PR description
Savepoints are used in multiple computes related to salary simulation for an offer, because it creates and updates existing records, but those should not be committed to the database. Those are currently made for each offer that needs to be recomputed. Frequent savepoint, without commit/rollback of the whole transaction, are a performance issue at the database level. To partially alleviate this issue, this commit creates 1 savepoint for the whole compute, instead of per record to compute.
This update adjusts Odoo’s mail server handling so it works correctly on newer Debian releases and with updated Python SSL libraries. It helps prevent warning messages and connection issues when sending mail in those environments.
Original PR description
Forward-Port-Of: odoo/odoo#234020 Forward-Port-Of: odoo/odoo#232699
This update prevents a crash that could happen when multiple default records matched the same setting. The system now picks a single matching record, so users can save defaults reliably even if duplicate entries exist.
Original PR description
There’s no constraint preventing duplicate `ir.default` records. When setting a default using `self.env['ir.default'].set()`, it searches for an existing one, but if more than one match is found,…
There’s no constraint preventing duplicate `ir.default` records. When setting a default using `self.env['ir.default'].set()`, it searches for an existing one, but if more than one match is found, accessing `default.json_value` raises a singleton error.
This fix makes sure the search only picks one record, avoiding that crash.
Before fix:
```py
self: res.users(1,)
>>> company = self.company_id
>>> company
res.company(1,)
>>> self.env['ir.default'].create({'field_id': 4540, 'company_id': company.id, 'json_value': 7})
ir.default(9,)
>>> self.env['ir.default'].set('res.partner', 'property_account_receivable_id', 7, company_id=company.id)
Traceback (most recent call last):
File "/home/odoo/odoo/odoo/odoo/orm/models.py", line 5630, in ensure_one
_id, = self._ids
^^^^
ValueError: too many values to unpack (expected 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/odoo/odoo/odoo/odoo/addons/base/models/ir_default.py", line 107, in set
if default.json_value != json_value:
^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/odoo/odoo/orm/fields.py", line 1670, in __get__
record.ensure_one()
File "/home/odoo/odoo/odoo/odoo/orm/models.py", line 5633, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: ir.default(4, 9)
```
After fix:
```py
self: res.users(1,)
>>> company = self.company_id
>>> company
res.company(1,)
>>> self.env['ir.default'].create({'field_id': 4540, 'company_id': company.id, 'json_value': 7})
ir.default(10,)
>>> self.env['ir.default'].set('res.partner', 'property_account_receivable_id', 7, company_id=company.id)
True
```
opw-5228419
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#234944
Forward-Port-Of: odoo/odoo#234893Branch companies can now see and use payment providers set up for their parent company when customers pay online. This fixes a problem that previously blocked checkout with a “No compatible payment providers found” message.
Original PR description
Versions -------- - 17.0+ Steps ----- 1. Have a published payment provider; 2. create a branch company; 3. create a sales order in branch company; 4. enable online payment for sales order; 5. open sales order in portal view; 6. attempt to pay. Issue ----- No compatible payment providers found. Cause ----- The `payment.provider._check_company_domain` is set to the default exact match, so when it's used in `_get_compatible_payment_providers`, it's unable to find any providers for the branch company. Solution -------- Set `_check_company_domain` to `check_company_domain_parent_of`. opw-5214269 Forward-Port-Of: odoo/odoo#234821 Forward-Port-Of: odoo/odoo#234763
This update prevents a website access error when a product variant has a ribbon assigned. It ensures visitors can view product pages normally, avoiding a broken shopping experience for public users.
Original PR description
### Issue: An access error is shown on the website if ribbon is added to a product variant. #### To reproduce: 1- Create a db with `website_sale` installed. 2- Activate product variant. 3- Create a product variant and publish it. 4- Add a ribbon to the variant. 5- Using public user, navigate to the website product page. #### Cause: This is caused due to not having proper access rights. On stable we can use both the csv modification and adding sudo env to have an easier view update without need to upgrade the module. In master, we can keep the fix only in the csv file. opw-5224552
This change adds a regression test to make sure work entry generation still succeeds when an employee has overlapping leave periods, such as sick leave and a public holiday. It helps prevent a specific runtime error for fully flexible employees using attendance-based work entries, improving reliability in payroll-related processing.
Original PR description
**Purpose:** Add regression test to verify that overlapping leave scenarios (sick leave + public holiday) do not cause singleton errors for fully flexible employees using attendance-based work entries. **Test Coverage:** - Fully flexible employee with no calendar assignment - Attendance-based work entry source configuration - Overlapping sick leave and public holiday scenario - Work entry generation and validation without singleton errors Related : [PR](https://github.com/odoo/odoo/pull/223448) opw-4979974 Forward-Port-Of: odoo/enterprise#96664 Forward-Port-Of: odoo/enterprise#93902
This change prevents the employee version timeline from trying to use payroll-related data when a user does not have access to it. As a result, HR users can open employee forms reliably, and the timeline simply omits the extra contract grouping line when the needed information is unavailable.
Original PR description
Before this commit, the version timeline component needs to read contract_type_id and contract dates fields to be able to add an extra line below the versions to mention the versions inside the same contract. The problem is `contract_type_id` and the contract dates fields are not available when the user cannot see the payroll tab in the employee form. This commit makes sure the fields needed to display the extra line inside the version timeline are only used if the user has access to them otherwise no extra line will be displayed since some information is missing to be able to display it. task-5253864 X-original-commit: 0b1d4b006908b1ad993177f024d65a484d477078
This update fixes when Avatax-related fields and address validation appear on customer and product screens. It ensures the system reads fiscal country codes correctly, so users see the right options at the right time and Avatax data is handled more reliably.
Original PR description
**Changes:** - Updated the logic for showing address validation in `res_partner.py` to handle fiscal country codes more robustly. - Modified visibility conditions for `is_avatax`, `avatax_category_id`, `avatax_unique_code`, `avalara_partner_code`, and `avalara_exemption_id` fields in XML views to correctly parse and check fiscal country codes. **Purpose:** These changes ensure that the application correctly identifies when to display certain fields based on the fiscal country codes, enhancing the accuracy of the Avatax integration. This is made necessary because of changes to the _compute_fiscal_country_codes method introduced in commit https://github.com/odoo/odoo/commit/c518589716ebde6fb418d907ee01799dd7b889e9.
This change makes uploaded videos compatible with Odoo’s content protection rules, so a page edited by an admin can still be updated later by restricted users. It prevents video embeds from being stored in a way that causes the page to look uneditable, improving collaboration on website content.
Original PR description
Steps to reproduce the current behaviour: - Update the DEMO user to be a website "restricted editor" and sales "admin" who cannot bypass HTML field sanitization. - As ADMIN, add a YouTube video to a…
Steps to reproduce the current behaviour: - Update the DEMO user to be a website "restricted editor" and sales "admin" who cannot bypass HTML field sanitization. - As ADMIN, add a YouTube video to a product page > Save. - As DEMO, try to update the content on the product page > You cannot (a dialog informs you that you cannot edit the content because an admin edited it previously). Explanation: Starting from [1], an HTML field can be flagged as `sanitize_overridable` which allowed users with the `base.group_sanitize_override` group to skip the HTML field sanitize process. If such users added some content that is not considered "sanitize friendly" (e.g. YouTube iframe), a restricted user won't be allowed to add content in the fields, since the sanitizer will remove the original content from the DOM. For this case, the code from [2] added an implementation to consider the field as none editable and warn the user once he tries to update it. Implementation: The goal of this commit it to fix the current limitation for video upload that currently prevents non admin users to edit a website record once an admin adds a video on it... The idea of the fix is the following: - We already have a technical fallback when uploading a video to save the iframe `src` to an attribute: `data-oe-expression`. - The public widget is now destroying the video iframes so they are never saved in the DOM. - A non-lazy code will build the iframes immediately on page load. - The public widget can always create the iframes if they are not already created (for compatibility). [1]: https://github.com/odoo/odoo/commit/cf844e34dd0ce4830eb99fd0fa5b6b9cb58c867c [2]: https://github.com/odoo/odoo/commit/cb80c15d3db49ede3c93171abcaa9064b88822c6 task-3757205 Forward-Port-Of: odoo/odoo#233792 Forward-Port-Of: odoo/odoo#175717