Daily updates from Odoo
Friday, July 10, 2026
20 changes · saas-18.3
New functionality added to Odoo
Odoo now supports WhatsApp business-scoped user IDs, allowing businesses to continue identifying and contacting customers even when WhatsApp does not provide a phone number. It also stores WhatsApp's canonical phone identifier to improve contact matching across different phone number formats, and avoids crashes when handling WhatsApp error messages.
Original PR description
Add support for whatsapp business-scoped user ids as outline in the [documentation](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids) for their introduction this June. This effectively adds a table mapping BSUID to contacts to enable contacting users who contact the business directly, as the business will now not necessarily be provided with their number. Additionally the “whatsapp id”, i.e. the canonical form of the phone number as stored in whatsapp, is stored to help better match contacts regardless of formatting details in odoo and whatsapp. task-5476552 Forward-Port-Of: odoo/enterprise#117782
Enhancements to existing features
Attachment deletion is now much faster for Chilean electronic invoicing and stock documents. This reduces long waits and database load when removing large numbers of attachments, especially for companies with high transaction volumes.
Original PR description
## The problem Deleting attachments checks foreign key triggers. Lookups in `account_move` and `stock_picking` tables for `ir_attachment` related fields coming from `l10n_cl_edi` overrides were slow due to missing indexes. ## The solution Added needed indexes to optimize triggers' lookups. ## Benchmark Time benchmark (deleting attachments from a customer database with 224K account moves and 204K stock pickings): |# of rows|Time (Before)|Time (After)| |----------|--------------|-------------| 100 | 29s | 16ms 1000 | 285s | 300ms OPW-6331845 Forward-Port-Of: odoo/enterprise#123350 Forward-Port-Of: odoo/enterprise#123252
In the even template, on the questions page, we can't drag and change the order of the questions. To fix it, we'll add the sequence field with handle widget to event.type questions list view to enable drag-and-drop reordering matching the behavir in event.event. Steps to reproduce: 1.Go to event templates 2.Select any templates 3.Go to questions page 4.We can't drag and drop questions opw-6260478 Forward-Port-Of: odoo/odoo#270576
Original PR description
In the even template, on the questions page, we can't drag and change the order of the questions. To fix it, we'll add the sequence field with handle widget to event.type questions list view to enable drag-and-drop reordering matching the behavir in event.event. Steps to reproduce: 1.Go to event templates 2.Select any templates 3.Go to questions page 4.We can't drag and drop questions opw-6260478 Forward-Port-Of: odoo/odoo#270576
No verification was done on the contact email in the wizard but if the email is invalid the user would have an error later task-6344324 Forward-Port-Of: odoo/odoo#274065
Original PR description
No verification was done on the contact email in the wizard but if the email is invalid the user would have an error later task-6344324 Forward-Port-Of: odoo/odoo#274065
Previously, bills could only be retrieved from the KSeF platform via a scheduled cron job, leaving users with no option to manually sync documents on demand. An "Import from KSeF" action has been added to the gear (cog) menu within both the list and Kanban views. Clicking this option triggers the synchronization process immediately and reloads the active view. Task [link](https://www.odoo.com/odoo/project.task/6306892) task-6306892 Forward-Port-Of: odoo/odoo#271459
Original PR description
Previously, bills could only be retrieved from the KSeF platform via a scheduled cron job, leaving users with no option to manually sync documents on demand. An "Import from KSeF" action has been added to the gear (cog) menu within both the list and Kanban views. Clicking this option triggers the synchronization process immediately and reloads the active view. Task [link](https://www.odoo.com/odoo/project.task/6306892) task-6306892 Forward-Port-Of: odoo/odoo#271459
Resolved issues and error corrections
Canadian EFT export files now populate each payment's Item Trace Number with a real payment identifier instead of zeros. This helps ensure payment batches meet CPA-005 requirements and avoids bank rejections caused by invalid trace numbers.
Original PR description
Issue: The Item Trace Number according to CPA-005 standard should be a nonzero sequence that serves as unique reference ID for payments. Currently, Odoo sets the Item Trace Number of all payments as…
Issue: The Item Trace Number according to CPA-005 standard should be a nonzero sequence that serves as unique reference ID for payments. Currently, Odoo sets the Item Trace Number of all payments as a zero-filled sequence According to CPA-005 standards on the Item Trace Number: "The data elements (b), (c) and (d) each must be greater than zero or the TRANSACTION WILL BE REJECTED" (page 36). https://www.payments.ca/sites/default/files/standard005eng.pdf Steps to reproduce: 1. Install the module l10n_ca_payment_cpa005 2. Go into "CA Company" 3. In the configuration for "CA Company", add something to the fields "Short Name used in Canadian EFT" and "Company ID" i.e. "CCC" 4. Set all the fields in the "Canadian EFT/CPA Configuration" section of the bank journal 5. Set the bank record on the bank journal. Set the field "Financial Institution ID Number" field of the "Account Number" record of the bank journal to any numerical sequence 6. Create a bank account on "Azure Interior" and make sure to check the field to trust the bank account that you created (otherwise there will be an error) 7. Create two payments with the vendor of "Azure Interior" using the payment method of "Canadian EFT" 8. Create a batch payment for both payments created 9. Validate the batch payment and the export file should show up in the chatter 10. Note that in the export file, the Item Trace Number for each payment is set to be all zeros, whereas it should be a nonzero identification sequence Solution: Set the Item Trace Number to be the payment's id opw-6323432 Forward-Port-Of: odoo/enterprise#123633
This fix ensures status messages in French reporting exports display with the intended formatting when reports are accepted or rejected. It prevents confusing or unreadable error logs, helping users understand report outcomes more clearly.
Original PR description
A mismatch between error titles and status logs was introduced in 18.0. Markup wasn't added to the status logs, leading to a type mismatch (Markup + str) when displaying errors for 'accepted' or 'rejected' statuses. As a result, the logs were not interpreted as HTML. This commit ensures Markup is applied to each element to guarantee coherence and proper rendering. backport of 5113752 task-6053842 Forward-Port-Of: odoo/enterprise#123250
Fixed an issue where expanding a Knowledge article in the sidebar could show only a favorited child article and hide its other children. Users can now reliably see the complete article structure without needing to refresh or reload.
Original PR description
The sidebar always loads the user's favorite articles along with the visible ones, so a favorited article is shown as a root of the favorite tree. When that favorite is also a child of a folded…
The sidebar always loads the user's favorite articles along with the visible ones, so a favorited article is shown as a root of the favorite tree. When that favorite is also a child of a folded article, it gets added to its parent's child_ids in the main tree, even though the parent's other children were not fetched. A folded article only gets its favorited children back from get_sidebar_articles, not its whole child set. When the parent is then unfolded, unfold() only read the children from the database when child_ids was empty. The favorited child already filled child_ids, so the call was skipped and the remaining children stayed hidden until the next reload. unfold() now uses a new children_loaded flag instead of the length of child_ids to decide whether to fetch the children. The flag is set once an article's whole child set is loaded: in loadChildren(), and in loadArticles() for the articles that were unfolded, since those come back with all their children. loadChildren() also rebuilds child_ids from the search result so a favorite already loaded is not added twice. The fix lives in the sidebar component because the partial child_ids only exists on the frontend, get_sidebar_articles already returns the right records. Steps to reproduce: 1. Open the Knowledge app 2. Create an article with two child articles 3. Add one of the two children to your favorites with the star icon 4. Open another article that is not under that parent 5. Fold the parent article in the sidebar, then refresh the page 6. Expand the parent article => only the favorited child is shown under the parent, the other child is missing Ticket [link](https://www.odoo.com/odoo/project.task/6186466) opw-6186466 Forward-Port-Of: odoo/enterprise#118804
Changing a project's visibility now ignores linked document shortcuts, preventing an unnecessary warning from appearing. This helps users update project access settings smoothly when shortcuts exist in project documents.
Original PR description
In 18.0 versions, when there is a shortcut document linked to a project, a warning appears as the access rights of a shortcut document is changed. This fix filters out the shortcut documents beforehand. To reproduce on runbot: 1. Go to a project with documents 2. Go to documents 3. Create shortcut of a document 4. Try to change visibility of project, warning occurs opw-6353516 Forward-Port-Of: odoo/enterprise#123184
Fixed an issue in Documents where the Actions menu could stop opening after selecting more than one document. Users can now reliably perform bulk actions, while selection counts also stay accurate after uploads.
Original PR description
***Issue:*** Since https://github.com/odoo/enterprise/commit/8baaad621a1555680cd77ab514f626137b5b584d the div holding the selection box and the Actions menu carries t-key="targetRecords.length" in…
***Issue:*** Since https://github.com/odoo/enterprise/commit/8baaad621a1555680cd77ab514f626137b5b584d the div holding the selection box and the Actions menu carries t-key="targetRecords.length" in the documents list and kanban views, so every change of the selection count destroys and recreates everything inside it. Ticking a document checkbox updates both the selection and the focused record, and with these two updates the recreation goes wrong: the new ActionMenus component ends up destroyed while its button is still in the page. The dropdown click listeners are removed with the component, so clicking Actions does nothing until the selection changes again. That t-key was added because the selection box shows a stale count after a file upload. The real problem is that SelectionBox caches this.root once in setup. The upload flow reloads the model, which replaces the root record list, and the box keeps counting the selection of the old one. ***Fix:*** Remove the t-key from documents_list_controller.xml and documents_kanban_controller.xml and make DocumentsSelectionBox read the current props.root instead of the value cached by the parent setup. The count follows the new record list after an upload without remounting anything, and the Actions menu is not recreated so its dropdown keeps working. The upload scenario stays covered by the existing multi_view.test.js test, a new test checks that the Actions menu opens with two selected documents. ***Steps to reproduce:*** 1. Open the Documents app 2. Switch to the list view 3. Tick the checkboxes of two documents 4. Click the Actions button next to the "2 selected" box => the Actions dropdown does not open Ticket [link](https://www.odoo.com/odoo/project.task/6365618) opw-6365618
The Argentine VAT Book ZIP export no longer fails for foreign partners marked as foreign providers with a foreign identification number. This helps accounting teams generate required VAT reports without manual workarounds or blocked exports.
Original PR description
Steps to reproduce: - Create a partner with: - State: Ireland - Identification Number: Foreign ID 55000004153 - ARCA Responsibility Type: Proveedor del Exterior - Create an invoice for the partner - Accounting > Reporting > Tax report - Select Report: VAT Book (AR), Tax Type: Sales - Click on gear icon > VAT Book (ZIP) Issue: Action will be blocked with error "No VAT configured for partner [58] <partner>" Analysis: Partners with ARCA responsibility type 'Proveedor del Exterior' (code 8) and a ForeignID identification type, causes a UserError when exporting the VAT Book (ZIP). Code 8 (foreign provider) is the purchase-side counterpart of code 9 (foreign customer), which already fell back to the country-level VAT. Extend the existing fallback branch to cover both codes. opw-6316008 Forward-Port-Of: odoo/enterprise#123506
**Steps to reproduce:** - Install the `l10n_it_edi` module and switch to an IT Company. - Create and confirm an invoice for a non-Italian customer. - Send the invoice, making sure that only `by Email` is enabled. - Attempt to send the invoice again. **Issue:** - In `18.0`, the XML file is not generated when re-sending an invoice that was previously sent only by email. - Starting from `19.0`, attempting to `Send to Tax Agency` raises an error: `UnboundLocalError: cannot access local
Original PR description
**Steps to reproduce:** - Install the `l10n_it_edi` module and switch to an IT Company. - Create and confirm an invoice for a non-Italian customer. - Send the invoice, making sure that only `by…
**Steps to reproduce:** - Install the `l10n_it_edi` module and switch to an IT Company. - Create and confirm an invoice for a non-Italian customer. - Send the invoice, making sure that only `by Email` is enabled. - Attempt to send the invoice again. **Issue:** - In `18.0`, the XML file is not generated when re-sending an invoice that was previously sent only by email. - Starting from `19.0`, attempting to `Send to Tax Agency` raises an error: `UnboundLocalError: cannot access local variable 'attachment_name' where it is not associated with a value` **Root cause:** At [1], `_get_alerts` method does not check whether the invoice was previously sent only by email. As a result, the warning banner is not displayed, and allows the user to `Send to Tax Agency`. **Fix:** Restore the expected behavior by preventing `Send to Tax Agency` when the invoice was previously sent only by email. Instead, display the appropriate warning message instructing the user to delete the PDF attachment before sending to the Tax Agency, matching the behavior in `17.0` (confirmed with PO). [1]: https://github.com/odoo/odoo/blob/c0d8d36481e106f0209521bbb127cb3b1ad1059a/addons/l10n_it_edi/models/account_move_send.py#L27-L33 opw-6293519 Forward-Port-Of: odoo/odoo#269547
**Steps to reproduce:** - Enable 2FA - Change user language - Log in in another private window / device - Check the notification email of a login with another device - Email body/subject are not properly adapted to user language **Issue:** View manual rendering doesn't pass the user language. **Fix:** Add it to the context before `_render_template` and subject translation (reapply similar fix [1]). [1] https://github.com/odoo/odoo/commit/4d8d1736ca03a3d6b4e86cbc7463a79a284d1f3c
Original PR description
**Steps to reproduce:** - Enable 2FA - Change user language - Log in in another private window / device - Check the notification email of a login with another device - Email body/subject are not properly adapted to user language **Issue:** View manual rendering doesn't pass the user language. **Fix:** Add it to the context before `_render_template` and subject translation (reapply similar fix [1]). [1] https://github.com/odoo/odoo/commit/4d8d1736ca03a3d6b4e86cbc7463a79a284d1f3c opw-6042550 Forward-Port-Of: odoo/odoo#272592 Forward-Port-Of: odoo/odoo#261468
Steps to reproduce ------------------- - Install sale_project, accountant and project_timesheet_forecast_sale modules; - Activate analytic accounting in the settings; - Add an outstanding account to the bank journal’s manual outgoing payment method; - Create a new billable project; - Open the top menu, add vendor bills and open it; - Create a new bill from there, it should use the project’s analytic distribution; - Confirm it and create a payment; - Open the payment’s journal entry, i
Original PR description
Steps to reproduce ------------------- - Install sale_project, accountant and project_timesheet_forecast_sale modules; - Activate analytic accounting in the settings; - Add an outstanding account to…
Steps to reproduce ------------------- - Install sale_project, accountant and project_timesheet_forecast_sale modules; - Activate analytic accounting in the settings; - Add an outstanding account to the bank journal’s manual outgoing payment method; - Create a new billable project; - Open the top menu, add vendor bills and open it; - Create a new bill from there, it should use the project’s analytic distribution; - Confirm it and create a payment; - Open the payment’s journal entry, it is using the analytic distribution too; Why is it happening -------------------- When opening a vendor bill from the project, the project_id is added to the account.move's context to use the correct analytic distribution when we create a bill. If we create a payment after accessing the bill from this route, the context is transfered to account.payment.register, and then to the payment's entry lines in the `_create_payments` method. Due to the `_compute_analytic_distribution` method, the project's distribution is used on the payment's entry. We propose to filter out payment lines in this compute method. opw-6329475 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275076
Steps: 1. Create an analytic model filtered by partner. 2. Create an analytic model filtered by product. 3. Create a project with an analytic distribution. (Make sure the distributions use different plans) 4. Create an SO for a product within the project that both the models apply to. 5. Confirm the SO. 6. Notice the analytic distribution for the project account is at 200%. When an SOL is created, the analytic distribution from each model is added as a separte line The analytic accoun
Original PR description
Steps: 1. Create an analytic model filtered by partner. 2. Create an analytic model filtered by product. 3. Create a project with an analytic distribution. (Make sure the distributions use different plans) 4. Create an SO for a product within the project that both the models apply to. 5. Confirm the SO. 6. Notice the analytic distribution for the project account is at 200%. When an SOL is created, the analytic distribution from each model is added as a separte line The analytic account for the project is added to each analytic distribution line. This can easily cause the account to have >100% distribution for a given SOL. This is unintuitive and confusing behaviour. This PR changes the behaviour to only create one line for all the distributions from analytic models. This should prevent this behaviour while keeping the functionality of applying the project distribution to each line. opw-6250908 / opw-6304033 Forward-Port-Of: odoo/odoo#270151
### Purpose of this PR: * Prevent the powerbox from opening inside a static file box. * Paste content as plain text inside a static file box, since formatting is not supported there. * Prevent the cursor from leaving an empty file name box when pressing `ArrowLeft` or `ArrowRight`. * o_we_preview_favicon had a fixed height larger than its content, causing the link popover to overflow and show an unwanted scrollbar when the file input was empty as there was nothing to preview. Reduced the h
Original PR description
### Purpose of this PR: * Prevent the powerbox from opening inside a static file box. * Paste content as plain text inside a static file box, since formatting is not supported there. * Prevent the cursor from leaving an empty file name box when pressing `ArrowLeft` or `ArrowRight`. * o_we_preview_favicon had a fixed height larger than its content, causing the link popover to overflow and show an unwanted scrollbar when the file input was empty as there was nothing to preview. Reduced the height to match the actual content size. * Shorthands (such as `#` for headings or `*` for lists) should not trigger when editing inside a file box. task-6283420 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#268827
This fix is the same as this one https://github.com/odoo/odoo/pull/271577 but for the backend part of the code. After the fix, if you followed the same steps to reproduce and tried to close the session you would have an unbalanced entry for the session. Steps to reproduce: ------------------- * Create a 21% tax not included in price * Create a product with a price of 76.01 and the tax created above * Create a loyalty program with a 10% discount * Create a POS order with the product ab
Original PR description
This fix is the same as this one https://github.com/odoo/odoo/pull/271577 but for the backend part of the code. After the fix, if you followed the same steps to reproduce and tried to close the session you would have an unbalanced entry for the session. Steps to reproduce: ------------------- * Create a 21% tax not included in price * Create a product with a price of 76.01 and the tax created above * Create a loyalty program with a 10% discount * Create a POS order with the product above and apply the loyalty program * Validate the order and generate the invoice * Close the session > Observation: You need to force close the session because of unbalanced entry Why the fix: ------------ Apply the same fix for backend code. opw-6052112 Forward-Port-Of: odoo/odoo#274985
Typing in an HTML field (e.g. a contact's Internal Notes) and validating a URL-like token with Enter or Space can crash the editor with "IndexSizeError: The index is not in the allowed range", leaving the user unable to continue typing. It happens on Safari (not Chromium). The trigger is a URL-like token that the editor auto-converts into a link. The splitText calls in prepareConvertToLink, run during beforeinput, leave Safari's native selection anchored on an empty text node with an out-of-ran
Original PR description
Typing in an HTML field (e.g. a contact's Internal Notes) and validating a URL-like token with Enter or Space can crash the editor with "IndexSizeError: The index is not in the allowed range",…
Typing in an HTML field (e.g. a contact's Internal Notes) and validating
a URL-like token with Enter or Space can crash the editor with
"IndexSizeError: The index is not in the allowed range", leaving the
user unable to continue typing. It happens on Safari (not Chromium).
The trigger is a URL-like token that the editor auto-converts into a
link. The splitText calls in prepareConvertToLink, run during
beforeinput, leave Safari's native selection anchored on an empty text
node with an out-of-range offset. Anything reading the selection
afterwards then works from a broken position: on Enter, splitBlock
reads it and makeActiveSelection ends up throwing in Range.setStart;
on Space, the browser inserts the character in the wrong node and the
selection is corrupted the same way.
```
UncaughtClientError > IndexSizeError
Uncaught Javascript Error > The index is not in the allowed range.
setStart@[native code]
createEditorSelection@.../web.assets_web.min.js:12239:15
getSelectionData@.../web.assets_web.min.js:12242:145
updateActiveSelection@.../web.assets_web.min.js:12230:92
@.../web.assets_web.min.js:12218:873
handler@.../web.assets_web.min.js:14366:121
```
Steps to reproduce:
1. Use Safari (Chromium-based browsers work fine)
2. Open any record with an HTML field (e.g. Contacts -> a contact ->
Internal Notes).
3. Type a URL-like token such as KF.16D2.0204.CG (.CG is a valid TLD,
so the editor auto-links it). Do not paste it.
4. Place the caret at the end of that token and press Enter or Space.
5. IndexSizeError is raised and the editor stops accepting input.
Fix it at the source: re-anchor the selection right after the splits in
prepareConvertToLink, so every consumer sees a valid caret position.
Since moving the selection during beforeinput makes WebKit cancel the
pending text insertion, the Space case now prevents the default and
performs the conversion, the space insertion and the caret placement
itself, in two history steps so that undo still reverts the link
conversion while keeping the typed space.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#270910This update resolves an issue where selecting an item in an autocomplete field would sometimes result in the selected value being lost. The fix ensures the field's value is correctly updated after selecting an item, improving data accuracy and user experience within the Project app. This was triggered by a specific interaction within the autocomplete component.
Original PR description
In this fix we only call the `props.onChange` when the ignoreBlur flag is flag, because it's only set to true when we click on the dropdown item[1]. Steps to reproduce: - Open Project app - Go to a task. - Click on Activity button - Select the "On the Assigned" to field CTRL + a => Delete Press a letter like 'e' Remove the letter Select a item inside the dropdown => the fields is value is empty and the selected item is lost => bug task-4504910 [1]: https://github.com/odoo/odoo/blob/9bc7638506262259ac54a962617884f3deff6b9b/addons/web/static/src/core/autocomplete/autocomplete.xml#L45 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275260
Miscellaneous changes
all_sm is referenced by two branches of the outer UNION, so PostgreSQL materializes it (a CTE used more than once is an optimization fence). Every query on report_stock_quantity therefore builds the forecast for all products/warehouses first and filters afterwards, so predicates like product_id can't reach the stock_move scan. This hurts single-product lookups such as _read_group() in mrp_report_bom_structure._get_stock_availability(), called repeatedly per component while rendering a BoM rep
Original PR description
all_sm is referenced by two branches of the outer UNION, so PostgreSQL materializes it (a CTE used more than once is an optimization fence). Every query on report_stock_quantity therefore builds the…
all_sm is referenced by two branches of the outer UNION, so PostgreSQL materializes it (a CTE used more than once is an optimization fence). Every query on report_stock_quantity therefore builds the forecast for all products/warehouses first and filters afterwards, so predicates like product_id can't reach the stock_move scan.
This hurts single-product lookups such as _read_group() in mrp_report_bom_structure._get_stock_availability(), called repeatedly per component while rendering a BoM report: each call does a full stock_move scan plus two sorts that spill to disk.
Marking all_sm NOT MATERIALIZED lets the planner inline it and push the product_id/warehouse_id filter down to an index scan, removing the full scan, the GENERATE_SERIES expansion and the on-disk sorts. warehouse_cte stays materialized (small and cheap).
Query generated from the _read_group:
```SQL
SELECT MIN("report_stock_quantity"."date")
FROM "report_stock_quantity"
WHERE (
(
(
(
("report_stock_quantity"."state" = 'forecast')
AND ("report_stock_quantity"."date" >= '2026-07-08')
)
AND ("report_stock_quantity"."product_id" = 15743)
)
AND ("report_stock_quantity"."product_qty" >= 1.0)
)
AND ("report_stock_quantity"."warehouse_id" = 4)
)
AND ("report_stock_quantity"."company_id" IN (1))
```
Before:
800 ms by _read_group in _get_stock_availability().
For opening a BoM overview with hundred of components takes 17 sec.
After:
25 ms by _read_group in _get_stock_availability().
Same BoM overview drops to 3 sec to open.
cc @Aurelienvd
I can send you EXPLAIN ANALYZE, but I prefer to not putting them here to avoid data leakage.
Forward-Port-Of: odoo/odoo#275022