Monday, November 10, 2025
14 changes · saas-18.3
Resolved issues and error corrections
This update corrects two issues in DHL shipment requests: a misspelled customer reference field and an incorrect date/time format. As a result, DHL labels and return labels should now be created more reliably without avoidable validation errors.
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
Branch companies can now use payment providers set up at the parent company level when customers pay online from a sales order. This prevents checkout from failing with no compatible providers found and makes online payment work more smoothly across company structures.
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
When Odoo sends delivery information to Amazon, it now includes the carrier code required by Amazon in some countries. If the carrier cannot be matched exactly, Odoo will send a fallback value so Amazon can still process the shipment using the carrier name.
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 fix ensures returned goods are sent to the right place: subcontracted items go to the subcontractor location, while regular supplier returns go back to the supplier. It prevents returns from being routed incorrectly when a receipt includes both subcontracted and non-subcontracted products, reducing fulfillment errors and manual corrections.
Original PR description
Steps to reproduce the bug: - Create a storable product "P1" and "P2" with vendor "azure interior" - for P2 subcontracting BoM referencing "azure interior" as subcontractor and component "C1" -…
Steps to reproduce the bug: - Create a storable product "P1" and "P2" with vendor "azure interior" - for P2 subcontracting BoM referencing "azure interior" as subcontractor and component "C1" - Create a receipt for partner "azure interior" including 1 unit of P1 and 1 unit of P2 - Validate the receipt - Create a return for both P1 and P2 Problem: A picking is created with destination location set to the subcontracting location for both products, instead of setting the partner location only for subcontracted products. Solution: Ensure only the move line for subcontracted products uses the subcontracting destination location, while other returned products go back to the supplier location. When the `picking_id.partner_id`` is changed, it triggers a write on the picking, which in turn triggers a write on its moves, but only on the ones that are not scrapped. However, since the `scrapped`` field is a stored computed field, and as it hasn't been accessed before, it needs to be computed. And because its computation depends on `location_dest_id`, that field also needs to be recomputed. as a result, the `location_dest_id`` of the moves that we manually set may be changed unexpectedly. Therefore, in the `_create_return`` function, we check that the picking’s `partner_id` is different from the moves’ partner_id before updating it, to avoid unnecessary writes and the chain of recomputations that could alter our values. Resetting the picking’s partner_id based on the move’s partner_id could actually be removed in master, as it serves no real purpose, the move.partner_id itself is already derived from the picking’s partner_id. We just keep it in stable versions to avoid any unexpected behavior changes. opw-5208289 Forward-Port-Of: odoo/odoo#234521
Users with Recruitment access can now send referral campaigns without hitting an access error, even if they do not have employee permissions. This fixes a blocking issue in the recruitment flow so eligible users can complete the campaign action normally.
Original PR description
STEP TO REPRODUCE:
------------------
1- Give to Marc Demo the right : Recruitment / Administrator (be sure he doesn't have any right on Employees)
2- Log as Marc Demo
3- Go to Recruitment
4- Click on the three dots in kanban card
5- Click on Referral Campaign
6- Click on Send
You will have an access error
This user (with these groups) should be able to send a referral campaign
task-5082344
Forward-Port-Of: odoo/enterprise#98712
Forward-Port-Of: odoo/enterprise#96746Products with a non-variant attribute and only one available option will now be matched correctly in the cart instead of being added as a new line every time. This prevents duplicate lines for the same item and makes shopping cart behavior more consistent for customers.
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 updates how page assets are included in the appointment and rental website snippets. It helps keep these pages working with the latest Odoo asset structure and reduces the risk of display or loading issues after upgrades.
Original PR description
See https://github.com/odoo/odoo/pull/104836 task-2963840 Forward-Port-Of: odoo/enterprise#98250 Forward-Port-Of: odoo/enterprise#35153
This change stops website assets from being accidentally turned off when modules are updated. It helps keep older page elements working correctly, while unused assets are still cleaned up later by the system's regular maintenance process.
Original PR description
Before this commit when a module was updated all ir.asset records were reset to their defined `active` state, if defined. This causes assets related to old snippet versions to be made inactive even…
When users click a channel in the Discuss kanban view, Odoo now opens the channel’s form view instead of doing nothing. This makes it easier to review and manage channel details directly from the channel list.
Original PR description
**Steps to reproduce:** Open 'Discuss' Click on 'Channel' in the menu to display channels in kanban view Click on any channel **Cause**: The action 'mail.discuss_channel_action' did not include form view in view_mode. **Effect**: Clicking on a kanban card does not display the form view. **Fix**: Open the form view of a channel when clicking on a kanban card. Task-5076555 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change prevents Spreadsheet from crashing when users freeze and share a sheet that includes a boolean global filter. It ensures Excel export works reliably even when a filter has no selected value, avoiding interruptions for users.
Original PR description
The method `getFilterDisplayValue` would not handled `undefined` values for boolean global filters, causing crashes when exporting to Excel. Task: [5188932](https://www.odoo.com/web#id=5188932&cids=1&menu_id=4720&action=333&active_id=2328&model=project.task&view_type=form) Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change updates Odoo’s email server handling so it works correctly on newer Debian Trixie environments. It prevents mail-related warnings and failures caused by changes in how system libraries handle secure connections and local address resolution.
Original PR description
Forward-Port-Of: odoo/odoo#234020 Forward-Port-Of: odoo/odoo#232699
This change stops Hoot tests from creating unnecessary IndexedDB databases during full test runs. It helps keep automated testing cleaner and more reliable, while IndexedDB coverage remains in dedicated tests.
Original PR description
Disable IndexedDB in Hoot tests to avoid creating to much IndexedDB databases when running the full test suite. IndexedDB is still tested in dedicated tours.
This fix ensures Odoo only uses one matching default record when saving a default value. It prevents a rare crash caused by duplicate records, so users can set defaults without interruption.
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#234893This update prevents the Mail module from failing during uninstallation when related database fields have already been removed. It helps ensure the module can be cleanly removed and later reinstalled without database errors.
Original PR description
When uninstalling module mail, an override of `unlink()` deletes the activities of the records being deleted. However, this override crashes whenever columns of `mail.activity` have been dropped already. As a consequence, it may prevent the deletion of the field `mail_message_id` of model `mail.tracking.value`, and its table, too. And this causes the reinstallation of module mail to log error: ``` column "mail_message_id" of relation "mail_tracking_value" contains null values ``` See https://runbot.odoo.com/odoo/runbot.build.error/233618 for the cases where it failed. The fix consists in checking whether the columns of `mail.activity` still exist before searching for activities.
Before this commit when a module was updated all ir.asset records were
reset to their defined `active` state, if defined.
This causes assets related to old snippet versions to be made inactive
even if those old snippet versions are used inside existing pages.
It used to work when the activation of assets was made through view
inheritance because when views are defined through a `<template>` tag,
the `active` attribute is in fact ignored during updates since [1],
except for new records since [2].
This commit introduces an `<asset>` tag in the XML import format.
It is an alias of `<record ... model="ir.asset">` with the additional
feature that it avoids taking the `active` field into account during
updates for existing `ir_asset` records, just like `<template>` if the
`active` field is mentioned as attribute of the tag.
We then rely on the `website_disable_unused_snippets_assets` cron to
properly disable any unused asset at a later stage (note that the bug
being fixed here was mitigated by the fact that cron also re-enabled
assets which were disabled by mistake... but that might happen only a
few days later).
Another approach was to overload `_load_records_write` in `base`'s
`ir_asset.py` to avoid taking the `active` field into account when
updating records:
```py
def _load_records_write(self, values):
values.pop('active', None)
super()._load_records_write(values)
```
But this is not as stable because it changes the way `ir.asset` records
are imported when the `<record>` tag is used. In the end we chose to be
consistent and do exactly the same as `<template>`, as this also allows
more and should be entirely stable.
[1]: https://github.com/odoo/odoo/commit/2d296cb77922d33be2dc45b900191fac34bda429#diff-175c28787c272a219b9275f79262a48af9aa029e718f45077fd609737559e84eR803-R804
[2]: https://github.com/odoo/odoo/commit/f1c70d4cc943ac4eb81a85a9dc005de34cd2060a#diff-175c28787c272a219b9275f79262a48af9aa029e718f45077fd609737559e84eR801-R804
task-2963840
(Follow-up of https://github.com/odoo/upgrade/pull/3829)
Forward-Port-Of: odoo/odoo#233402
Forward-Port-Of: odoo/odoo#104836