Daily updates from Odoo
Tuesday, August 11, 2026
16 changes · saas-18.3
Resolved issues and error corrections
Users with Accounting Read-Only access can now see the General section in the Accounting tab on contact records, including bank account details. This fixes a view setup issue that accidentally hid information those users were already allowed to access.
Original PR description
Problem: The General group of the Accounting tab of the partner form view is not visible to some users, even if they have the access rights to see it. Steps to reproduce: 1. Create a user or edit an existing one, giving them Accounting Read-Only access rights. 2. Log in with that user. 3. Go to Contacts and select a partner. 4. Open the Accounting tab 5. Notice how the General group (with the bank account details) is not visible. Cause: In the account_accountant module, the partner form view is inherited in one of the views to add additional groups to the General group of the Accounting tab. However, it doesn't add the new group, but instead replaces the existing groups with the new one. opw-6413683
Fixes an error that could block users from editing worksheet design templates after upgrading multi-company data. This ensures Studio customizations work reliably when the same worksheet template exists across multiple companies.
Original PR description
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field. For example, in v17, a single worksheet template linked to 3 companies via the m2m field…
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field.
For example, in v17, a single worksheet template linked to 3 companies via the m2m field was returned as 1 record when opening Design Template. After the upgrade in v18, company_id became m2o, and the same data is split into 3 separate records (one per company).
When trying to add a customization via Studio, the search [fetches](https://github.com/odoo/enterprise/blob/18.0/worksheet/controllers/main.py#L12) records based on the model set on the worksheet. In the new version, Studio
[creates](https://github.com/odoo/enterprise/blob/18.0/worksheet/models/worksheet_template.py#L112)
a new model, but for existing records the
model is the same across the 3 worksheet records tied to the same template. This causes the search to match all 3 records and raise a SingletonError.
```py
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2856, in __call__
response = request._serve_db()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2331, in _serve_db
raise self._update_served_exception(exc)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/industry_fsm_report/controllers/main.py", line 9, in edit_view
action = super().edit_view(view_id, studio_view_arch, operations, model, context)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/worksheet/controllers/main.py", line 17, in edit_view
worksheet_template_to_change._generate_qweb_report_template()
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 490, in _generate_qweb_report_template
new_arch = self._get_qweb_arch(worksheet_template.model_id, report_name, form_view_id)
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 460, in _get_qweb_arch
if 'name' in row_node.attrib and row_node.attrib['name'] not in self._get_qweb_arch_omitted_fields() and row_node.attrib['name'] in form_view_fields:
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 378, in _get_qweb_arch_omitted_fields
'x_%s_id' % self.res_model.replace('.', '_'), 'x_name', # redundant
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1657, in __get__
record.ensure_one()
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5942, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: worksheet.template(3, 14, 18)
```
OPW: 6389190
Forward-Port-Of: odoo/enterprise#126773The UK CIS report now correctly shows payments linked to receipts that include CIS tax, not only vendor bills. This ensures businesses get a more complete and accurate CIS reporting view for affected transactions.
Original PR description
With the l10n_uk_reports_cis module installed: - Create a vendor bills and add a CIS tax --> This vendor's bills appear correctly in the report. - Create a receipt and add a CIS tax --> This type of bill appears in the report, but the payment is not showing up. opw-6282548 Forward-Port-Of: odoo/enterprise#124043
Odoo now recognizes five new response codes introduced by Chile's tax authority for supplier electronic documents. This prevents affected supplier documents from getting stuck during processing and keeps the workflow aligned with the latest SII rules.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
Global invoices for Mexican Point of Sale now ignore cancelled refund orders. This prevents invoice creation errors when a customer refund was started, cancelled, and later completed correctly.
Original PR description
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click…
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click "Return Products" to make a refund, but don't pay it, cancel it instead. 4. From the same order, click "Return Products" again to make a second refund, and pay it normally. 5. Go to the orders list, select the main order and the paid refund (not the cancelled one), then Actions > Create Global Invoice. -> Observation: error in the global invoice. In the CFDI tab of the main order the line is "Send Global In Error", and hovering on it the detail says "Failed to distribute some negative lines". Why: ---- When we make the global invoice, we remove the refunds from the order. A cancelled refund was never paid, so we should not count it. But we were counting it too. So we removed the refund amount twice in our case, one for the paid refund, and one for the cancelled one, and we end up with an order with negative amount that cannot be distributed. The fix: -------- We now skip the cancelled orders when we search the refunds, the same way it is done above when we collect the refunded orders. opw-6261404 Forward-Port-Of: odoo/enterprise#120996
l10n_vn_pos_symbol carries groups='base.group_system,point_of_sale.group_pos_manager', but _prepare_invoice_vals reads it while an ORDINARY salesperson closes an order: _process_saved_order calls _generate_pos_order_invoice as the acting user, with no sudo anywhere on the way. So on a Vietnamese POS with auto-send to SInvoice enabled, a user in point_of_sale.group_pos_user alone hit Access Denied by ACLs for operation: read, model: pos.config, fields: l10n_vn_pos_symbol and the order nev
Original PR description
l10n_vn_pos_symbol carries groups='base.group_system,point_of_sale.group_pos_manager', but _prepare_invoice_vals reads it while an ORDINARY salesperson closes an order: _process_saved_order calls…
l10n_vn_pos_symbol carries groups='base.group_system,point_of_sale.group_pos_manager', but _prepare_invoice_vals reads it while an ORDINARY salesperson closes an order: _process_saved_order calls _generate_pos_order_invoice as the acting user, with no sudo anywhere on the way. So on a Vietnamese POS with auto-send to SInvoice enabled, a user in point_of_sale.group_pos_user alone hit
Access Denied by ACLs for operation: read, model: pos.config, fields: l10n_vn_pos_symbol
and the order never finished syncing - the receipt screen's Done button stayed disabled and the checkout dead-ended.
This is reachable from the module's own test suite, which is what makes it more than theoretical: point_of_sale's start_pos_tour logs in as `pos_user` (group_pos_user, NOT group_pos_manager - that is `pos_admin`), so test_l10n_vn_edi_pos_refund_reason_tour exercises exactly the unprivileged path.
Reading the config as sudo is the right resolution rather than widening the field's groups: the salesperson is never shown the symbol or allowed to change it, it is only stamped onto the invoice their own sale produces, which is what a per-POS configuration field is for. Both symbols are read through the same sudo recordset so the company-level fallback keeps working identically.
Reproduced and verified on v18_full (5-repo addons path):
before - FAILED: [13/36] Tour L10nVnEdiPosRefundReasonTour -> "go to next screen from
receipt"; 1 failed, 0 error(s) of 1 tests
after - tour succeeded for both tours in the class; 0 failed, 0 error(s) of 2 tests,
and no ACL line left in the log
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#281119Prerequisite: ------------------------------------- 1. Configure outgoing mail server Steps to reproduce: ------------------------------------- 1. Install Contact and Sales modules 2. Create a Company contact and it's child contact as follows: * Type: Invoice Address * Keep the Name field empty and add any email 3. Create and confirm the sale order with the Child Contact as customer 4. Send the order by Email 5. Click on the mail icon on the Mail in Chatter Observation:
Original PR description
Prerequisite: ------------------------------------- 1. Configure outgoing mail server Steps to reproduce: ------------------------------------- 1. Install Contact and Sales modules 2. Create a…
Prerequisite:
-------------------------------------
1. Configure outgoing mail server
Steps to reproduce:
-------------------------------------
1. Install Contact and Sales modules
2. Create a Company contact and it's child contact as follows:
* Type: Invoice Address
* Keep the Name field empty and add any email
3. Create and confirm the sale order with the Child Contact as customer
4. Send the order by Email
5. Click on the mail icon on the Mail in Chatter
Observation:
-------------------------------------
The recipient name is displayed as false in the notification popover.
Issue:
-------------------------------------
The bug traces through two separate layers
1. Child contacts of type invoice, delivery, etc. are allowed to have `name = False`. `_to_store()` serialized only the name field for recipients, which produced `{'name': false}` for nameless contacts.
2. The frontend used `name` with no fallback and rendered 'false' https://github.com/odoo/odoo/blob/0b17840fb3cc72935e1a6302a057fb55c253c498/addons/mail/static/src/core/common/message_notification_popover.xml#L8
Solution:
-------------------------------------
1. Include `display_name` in the recipient serialization, since it is always computed and never empty
2. Use `displayName` as a frontend fallback when name is missing.
3. Used `sudo` in a condition to respect the multicompany access rights
**Before:**
<img width="576" height="314" alt="image" src="https://github.com/user-attachments/assets/26c7e67d-9ab2-4089-bd07-2871c2039f8f" />
**After:**
<img width="630" height="317" alt="image" src="https://github.com/user-attachments/assets/7c84afef-48b3-4243-bf6e-3bac7198ccf6" />
opw-5898004
Forward-Port-Of: odoo/odoo#267132Issue: ------- After the fix: https://github.com/odoo/odoo/commit/f1557211d9e7f83761bb36e4800e4c2f62b234c5 we can't create a child menu for a mega menu or a menu can't be a mega menu when there's an existing child menu except the case of top level menu i.e; (url: /default-main-menu) and that menu will have no parent_id obviously... Now, as per the above pr conditions the top level can be set as mega menu since it has no parent id. And in version 17.3 in the pr https://github.com/odoo/odoo/
Original PR description
Issue: ------- After the fix: https://github.com/odoo/odoo/commit/f1557211d9e7f83761bb36e4800e4c2f62b234c5 we can't create a child menu for a mega menu or a menu can't be a mega menu when there's an…
Issue:
-------
After the fix:
https://github.com/odoo/odoo/commit/f1557211d9e7f83761bb36e4800e4c2f62b234c5 we can't create a child menu for a mega menu or a menu can't be a mega menu when there's an existing child menu except the case of top level menu i.e; (url: /default-main-menu) and that menu will have no parent_id obviously...
Now, as per the above pr conditions the top level can be set as mega menu since it has no parent id. And in version 17.3 in the pr https://github.com/odoo/odoo/commit/47af533e9f5f721b63570d3b301951f3855384a1 a 'Jobs' menu is being created and its parent_id refers to that top level menu which we have set as mega menu. And when the records gets validated during migration the database will get blocked.
Solution:
-----------
Restrict the user by throwing the same user error, when checking/selecting the top level menu as mega menu since it has existing child menus.
Step to reproduce:
-----------------------
1. Create a database in version 17.0 with 'website_hr_recruitment' installed.
2. Go to website menus, set a top level menu(/default-main-menu) as mega menu.
3. Migrate the database to version 18.0 or more.
Traceback:
```
File "/home/odoo/src/odoo/18.0/odoo/models.py", line 5297, in _create
records._validate_fields(name for data in data_list for name in data['stored'])
File "/home/odoo/src/odoo/18.0/odoo/models.py", line 1636, in _validate_fields
check(self)
File "/home/odoo/src/odoo/18.0/addons/website/models/website_menu.py", line 95, in _validate_parent_menu
raise UserError(_("A mega menu cannot have a parent or child menu."))
odoo.exceptions.UserError: A mega menu cannot have a parent or child menu.
File "/home/odoo/src/odoo/18.0/odoo/tools/convert.py", line 603, in _tag_root
raise ParseError('while parsing %s:%s, somewhere inside\n%s' % (
odoo.tools.convert.ParseError: while parsing /home/odoo/src/odoo/18.0/addons/website_hr_recruitment/data/config_data.xml:13, somewhere inside
<record id="website_menu_jobs" model="website.menu">
<field name="name">Jobs</field>
<field name="url">/jobs</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">59</field>
</record>
```
Ref Images:
Before Fix:
<img width="1598" height="599" alt="image" src="https://github.com/user-attachments/assets/ef719945-a11b-4134-97f8-4b583c4ea6bc" />
After Fix:
<img width="1582" height="633" alt="image" src="https://github.com/user-attachments/assets/da326e45-0de8-4d42-ad47-845bfaedc84e" />
OPW - 6094298
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#263025### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/c215f066de [REL] 18.3.59 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/2f613c8067 [PERF] vectorization: specialize formula call for common arities [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/2abdf1182f [PERF] vectorization: inline generateMatrix [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/c215f066de [REL] 18.3.59 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/c215f066de [REL] 18.3.59 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/2f613c8067 [PERF] vectorization: specialize formula call for common arities [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/2abdf1182f [PERF] vectorization: inline generateMatrix [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/544e9f8f7c [PERF] vectorization: skip non-vectorized args in inner loop [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/580f105608 [PERF] vectorization: hoist argDefinitions out of vectorized inner loop [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/2d288cb53b [PERF] vectorization: hoist per-arg getter resolution out of inner loop [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) https://github.com/odoo/o-spreadsheet/commit/50b7b57b04 [PERF] vectorization: reuse args buffer across cells [Task: 6222157](https://www.odoo.com/odoo/2328/tasks/6222157) 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>
Clearing the cache in the `write` of `ir.module` caused issues when installing `website`. Having it in `create` was enough for the original use case that prompted the PR. So we remove the `write` in this commit. Reproduce: 1. Create a new empty db 2. Set language to German 3. Install Website 4. The install gets stuck and you can't access the db anymore. task-None Forward-Port-Of: odoo/odoo#281465
Original PR description
Clearing the cache in the `write` of `ir.module` caused issues when installing `website`. Having it in `create` was enough for the original use case that prompted the PR. So we remove the `write` in this commit. Reproduce: 1. Create a new empty db 2. Set language to German 3. Install Website 4. The install gets stuck and you can't access the db anymore. task-None Forward-Port-Of: odoo/odoo#281465
When edit_translations is set, convert_to_record wraps translated terms in branding spans. Related (non-stored) fields re-read that already-wrapped value and ran the same wrapping again, producing nested spans. Only wrap terms for stored fields so related Html inherits the source branding unchanged. Also keep data-oe-translation-state in HTML safe_attrs so sanitization does not strip it. Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior af
Original PR description
When edit_translations is set, convert_to_record wraps translated terms in branding spans. Related (non-stored) fields re-read that already-wrapped value and ran the same wrapping again, producing nested spans. Only wrap terms for stored fields so related Html inherits the source branding unchanged. Also keep data-oe-translation-state in HTML safe_attrs so sanitization does not strip it. 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 Forward-Port-Of: odoo/odoo#280997 Forward-Port-Of: odoo/odoo#280397
Problem: Clicking a non-editable link does nothing, making it impossible to open or inspect the link. Solution: Allow the link popover to open in read-only mode for non-editable links. Steps to reproduce: - Run `/article`. - Click on the inserted article link. - Observe that nothing happens. opw-6442026 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
Problem: Clicking a non-editable link does nothing, making it impossible to open or inspect the link. Solution: Allow the link popover to open in read-only mode for non-editable links. Steps to reproduce: - Run `/article`. - Click on the inserted article link. - Observe that nothing happens. opw-6442026 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
#### Description of the issue this PR addresses: - The predicate loop kept climbing ancestors above the editable root when no match was found inside it. The later containment check would discard such a match anyway, so the predicate should never run outside the editable in the first place. - This ran the predicate on at least 14 unnecessary ancestors up to `<html>`, costly if the predicate is expensive. Stop the search at the editable boundary instead. - `movenode_plugin` calls closestElement
Original PR description
#### Description of the issue this PR addresses: - The predicate loop kept climbing ancestors above the editable root when no match was found inside it. The later containment check would discard such a match anyway, so the predicate should never run outside the editable in the first place. - This ran the predicate on at least 14 unnecessary ancestors up to `<html>`, costly if the predicate is expensive. Stop the search at the editable boundary instead. - `movenode_plugin` calls closestElement on every mousemove, so its predicate was needlessly re-evaluated on those 14+ ancestors on every single mouse move. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280011
An old refactor left some data around that are in conflict with other records for the same model. 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 Forward-Port-Of: odoo/odoo#281276
Original PR description
An old refactor left some data around that are in conflict with other records for the same model. 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 Forward-Port-Of: odoo/odoo#281276
Description of the issue/feature this PR addresses: Odoo supports WebP image fields, but `base_import` validates remote images with `PIL.Image.open()`. Odoo intentionally leaves Pillow's WebP decoder unloaded, so a valid WebP URL is rejected as an unidentified image. Current behavior before PR: Importing a valid WebP image URL fails with `cannot identify image file`. Oversized and unsupported WebP files are also rejected by Pillow before the import-specific size policy can be applied. Desire
Original PR description
Description of the issue/feature this PR addresses: Odoo supports WebP image fields, but `base_import` validates remote images with `PIL.Image.open()`. Odoo intentionally leaves Pillow's WebP decoder…
Description of the issue/feature this PR addresses: Odoo supports WebP image fields, but `base_import` validates remote images with `PIL.Image.open()`. Odoo intentionally leaves Pillow's WebP decoder unloaded, so a valid WebP URL is rejected as an unidentified image. Current behavior before PR: Importing a valid WebP image URL fails with `cannot identify image file`. Oversized and unsupported WebP files are also rejected by Pillow before the import-specific size policy can be applied. Desired behavior after PR is merged: Use Odoo's existing WebP header parser for dimension validation. Valid WebP URLs import unchanged, unsupported WebP remains rejected, and the existing 42-million-pixel import limit remains enforced. Tests cover valid, unsupported, and oversized WebP URL payloads. The complete `test_base_import` suite passes (56 tests, 0 failures/errors). --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281600 Forward-Port-Of: odoo/odoo#276648
Before this commit, a call test where a participant card appears while in a call fails with: Unimplemented server route: /mail/rtc/channel/upgrade_connection The client sends that request a few seconds later, so only a test still running by then fails, as `Adjust view: switching between Tiled and Spotlight` does on master under load. This happens because the other participants of a test call are only records, so their peer connection negotiates no transceiver, and asking it to downl
Original PR description
Before this commit, a call test where a participant card appears while in a call fails with:
Unimplemented server route: /mail/rtc/channel/upgrade_connection
The client sends that request a few seconds later, so only a test still running by then fails, as `Adjust view: switching between Tiled and Spotlight` does on master under load.
This happens because the other participants of a test call are only records, so their peer connection negotiates no transceiver, and asking it to download the video of the card schedules a connection recovery instead. Recovering without a TURN server, which a test never has, asks the server to move the call to an SFU with that route, and the mock server implements join_call and leave_call only.
This commit registers the route with an empty handler, as no test has an SFU server to join.