Saturday, August 22, 2026
11 changes · saas-19.3
Enhancements to existing features
This update enables an automated quality check for a set of web document tests. It helps keep the test code consistent and easier to maintain, reducing the risk of future issues in this area.
Original PR description
Task-5180137 Forward-Port-Of: odoo/odoo#283893 Forward-Port-Of: odoo/odoo#280233
Users can again access key sharing and export actions from document folder menus, including when using spreadsheet and knowledge embedded views. This makes it easier to share dynamic folder views while adding safeguards to avoid exposing access tokens in knowledge links.
Original PR description
Also impacted: test_documents_full It is convenient to export a dynamic view of a folder in both spreadsheet and knowledge links settings. * Care is taken to avoid leaking access folders tokens through the search panel/model's state in knowledge. * We also enable sharing folders shared via link through embedded views as it enables benefitting from the power of them vs. adding the link to the folder in the article. * As with other actions initiated on shortcuts, the "real" operation is done on the target. Sharing the target is simpler than patching a folder "child_of" to return the target children (shortcut as documents_unique_folder_id is not supported). Task-5180137 Forward-Port-Of: odoo/enterprise#128805 Forward-Port-Of: odoo/enterprise#122481
Timesheet Assistant now matches Gmail-related activities more reliably by prioritizing learned subject-based matches before falling back to email address matching. When an email address is shared by multiple contacts, it prefers the contact linked to a user with the same email, reducing incorrect project or contact suggestions.
Resolved issues and error corrections
Odoo now hides deprecated Peppol EAS identifier codes when setting up partners or registering for Peppol. This prevents users from choosing outdated identifiers that could cause registration or e-invoicing issues.
Original PR description
Peppol EAS codes 0037, 0213, 9955, and 0193 are deprecated or removed from the Peppol specification but are still present in the selection field on stable branches, allowing users to register invalid identifiers. See: [eas codes](https://docs.peppol.eu/edelivery/codelists/v9.7/Peppol%20Code%20Lists%20-%20Participant%20identifier%20schemes%20v9.7.html) Before: - deprecated EAS codes were listed alongside valid ones in the partner's available Peppol EAS options, allowing users to select an outdated identifier for new or duplicated partners, or during Peppol registration. After: - Excluded deprecated EAS codes from the available Peppol EAS selection list on partners, preventing users from selecting them for new or duplicated partners, or during Peppol registration. Removed Deprecated codes in Master: odoo/odoo#271288 Task [link](https://www.odoo.com/odoo/project.task/6299691) task-6299691 Forward-Port-Of: odoo/odoo#283698 Forward-Port-Of: odoo/odoo#271435
This update refreshes the spreadsheet component and fixes issues affecting sorting, input display, and number formatting. Users should see more consistent spreadsheet behavior, especially when working with filtered data and locale-specific decimal separators.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/cf8c61430a [REL] 19.3.17 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/cf8c61430a [REL] 19.3.17 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/2e19bb8a08 [FIX] inputs: explicitly define border-style [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/6a672478e5 [FIX] filter_menu: take number of headers into account when sorting [Task: 6471792](https://www.odoo.com/odoo/2328/tasks/6471792) https://github.com/odoo/o-spreadsheet/commit/9b2e04eda5 [IMP] claude: add gitignore for claude local settings [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/7c8752e525 [FIX] toLocaleString : new helper to use the locale's decimal separator [Task: 6232818](https://www.odoo.com/odoo/2328/tasks/6232818) https://github.com/odoo/o-spreadsheet/commit/b7f4fbfebe [FIX] toLocaleString : new helper to use the locale's decimal separator [Task: 6232818](https://www.odoo.com/odoo/2328/tasks/6232818) 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>
This update fixes issues in how Odoo prepares structured log information, including cases where ignored fields and test-related details were handled incorrectly. It also adds automated checks to help prevent these logging issues from returning, improving reliability for monitoring and troubleshooting.
Original PR description
The JsonFormatter has two bugs - the ignore list is not working as expected - in 18.0-18.4 the 'test' key is broken This commit add tests to ensure those behavior works as expected While on it, also…
The JsonFormatter has two bugs - the ignore list is not working as expected - in 18.0-18.4 the 'test' key is broken This commit add tests to ensure those behavior works as expected While on it, also adds a `additional_record_keys` parameter to allow to specifically add keys to the default list, without having to override the whole list, and add additional default keys (exc_info and test) The previous `ignored_record_keys` default value was possible to remove by calling `JSONFormatter(ignore_record_keys=[])` The purpose was to be able to easily include all keys and ignore the default ingnore list, but this makes the additional blacklisting of a few keys more tedious, and the general usage and implementation more complex `JSONFormatter(ignore_record_keys=[*JSONFormatter.DEFAULT_IGNORED_RECORD_KEYS, 'other key'])` To simplify the logic, **this is not the case anymore**, so to include all keys something like this would be needed `JSONFormatter(additional_record_keys=JSONFormatter.DEFAULT_IGNORED_RECORD_KEYS)` Or an hardcoded list. Forward-Port-Of: odoo/odoo#281449 Forward-Port-Of: odoo/odoo#279049
This fixes an issue where customer-related sales order line lookups could fail when the search criteria included list values. The change keeps timesheet and sales service workflows reliable by preserving the original search data while safely caching repeated lookups.
Original PR description
- Avoid converting list values in `_get_last_sol_of_customer_domain` to an invalid domain structure when computing the last sale order line of a customer. - Fix by using `str(domain)` as the cache key instead of the domain itself, while still passing the original `domain` to `search()`. This keeps the per-domain caching behavior intact and works for any domain, regardless of whether it contains list values. task-6425335 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281897
Odoo now correctly creates replenishment suggestions when stock is moved from one internal location to another monitored replenishment location. This helps businesses avoid missed restocking alerts when a transfer makes the source location run negative.
Original PR description
Steps to reproduce: ------------------- 1. Install `stock` module. 1. Enable the "Storage Locations" from setting. 2. Create an internal location and enable `replenish_location` on it and set…
Steps to reproduce:
-------------------
1. Install `stock` module.
1. Enable the "Storage Locations" from setting.
2. Create an internal location and enable `replenish_location` on it and set warehouse(WH) as Parent.
3. Create a storable product track by quantity with no on-hand quantity.
4. Create an internal transfer from the warehouse stock location to the new internal location.
5. Confirm the transfer.
6. Open the Replenishment report.
Issue:
------
No manual reordering rule is created for the product at the source location, although
the confirmed transfer makes its forecasted quantity negative.
If `replenish_location` is disabled on the destination, the expected reordering rule is created.
Cause:
------
When both the source and destination have `replenish_location=True`, Odoo considers both locations
inside the same replenishment area. Therefore, the internal transfer is not counted as either
incoming or outgoing, and no replenishment is created for the source location.
Code Flow:
Opening the Replenishment report calls
`stock.warehouse.orderpoint.action_open_orderpoints()`, which delegates the report preparation to `_get_orderpoint_action()`:
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/stock_orderpoint.py#L324-L326
`_get_orderpoint_action()` first obtains every location that must be monitored for replenishment through `_get_orderpoint_locations()`:
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/stock_orderpoint.py#L520
`_get_orderpoint_locations()` returns all locations whose `replenish_location` field is enabled:
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/stock_orderpoint.py#L799-L800
All these locations are passed together to
`product.product._get_domain_locations_new()`:
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/product.py#L396-L464
This method treats the provided locations and their descendants as a set. Its move domains are equivalent to:
- incoming: destination is inside the set and source is outside;
- outgoing: source is inside the set and destination is outside.
Consider the following sibling locations:
WH
├── Stock
└── Replenish Location
and the transfer:
Stock -- 3 units --> Replenish Location
When `replenish_location` is disabled on the destination, the considered location set contains only `Stock`:
considered location set = {Stock}
The source is inside the set and the destination is outside:
source inside = True
destination outside = True
The transfer therefore matches the outgoing domain:
source inside AND destination outside
True AND True
= True
It is included in `moves_out`, and the preliminary forecast for `Stock` becomes:
0 on hand + 0 incoming - 3 outgoing = -3
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/stock_orderpoint.py#L552
The negative quantity is detected and a manual orderpoint is created.
When `replenish_location` is enabled on the destination, both sibling locations belong to the considered location set:
considered location set = {Stock, Replenish Location}
Both ends of the transfer are now inside:
source inside = True
destination inside = True
destination outside = False
source outside = False
The transfer matches neither aggregate domain:
incoming:
destination inside AND source outside
True AND False
= False
outgoing:
source inside AND destination outside
True AND False
= False
The transfer is considered internal to the considered location set and is therefore absent from both `moves_in` and `moves_out`:
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/stock_orderpoint.py#L530-L540
This aggregate treatment conflicts with the next step, where `_get_orderpoint_action()` computes the quantity separately for each replenishment location:
https://github.com/odoo/odoo/blob/12a66c6931d81e1cce18f676836aca0b32090d16/addons/stock/models/stock_orderpoint.py#L549-L552
When `Stock` is evaluated separately, the internal transfer has already been discarded. Its calculated outgoing quantity is consequently zero:
0 on hand + 0 incoming - 0 outgoing = 0
As the quantity is not negative, the product is not scheduled for the final `virtual_available` computation and no manual orderpoint is created.
Fix:
----
Also retrieve moves whose source and destination are both inside the aggregate replenishment-location set.
Include these internal moves in both the incoming and outgoing grouped queries. The existing per-location path filtering then assigns each side correctly:
- the source replenishment location counts the move as outgoing;
- the destination replenishment location counts it as incoming.
For the reported transfer, this produces:
Stock:
0 on hand + 0 incoming - 3 outgoing = -3
Replenish Location:
0 on hand + 3 incoming - 0 outgoing = 3
This is correct because the report creates replenishment propositions per location, even though the initial move query is performed for all replenishment locations together.
---
opw-6462608
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#282168This fix improves how Odoo loads related Sign template data when opening templates with restricted user access. It reduces unnecessary repeated database work, making the template screen more efficient and helping avoid slowdowns as templates contain more signing fields.
Original PR description
Steps to reproduce: - with a user with "Sign / User: Own Templates" access rights - go to Sign / Templates - click on a template to open it => reading `sign.item.role.item_ids.template_id` triggers the computation of the related field `sign.item.template_id`, whose inverse `sign.template.sign_item_ids` carries a domain. When applying the domain, the sign.item's fields need to be fetched but they are fetched with one query per sign.item instead of a single batched one. task-6478942 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283864 Forward-Port-Of: odoo/odoo#282994
This fixes a problem where Android users could not download images or files opened in Odoo's mobile file viewer. Downloads are now passed directly to the mobile app in a compatible way, avoiding the previous error and improving the mobile user experience.
Original PR description
Steps to reproduce: - send an image in a Discuss channel - click the image to open the file viewer - click the download button => Android shows "The Odoo Mobile Apps only supports file downloads…
Steps to reproduce: - send an image in a Discuss channel - click the image to open the file viewer - click the download button => Android shows "The Odoo Mobile Apps only supports file downloads using the HTTP protocol." downloadFile()'s GET-by-URL case fetches the URL via XHR, then saves the Blob response by clicking a hidden <a download> anchor on a blob: URL. Android's DownloadManager only accepts http(s) URLs, so it rejects that blob: URL instead of downloading anything. Patch downloadFile._download to hand the URL directly to a new mobile.methods.saveFile bridge method when available, the same way download._download already delegates to mobile.methods.downloadFile. Blob/string content downloads aren't handled here — the only such call site (spreadsheet JSON export) is debug-mode only, so this is left as a console.warn for now. Related to odoo/odoo@e83fd8c08c879f5e262d39f24edcb3f81238ea82 Code made by Claude Changes supervised by HUVW Forward-Port-Of: odoo/enterprise#128471 Forward-Port-Of: odoo/enterprise#127693
This fix prevents Colombian contacts from being automatically marked as companies when no VAT number has been entered. It helps users create individual child contacts correctly, avoiding incorrect company classification during contact setup.
Original PR description
Before this change: When creating a child contact under a Colombian company from another company context, the identification type defaults to NIT. The system evaluated the child contact as a company immediately, regardless of whether a VAT number was entered, preventing proper individual contact creation. To reproduce: 1. Create a new contact and set the country to Colombia. 2. Set the identification type to NIT. 3. Leave the VAT field empty. 4. Observe that the "Is a Company" checkbox becomes checked automatically. After this change: The company computation logic explicitly verifies that a valid VAT number is present before evaluating NIT contacts as companies, allowing individual child contacts to retain their correct entity status. opw-6468848 Forward-Port-Of: odoo/enterprise#128656