Daily updates from Odoo
Thursday, August 13, 2026
255 changes
22 changes
Enhancements to existing features
This commit adds a "Reload Data" button to the traceback dialog for PWA applications. When clicked, the user is asked to confirm the action. Once confirmed, all locally stored browser data is cleared, allowing the POS to recover from errors caused by corrupted or outdated local data. task-6388234 Forward-Port-Of: odoo/odoo#282150 Forward-Port-Of: odoo/odoo#276559
Original PR description
This commit adds a "Reload Data" button to the traceback dialog for PWA applications. When clicked, the user is asked to confirm the action. Once confirmed, all locally stored browser data is cleared, allowing the POS to recover from errors caused by corrupted or outdated local data. task-6388234 Forward-Port-Of: odoo/odoo#282150 Forward-Port-Of: odoo/odoo#276559
…h lookup _search_phone_mobile_search previously searched only raw number fields returned by _phone_get_number_fields (e.g. mobile, phone). E164-normalized phone_sanitized was ignored, so searching by a sanitized value could miss records that had no match on the raw fields. This commit: - Extracts _phone_get_searchable_fields() so that both init() and _search_phone_mobile_search build the same searchable field list, appending phone_sanitized alongside mobile/phone when available. - Add
Original PR description
…h lookup _search_phone_mobile_search previously searched only raw number fields returned by _phone_get_number_fields (e.g. mobile, phone). E164-normalized phone_sanitized was ignored, so searching by a sanitized value could miss records that had no match on the raw fields. This commit: - Extracts _phone_get_searchable_fields() so that both init() and _search_phone_mobile_search build the same searchable field list, appending phone_sanitized alongside mobile/phone when available. - Adds expression indexes for phone_sanitized in init() so the REGEXP_REPLACE-based queries hit indexes instead of scanning tables. - Guards init() with self._auto to prevent index creation on AbstractModel subclasses that lack a physical table. Task-6395518 ENTPR: odoo/enterprise#124797 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281598 Forward-Port-Of: odoo/odoo#278018
Resolved issues and error corrections
When LNA support was added for the ETA integration in odoo/odoo#264766, a small oversight was made when using trying to use localhost as the IoT (common for virtual IoT setups). Because the `targetAddressSpace` was always set to `"local"`, requests to localhost would fail with this error: ``` Request had a target IP address space of `local` yet the resource is in address space `loopback`. ``` This commit fixes the issue by setting the value to `loopback` if localhost is being used. o
Original PR description
When LNA support was added for the ETA integration in odoo/odoo#264766, a small oversight was made when using trying to use localhost as the IoT (common for virtual IoT setups). Because the `targetAddressSpace` was always set to `"local"`, requests to localhost would fail with this error: ``` Request had a target IP address space of `local` yet the resource is in address space `loopback`. ``` This commit fixes the issue by setting the value to `loopback` if localhost is being used. opw-6467156 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce the bug: - Install a localization that overrides invoice_policy defaults for storable products without an explicit company_id (e.g. l10n_ke_edi_oscu_stock, which forces 'delivery' in that case) - Run TestSaleMRPAngloSaxonValuation.test_sale_mrp_kit_bom_cogs (sale_mrp) or TestAngloSaxonValuation.test_anglo_saxon_cogs_partial_down_payment_credit_note (sale_stock) Problem: These tests create their products without setting invoice_policy explicitly, relying on the field's im
Original PR description
Steps to reproduce the bug: - Install a localization that overrides invoice_policy defaults for storable products without an explicit company_id (e.g. l10n_ke_edi_oscu_stock, which forces 'delivery'…
Steps to reproduce the bug: - Install a localization that overrides invoice_policy defaults for storable products without an explicit company_id (e.g. l10n_ke_edi_oscu_stock, which forces 'delivery' in that case) - Run TestSaleMRPAngloSaxonValuation.test_sale_mrp_kit_bom_cogs (sale_mrp) or TestAngloSaxonValuation.test_anglo_saxon_cogs_partial_down_payment_credit_note (sale_stock) Problem: These tests create their products without setting invoice_policy explicitly, relying on the field's implicit default. l10n_ke_edi_oscu_stock's _compute_invoice_policy (https://github.com/odoo/enterprise/blob/4e459417dac809caafea34aa2e487fc3c1f0ce1a/l10n_ke_edi_oscu_stock/models/product.py#L16-L21) forces invoice_policy to 'delivery' for any storable product whose company_id is not set, which is the case for products created in these test fixtures. Once invoice_policy becomes 'delivery', invoiced quantities are driven by qty_delivered instead of the ordered quantity, which the affected tests never account for (some deliver an arbitrary quantity instead of the exact BoM demand, others never validate a delivery at all), causing wrong COGS amounts or wrongly invoiced quantities as soon as such a localization is installed alongside these modules. Solution: Pin invoice_policy to 'order' explicitly wherever these test fixtures create their products, so the test outcome no longer depends on which other modules happen to be installed. runbot-243633 Forward-Port-Of: odoo/odoo#279277 Forward-Port-Of: odoo/odoo#278346
`test_orderpoint_activity_portal_context_leak` assumes that running the orderpoint will trigger a procurement exception. However, depending on which modules are installed (e.g., when `purchase_stock` is absent), standard stock rules for the test warehouse destination location can succeed in generating stock moves rather than raising an error. Deactivate all matching destination stock rules on the test warehouse prior to running procurement so the orderpoint is guaranteed to fail. runbot-94
Original PR description
`test_orderpoint_activity_portal_context_leak` assumes that running the orderpoint will trigger a procurement exception. However, depending on which modules are installed (e.g., when `purchase_stock` is absent), standard stock rules for the test warehouse destination location can succeed in generating stock moves rather than raising an error. Deactivate all matching destination stock rules on the test warehouse prior to running procurement so the orderpoint is guaranteed to fail. runbot-941316 Forward-Port-Of: odoo/odoo#279781
Before this commit, marking several manufacturing orders as done at once crashed or could post labour costs on the wrong account, because the labour posting loop read the product and the company from the whole recordset instead of the manufacturing order being processed. Steps to reproduce: - activate a second company, e.g. My Company (Chicago) - create a manufacturing order in each company and confirm them - in the Manufacturing Orders list view, select both orders and mark them as done
Original PR description
Before this commit, marking several manufacturing orders as done at once crashed or could post labour costs on the wrong account, because the labour posting loop read the product and the company from…
Before this commit, marking several manufacturing orders as done at once crashed or could post labour costs on the wrong account, because the labour posting loop read the product and the company from the whole recordset instead of the manufacturing order being processed. Steps to reproduce: - activate a second company, e.g. My Company (Chicago) - create a manufacturing order in each company and confirm them - in the Manufacturing Orders list view, select both orders and mark them as done A "ValueError: Expected singleton: res.company(...)" traceback is raised and none of the orders can be closed, even though each one can be marked as done individually. With same-company orders of different products, the production location resolved from the union of products, so the labour entry could be posted against another product's WIP account. Use the manufacturing order of the current loop iteration to resolve the production location, as the rest of the loop already does. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276586
In saas-19.1, `purchase_cdnur_regular` was renamed to `purchase_cdnur_overseas`. However, commit https://github.com/odoo/odoo/commit/c4b0911e061ff0619bd3d6a411701fc898dad258 still used the old section name. This commit updates `purchase_cdnur_regular` to `purchase_cdnur_overseas`. Forward-Port-Of: odoo/odoo#281895
Original PR description
In saas-19.1, `purchase_cdnur_regular` was renamed to `purchase_cdnur_overseas`. However, commit https://github.com/odoo/odoo/commit/c4b0911e061ff0619bd3d6a411701fc898dad258 still used the old section name. This commit updates `purchase_cdnur_regular` to `purchase_cdnur_overseas`. Forward-Port-Of: odoo/odoo#281895
A POS session could not be closed if there were draft orders planned for later the same day. The backend check was only filtering out orders with a date strictly in the future, ignoring the time part for same-day orders. task-id: 6000698 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272241 Forward-Port-Of: odoo/odoo#251935
Original PR description
A POS session could not be closed if there were draft orders planned for later the same day. The backend check was only filtering out orders with a date strictly in the future, ignoring the time part for same-day orders. task-id: 6000698 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#272241 Forward-Port-Of: odoo/odoo#251935
Issue: ```python In [14]: receiver._get_peppol_proxy_endpoint('/2/get_services') Out[14]: '/api/peppol//2/get_services' In [15]: receiver._get_peppol_proxy_endpoint('2/get_services') Out[15]: '/api/peppol/2/get_services' ``` this raises: ```bash [ERROR] odoo.addons.account_peppol_response.models.account_edi_proxy_user Auto registration of peppol services for module: account_peppol_response failed on the user: ***, with exception: Invalid signature for request. This might be due to
Original PR description
Issue:
```python
In [14]: receiver._get_peppol_proxy_endpoint('/2/get_services')
Out[14]: '/api/peppol//2/get_services'
In [15]: receiver._get_peppol_proxy_endpoint('2/get_services')
Out[15]: '/api/peppol/2/get_services'
```
this raises:
```bash
[ERROR] odoo.addons.account_peppol_response.models.account_edi_proxy_user
Auto registration of peppol services for module: account_peppol_response failed on the user: ***, with exception: Invalid signature for request. This might be due to another connection to odoo Access Point server. It can occur if you have duplicated your database
```
OPW-6431279
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#281783Steps to reproduce: 1. Refund an order using a Glory Cash payment method 2. The machine refunds the cash correctly **Expected behaviour:** Odoo validates the refund **Actual behaviour:** Odoo sets the line amount to zero, refund incomplete The fix is to correctly remove the money dispensed from the payment total, resulting in a negative payment amount rather than zero. In addition, we remove similar logic for Cashdro machines that was also broken as the code path was never executed.
Original PR description
Steps to reproduce: 1. Refund an order using a Glory Cash payment method 2. The machine refunds the cash correctly **Expected behaviour:** Odoo validates the refund **Actual behaviour:** Odoo sets the line amount to zero, refund incomplete The fix is to correctly remove the money dispensed from the payment total, resulting in a negative payment amount rather than zero. In addition, we remove similar logic for Cashdro machines that was also broken as the code path was never executed. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281938
Before this commit, the html composer variant of "Opening thread with needaction messages should mark all messages of thread as read" failed at random on runbot: 11. [verifySteps] expected the following steps > Expected: [ "mark-all-messages-as-read", ] > Received: [] This happens because the channel of that variant holds no message, so the test goes to the inbox before the channel is loaded and it keeps scrollUnread set. Opening the channel again then loads its m
Original PR description
Before this commit, the html composer variant of "Opening thread with needaction messages should mark all messages of thread as read" failed at random on runbot: 11. [verifySteps] expected the…
Before this commit, the html composer variant of "Opening thread with needaction messages should mark all messages of thread as read" failed at random on runbot:
11. [verifySteps] expected the following steps
> Expected: [
"mark-all-messages-as-read",
]
> Received: []
This happens because the channel of that variant holds no message, so the test goes to the inbox before the channel is loaded and it keeps scrollUnread set. Opening the channel again then loads its messages around the 0 separator, and that fetch calls set_message_done, which marks the needaction message as read and leaves nothing for mark_all_as_read to mark.
This commit fixes the issue by giving the channel two messages, pointing the separator at the first one and waiting for their display: loading messages around the separator is skipped when that message is already loaded, so opening the channel again fetches nothing, which the closing assertion checks as well.
The text composer variant gets the same fixture, so that both variants read as master does, from commit 8cd330e3e2d5b1d857d3effb80aa8176d60b38f9. It does not fail: it already waits for a message of its own, and a fetch around its non-zero separator leaves the messages alone in the mock.
https://runbot.odoo.com/odoo/error/945744
Forward-Port-Of: odoo/odoo#282038
Forward-Port-Of: odoo/odoo#281714Reduce idletimeout task id: 6374370 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275081
Original PR description
Reduce idletimeout task id: 6374370 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275081
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new c
Original PR description
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']"…
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new call carries the id of the one just left. Leaving broadcasts "discuss.channel.rtc.session/ended" for that id, and under load it lands after the join: the client reads it as its own session being closed and ends the call it has just joined. This commit numbers the records of a model with a counter, started above the ids its definition gives, as a database sequence does, so a notification about a deleted record can no longer name a live one. https://runbot.odoo.com/odoo/error/945965 Forward-Port-Of: odoo/odoo#282244
**Steps to Reproduce:** 1. Configure a main product and its optional product as service products, both with a non-default Unit of Measure (e.g. m²). 2. Add the main product to a quotation. **Issue:** - The main product line's UoM is set to the generic "Units" default instead of the product's own m² UoM. Only reproduces when the product has optional products (which routes it through the product configurator dialog) **Why this happens:** - `_compute_product_uom_id` in `sale.order.line` w
Original PR description
**Steps to Reproduce:** 1. Configure a main product and its optional product as service products, both with a non-default Unit of Measure (e.g. m²). 2. Add the main product to a quotation. **Issue:**…
**Steps to Reproduce:** 1. Configure a main product and its optional product as service products, both with a non-default Unit of Measure (e.g. m²). 2. Add the main product to a quotation. **Issue:** - The main product line's UoM is set to the generic "Units" default instead of the product's own m² UoM. Only reproduces when the product has optional products (which routes it through the product configurator dialog) **Why this happens:** - `_compute_product_uom_id` in `sale.order.line` was updated to return `Units` as UoM in the commit d069ce59e28fda2bc25fb89fd01ee7b988a30fa0 which now defaults a line's `product_uom_id` to the generic reference unit as soon as the line exists, before any product is selected - When a product with optional products is picked, `_openProductConfigurator` reads that placeholder value and forwards it to `/sale/product_configurator/get_values` as `product_uom_id`. - `_get_product_information` finds the `product_uom_id` already set in `uom = (product_uom_id and browse(product_uom_id)) or product_template._get_main_uom()`, so the placeholder "Units" overrides the product's own UoM. **Fix:** - Only forward `productUOMId` to the configurator when actually editing an existing line (`edit=True`), where `product_uom_id` reflects a product-derived value. On a fresh add, no product has been chosen yet, so the line's current `product_uom_id` is just a placeholder default and should not be sent opw-6439473
[FIX] mail: attach dropped files to the message in edition Root cause: When editing a message in the chatter, the composer shown inside the message does not get the dropzoneRef prop, so it does not create its own dropzone: https://github.com/odoo/odoo/blob/5ca10578a2fd1b40cd371ed5ad20c1654dfe54d3/addons/mail/static/src/core/common/composer.js#L197-L209 The chatter creates a dropzone covering the whole chatter that saves dropped files as attachments of the record: https://github.com/odoo/odoo/b
Original PR description
[FIX] mail: attach dropped files to the message in edition Root cause: When editing a message in the chatter, the composer shown inside the message does not get the dropzoneRef prop, so it does not…
[FIX] mail: attach dropped files to the message in edition Root cause: When editing a message in the chatter, the composer shown inside the message does not get the dropzoneRef prop, so it does not create its own dropzone: https://github.com/odoo/odoo/blob/5ca10578a2fd1b40cd371ed5ad20c1654dfe54d3/addons/mail/static/src/core/common/composer.js#L197-L209 The chatter creates a dropzone covering the whole chatter that saves dropped files as attachments of the record: https://github.com/odoo/odoo/blob/5ca10578a2fd1b40cd371ed5ad20c1654dfe54d3/addons/mail/static/src/chatter/web/chatter_patch.js#L106-L138 Since the composer of the message in edition has no dropzone, a file dropped on it is caught by the chatter dropzone and ends up attached to the record instead of the message. Fix: Pass the message body as dropzoneRef to the composer in message.xml. The composer then creates its own dropzone over the message, the same way the chatter composer gets one from chatter.xml, and the dropped file is added to the message in edition. The chatter dropzone and the thread composer dropzone cover that same area and would show at the same time, so both are turned off while a message is in edition. The thread already knows which message that is through messageInEdition, so neither of them has anything to keep track of. Steps to reproduce: 1. Open the Contacts app and open any contact 2. Click Log note, type some text and click Log 3. Hover the note and click the pencil icon to edit it 4. Drag a file from the file explorer and drop it on the note => the file is added to the attachments of the contact instead of the note Ticket [link](https://www.odoo.com/odoo/project.task/6385377) opw-6385377 Forward-Port-Of: odoo/odoo#281580 Forward-Port-Of: odoo/odoo#278373
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#281710 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#281710 Forward-Port-Of: odoo/odoo#281276
Steps: - Install `account_peppol` module. - Set `Peppol` compatible country and related details - Go to my/account page. Issue: - Peppol related details always displayed on `my/account` page even though user select different invoice sending method like: `By Email`. Casue: - selector to manage visibility of Peppol related details in `my/account` is wrong and because of that those fields always display. Probably because https://github.com/odoo/odoo/pull/195764 and backport of this https
Original PR description
Steps: - Install `account_peppol` module. - Set `Peppol` compatible country and related details - Go to my/account page. Issue: - Peppol related details always displayed on `my/account` page even…
Steps: - Install `account_peppol` module. - Set `Peppol` compatible country and related details - Go to my/account page. Issue: - Peppol related details always displayed on `my/account` page even though user select different invoice sending method like: `By Email`. Casue: - selector to manage visibility of Peppol related details in `my/account` is wrong and because of that those fields always display. Probably because https://github.com/odoo/odoo/pull/195764 and backport of this https://github.com/odoo/odoo/pull/198327 merged at same time. Fix: - Update selector to fix this ### [FIX] account_peppol: fix error when setting wrong endpoint Steps: - Install `account_peppol` module. - Set `Peppol` compatible country and related details. - Go to my/account page. - Set some wrong Peppol value for `Peppol e-Address (EAS)` or `Peppol Endpoint` or `Electronic format`. Issue: - Not able to save those details without any error message on address page and getting error on console `Cannot read properties of undefined (reading 'classList')`. Casue: - In this PR https://github.com/odoo/odoo/pull/190312 when adapting portal page we set not existing fields in `invalid_fields` details and because of that it can't find related fields on address page and don't allow to save details without raising proper error message. Fix: - Updated `invalid_fields` values to properly target them ### [FIX] account_peppol: fix display issue for peppol related fields in address Forward-Port-Of: odoo/odoo#281718
Steps to reproduce the bug: - Install repair and accounting - Create a user and grant them only Inventory / User access - Log in as that user and open any Repair Order Problem: Opening the repair order raised: "Failed to read field repair.order.invoice_ids You are not allowed to access 'Journal Entry' (account.move) records." `invoice_count` and `can_create_sale_or_invoice` are computed fields that read `invoice_ids`, a One2many to `account.move` Reading a One2many always queries the
Original PR description
Steps to reproduce the bug: - Install repair and accounting - Create a user and grant them only Inventory / User access - Log in as that user and open any Repair Order Problem: Opening the repair…
Steps to reproduce the bug: - Install repair and accounting - Create a user and grant them only Inventory / User access - Log in as that user and open any Repair Order Problem: Opening the repair order raised: "Failed to read field repair.order.invoice_ids You are not allowed to access 'Journal Entry' (account.move) records." `invoice_count` and `can_create_sale_or_invoice` are computed fields that read `invoice_ids`, a One2many to `account.move` Reading a One2many always queries the comodel, so this triggers an ACL check on `account.move` for the current user, even though the repair order has no invoice and the field is only used to display a count/boolean. A user with `stock.group_stock_user` but no accounting/sales rights has no access to `account.move`, so simply opening the form crashes. Solution: Read `invoice_ids` with `sudo()` inside `_compute_invoice_count` and `_compute_can_create_sale_or_invoice`, since only a derived count/boolean is exposed to the user, not the invoice records themselves. opw-6447829 Forward-Port-Of: odoo/odoo#281692
- Some clients does not know that they can skip the feedback screen timeout by clicking on the screen. So we decrease the timeout to 1.5 seconds to avoid that they wait too much time. - This timeout was already reduced in version `saas-19.1` to 1 second (see PR: github.com/odoo/odoo/issues/235316) - We now set it to 1.5 seconds (because 1 second is not enough for the paid animation to finish on the feedback screen). task-id: 6425204 --- I confirm I have signed the CLA and read the PR gu
Original PR description
- Some clients does not know that they can skip the feedback screen timeout by clicking on the screen. So we decrease the timeout to 1.5 seconds to avoid that they wait too much time. - This timeout was already reduced in version `saas-19.1` to 1 second (see PR: github.com/odoo/odoo/issues/235316) - We now set it to 1.5 seconds (because 1 second is not enough for the paid animation to finish on the feedback screen). task-id: 6425204 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280728 Forward-Port-Of: odoo/odoo#278841
Making sure that during the race condition between the save triggered by closing the widget and the one triggered by the ’ NEED_LOCAL_CHANGES’ bus doesn’t lead to the re-opening of the widget itself and subsequently the list mass edit pop up. Forward-Port-Of: odoo/odoo#281013
Original PR description
Making sure that during the race condition between the save triggered by closing the widget and the one triggered by the ’ NEED_LOCAL_CHANGES’ bus doesn’t lead to the re-opening of the widget itself and subsequently the list mass edit pop up. Forward-Port-Of: odoo/odoo#281013
…tour Scope kanban selection triggers to the active modal to avoid a race with Owl's async modal state, and make Many2XAutocomplete's onClose null-safe against a crash the race was masking. runbot-error-id~939488 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
Original PR description
…tour Scope kanban selection triggers to the active modal to avoid a race with Owl's async modal state, and make Many2XAutocomplete's onClose null-safe against a crash the race was masking. runbot-error-id~939488 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
Previously, refreshing the PoS caused categories with sequence = 0 to fall back to ID-based sorting from IndexedDB. Sequence-based ordering was already fixed in this [pr](https://github.com/odoo/odoo/pull/207172), but the fallback for sequence 0 still sorted by ID. This change ensures categories with sequence = 0 follow the expected ordering when refreshing. Task-6185359 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merge
Original PR description
Previously, refreshing the PoS caused categories with sequence = 0 to fall back to ID-based sorting from IndexedDB. Sequence-based ordering was already fixed in this [pr](https://github.com/odoo/odoo/pull/207172), but the fallback for sequence 0 still sorted by ID. This change ensures categories with sequence = 0 follow the expected ordering when refreshing. Task-6185359 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#281248 Forward-Port-Of: odoo/odoo#273954
3 changes
Resolved issues and error corrections
Before this commit, the html composer variant of "Opening thread with needaction messages should mark all messages of thread as read" failed at random on runbot: 11. [verifySteps] expected the following steps > Expected: [ "mark-all-messages-as-read", ] > Received: [] This happens because the channel of that variant holds no message, so the test goes to the inbox before the channel is loaded and it keeps scrollUnread set. Opening the channel again then loads its m
Original PR description
Before this commit, the html composer variant of "Opening thread with needaction messages should mark all messages of thread as read" failed at random on runbot: 11. [verifySteps] expected the…
Before this commit, the html composer variant of "Opening thread with needaction messages should mark all messages of thread as read" failed at random on runbot:
11. [verifySteps] expected the following steps
> Expected: [
"mark-all-messages-as-read",
]
> Received: []
This happens because the channel of that variant holds no message, so the test goes to the inbox before the channel is loaded and it keeps scrollUnread set. Opening the channel again then loads its messages around the 0 separator, and that fetch calls set_message_done, which marks the needaction message as read and leaves nothing for mark_all_as_read to mark.
This commit fixes the issue by giving the channel two messages, pointing the separator at the first one and waiting for their display: loading messages around the separator is skipped when that message is already loaded, so opening the channel again fetches nothing, which the closing assertion checks as well.
The text composer variant gets the same fixture, so that both variants read as master does, from commit 8cd330e3e2d5b1d857d3effb80aa8176d60b38f9. It does not fail: it already waits for a message of its own, and a fetch around its non-zero separator leaves the messages alone in the mock.
https://runbot.odoo.com/odoo/error/945744
Forward-Port-Of: odoo/odoo#282038
Forward-Port-Of: odoo/odoo#281714Steps to reproduce: - 1. In the website editor, open the portal "My Account" page and, in the Customize panel, disable the "Timesheets" option. 2. As a portal user, open My Account > Tasks for a project whose tasks have allocated time and logged timesheets. 3. Look at the task list, then open one of those tasks. Issue: - The task list still shows the per-group "Total: spent / allocated", and the task detail page still shows "Allocated Time", even though timesheets are hidden in the port
Original PR description
Steps to reproduce: - 1. In the website editor, open the portal "My Account" page and, in the Customize panel, disable the "Timesheets" option. 2. As a portal user, open My Account > Tasks for a project whose tasks have allocated time and logged timesheets. 3. Look at the task list, then open one of those tasks. Issue: - The task list still shows the per-group "Total: spent / allocated", and the task detail page still shows "Allocated Time", even though timesheets are hidden in the portal. Fix: - - Add `_show_portal_timesheets()` to the condition of the list "Total" column. - Gate the `portal_my_task_allocated_hours` block on `_show_portal_timesheets()` in the task detail page. task-6140807 Forward-Port-Of: odoo/odoo#272043
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new c
Original PR description
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']"…
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new call carries the id of the one just left. Leaving broadcasts "discuss.channel.rtc.session/ended" for that id, and under load it lands after the join: the client reads it as its own session being closed and ends the call it has just joined. This commit numbers the records of a model with a counter, started above the ids its definition gives, as a database sequence does, so a notification about a deleted record can no longer name a live one. https://runbot.odoo.com/odoo/error/945965 Forward-Port-Of: odoo/odoo#282244
1 change
Resolved issues and error corrections
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#281729 Forward-Port-Of: odoo/odoo#280397
5 changes
Enhancements to existing features
### [FIX] website: fix GMaps deprecation console warning for s_google_map Google deprecated the synchronous script loader for initializing the Maps JS API. This caused browser console warnings. The fix adopts Google's official `importLibrary()` bootstrap pattern, which loads map libraries (places, maps, marker) lazily on demand rather than all at once.The version is updated to `v=weekly`, which Google recommends as it receives updates weekly versus quarterly for version numbers(`v=num
Original PR description
### [FIX] website: fix GMaps deprecation console warning for s_google_map Google deprecated the synchronous script loader for initializing the Maps JS API. This caused browser console warnings. The…
### [FIX] website: fix GMaps deprecation console warning for s_google_map Google deprecated the synchronous script loader for initializing the Maps JS API. This caused browser console warnings. The fix adopts Google's official `importLibrary()` bootstrap pattern, which loads map libraries (places, maps, marker) lazily on demand rather than all at once.The version is updated to `v=weekly`, which Google recommends as it receives updates weekly versus quarterly for version numbers(`v=number`). Steps to reproduce: 1. Add the `s_google_map` snippet(not the`s_map`, enable debug mode) 2. Open the browser console and observe the deprecation warning ### [IMP] website: warn user to reload after GMaps config changes Switching from the legacy Google Maps APIs to the new APIs requires enabling additional services in Google Cloud. Existing maps using the legacy API continue to work, but when an admin edits a map without a proper configuration, the `GoogleMapAPIKeyDialog` dialog opens. Google Maps configuration changes (API key update or enabling services) do not take effect during the current editor session because the Maps JavaScript API is loaded at page initialization. Before this commit, such misconfigurations (disabled services or invalid API keys) only triggered a dialog showing a generic Google Maps error. After this commit, a notification informs the user that the page must be reloaded for configuration changes to take effect. The setup instructions are also updated to reference the "Places API (NEW)" service. ### [IMP] website: replace deprecated Places API calls in GPS picker The GPS picker relied on `PlacesService.nearbySearch` and `getDetails`, which are part of the deprecated Places API. The new places API replaces these with `Place.searchNearby` and `fetchFields`. Error handling is consolidated into a single try/catch since the new Places API throws on failure rather than returning a status code, removing the need for `PlacesServiceStatus` checks. ### [IMP] website, *: replace deprecated Google Autocomplete *: website_form_project google.maps.places.Autocomplete is deprecated in the new Places API. The replacement (`AutocompleteSuggestion.fetchAutocompleteSuggestions`) does not fire DOM events, making it incompatible with the old event-listener pattern used in GPSPicker. A new Owl component (`PlacesAutoComplete`) is introduced to wrap the new API, built on top of the existing `AutoCompleteWithPages`. References: https://developers.google.com/maps/documentation/javascript/load-maps-js-api https://developers.google.com/maps/documentation/javascript/advanced-markers/migration https://developers.google.com/maps/documentation/javascript/legacy/places-migration-overview task-[4441041](https://www.odoo.com/odoo/project/974/tasks/4441041) Forward-Port-Of: odoo/odoo#281029 Forward-Port-Of: odoo/odoo#242765
Resolved issues and error corrections
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#281829 Forward-Port-Of: odoo/odoo#276648
Steps to Reproduce: ------------------------ - Install the Point of Sale module & create an order with a customer (partner). - Scan the QR code on the receipt to retrieve the invoice. Issue: ------- When clicking `Get My Invoice`, the invoice was not being generated. Cause: --------- - The correct partner was not being set. POS customer scanning the QR code is not logged into Odoo, so no partner is associated with the request. We were incorrectly using the logged-in user’s partner,
Original PR description
Steps to Reproduce: ------------------------ - Install the Point of Sale module & create an order with a customer (partner). - Scan the QR code on the receipt to retrieve the invoice. Issue: -------…
Steps to Reproduce: ------------------------ - Install the Point of Sale module & create an order with a customer (partner). - Scan the QR code on the receipt to retrieve the invoice. Issue: ------- When clicking `Get My Invoice`, the invoice was not being generated. Cause: --------- - The correct partner was not being set. POS customer scanning the QR code is not logged into Odoo, so no partner is associated with the request. We were incorrectly using the logged-in user’s partner, which resulted in an empty partner and therefore an empty VAT value. - The customer's country was also incorrect because the partner’s country was fetched but then overwritten by the order’s country due to improper value assignment. Fix: ---- - In most POS scenarios, the customer associated with the order is the one requesting the invoice. Therefore, if no logged-in user is found, we now correctly use the order’s partner to retrieve VAT and other required information. - The partner's country was corrected by ensuring the values are assigned in the proper order and no longer overridden incorrectly. task: 5406951 Forward-Port-Of: odoo/odoo#239811
Steps to reproduce: 1. Install CRM 2. Activate the Arabic language with English 3. Create a lead with a new email, a new company name, and the Arabic language 4. Save and try to send a message from the chatter Issue: - After the message is sent, the language of Lead is changed to EN from Arabic - Contacts created with the English language Cause: - When we send a message from the chatter of a Lead that has an email_from, a partner_name, and the Arabic language, this forces the creati
Original PR description
Steps to reproduce: 1. Install CRM 2. Activate the Arabic language with English 3. Create a lead with a new email, a new company name, and the Arabic language 4. Save and try to send a message from…
Steps to reproduce: 1. Install CRM 2. Activate the Arabic language with English 3. Create a lead with a new email, a new company name, and the Arabic language 4. Save and try to send a message from the chatter Issue: - After the message is sent, the language of Lead is changed to EN from Arabic - Contacts created with the English language Cause: - When we send a message from the chatter of a Lead that has an email_from, a partner_name, and the Arabic language, this forces the creation of a new contact First child contact is created with arabic language, but `parent_name` is present in the creation dictionary, so this triggers `_create_parent_from_name` that builds a dictionary to create the parent company, but it does not pass the language. As a result, the parent company is created with the English language. then `_create_parent_from_name` links the child to this new parent and this linking triggers the `_compute_lang` on the child and overwrites the child's language with the parent's language At last, Lead's own computed field `_compute_lang_id` triggers and sets the language to English Solution: - pass the language in the dictionary to create the parent company opw-6449407
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new c
Original PR description
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']"…
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new call carries the id of the one just left. Leaving broadcasts "discuss.channel.rtc.session/ended" for that id, and under load it lands after the join: the client reads it as its own session being closed and ends the call it has just joined. This commit numbers the records of a model with a counter, started above the ids its definition gives, as a database sequence does, so a notification about a deleted record can no longer name a live one. https://runbot.odoo.com/odoo/error/945965 Forward-Port-Of: odoo/odoo#282244
9 changes
Resolved issues and error corrections
In the sampel dashboard the figures are half transparent to indicate that they are just sample data, but they are still interactive. This commit disable all the pointer events on them. Task: [6467022](https://www.odoo.com/web#id=6467022&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 rea
Original PR description
In the sampel dashboard the figures are half transparent to indicate that they are just sample data, but they are still interactive. This commit disable all the pointer events on them. Task: [6467022](https://www.odoo.com/web#id=6467022&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
Before this commit, deleting a record leaves its id in the relations of a record deleted before it in the same update, and reading one of those relations hands out an entry for a record that is gone. This happens because an update takes a deleted record out of the relations that hold it, but forgets it as soon as it is deleted. However, deleting a record is what queues the deletion of the records it holds: `channelMembers` carries `onDelete: (r) => r.delete()`, so the members of a thread are
Original PR description
Before this commit, deleting a record leaves its id in the relations of a record deleted before it in the same update, and reading one of those relations hands out an entry for a record that is gone. This happens because an update takes a deleted record out of the relations that hold it, but forgets it as soon as it is deleted. However, deleting a record is what queues the deletion of the records it holds: `channelMembers` carries `onDelete: (r) => r.delete()`, so the members of a thread are deleted once the thread is already forgotten, and their ids stay in its `onlineMembers`. This commit fixes the issue by keeping the records deleted by an update known until it ends. Forward-Port-Of: odoo/odoo#281983
Before this commit, the hoot test "keep banner for messages received while scrolled up" failed at random on runbot: ``` Failed to find 1 of ".o-mail-Thread-banner:has(:text('1 new message'))" (Timeout of 10 seconds). Found 0 instead. ``` This happens because the test waits for the scroll position it sets in the DOM only, while the thread copies that position to the record on the scroll event, one animation frame later. Bob's message can arrive in between, when the record still says "bot
Original PR description
Before this commit, the hoot test "keep banner for messages received while scrolled up" failed at random on runbot:
```
Failed to find 1 of ".o-mail-Thread-banner:has(:text('1 new message'))"
(Timeout of 10 seconds). Found 0 instead.
```
This happens because the test waits for the scroll position it sets in the DOM only, while the thread copies that position to the record on the scroll event, one animation frame later. Bob's message can arrive in between, when the record still says "bottom": the counter the banner reads stays frozen at 0 and the message is marked as read on arrival, so the banner never shows.
This commit waits until the record holds that position before posting.
https://runbot.odoo.com/odoo/error/945967Before this commit: ----------- - When a product attribute had only one available value, it was not automatically selected for display types other than multi. After this commit: ------------ - Automatically select the attribute value when an attribute has a single available value and its display type is not multi, allowing the product to be added without any additional user interaction. Task-6327371 Forward-Port-Of: odoo/odoo#272437
Original PR description
Before this commit: ----------- - When a product attribute had only one available value, it was not automatically selected for display types other than multi. After this commit: ------------ - Automatically select the attribute value when an attribute has a single available value and its display type is not multi, allowing the product to be added without any additional user interaction. Task-6327371 Forward-Port-Of: odoo/odoo#272437
# Introduction This PR fixes broken mention links linked to the fact that we replace strings without paying attention to the fact that some strings may contain others that we want to replace later. This affects both id's and names of records. See commit messages for more details. # How to reproduce - Create Contact A and then Contact B and either : - Contact B's id need to contain Contact A's id (e.g. Contact B id = 12; Contact A id = 1) - Contact B's name need to contain Contact
Original PR description
# Introduction This PR fixes broken mention links linked to the fact that we replace strings without paying attention to the fact that some strings may contain others that we want to replace later.…
# Introduction
This PR fixes broken mention links linked to the fact that we replace strings
without paying attention to the fact that some strings may contain others
that we want to replace later. This affects both id's and names of records.
See commit messages for more details.
# How to reproduce
- Create Contact A and then Contact B and either :
- Contact B's id need to contain Contact A's id (e.g. Contact B id = 12; Contact A id = 1)
- Contact B's name need to contain Contact A's name (e.g. Contact B name = ABC; Contact A name = AB)
- In a chatter create a message mentionning first Contact B and then Contact A
> Depending on the version, you might need to reload the page here
- Edit the message and save
# The issue
We see a broken mention in the chatter
# Cause
When saving an edited message, we give the raw body of the message (without the mention links) and the mentionend partners to `generateMentionsLinks` : https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L152
This method's purpose is to replace the text links ("@Contact A") with actual html links. It does so by enumerating each partner given as an argument and replace the text mention with a placeholder :
https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L158
It will then replace the placeholders with actual links : https://github.com/odoo/odoo/blob/f9f605b1783d252d5e005bec50a2a72dd4ae0e13/addons/mail/static/src/utils/common/format.js#L208-L218
The issue is that in both of those steps, we can try to replace a string that is contained
in another string we want to replace.
For exemple :
"string123 some text string12"
If we try to replace "string12" first, then we will select the wrong string :
"[string12]3 some text string12".
opw-6313748
Forward-Port-Of: odoo/odoo#272549Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new c
Original PR description
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']"…
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new call carries the id of the one just left. Leaving broadcasts "discuss.channel.rtc.session/ended" for that id, and under load it lands after the join: the client reads it as its own session being closed and ends the call it has just joined. This commit numbers the records of a model with a counter, started above the ids its definition gives, as a database sequence does, so a notification about a deleted record can no longer name a live one. https://runbot.odoo.com/odoo/error/945965 Forward-Port-Of: odoo/odoo#282244
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7
Original PR description
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10…
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7 instead of 10. `mrp_subcontracting` overrides `_prepare_move_default_values` to force the move `location_dest_id` to the subcontractor location for every `is_subcontract` move: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/wizard/stock_picking_return.py#L20-L25 That is correct for the return, but the same override also runs for the exchange re-receipt, an `incoming` picking whose destination should be the stock location from `return_type.default_location_dest_id`: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/wizard/stock_picking_return.py#L137-L153 The exchange move then goes from the subcontracting location back to itself, so validating it nets zero and `WH/Stock` never receives the units. Skipping the override when `new_picking.picking_type_id.code` is `incoming` lets the exchange land in stock. The received quantity must also count that receipt. `_should_count_for_quantity_received` only counts `supplier` or `transit` sources: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/models/stock_move.py#L330-L331 so the exchange, sourced from the internal subcontracting location, is skipped while the return still subtracts its quantity. Counting subcontracting-sourced moves: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/models/stock_move.py#L312-L314 restores `qty_received` to 10. opw-6410978 Forward-Port-Of: odoo/odoo#279431
Miscellaneous changes
Behavior before: When uploading an animated GIF to fields utilizing image responsive sizing or cropping (such as employee avatars or product images), no downscaling or cropping occurs for sub-variants like 'image_128' or 'image_1024'. The responsive fields replicate the exact file size and data footprint of the original large image, leading to heavy storage overhead and unnecessary frontend asset loading. Behavior after: Animated GIF images scale down and crop correctly to match requested r
Original PR description
Behavior before: When uploading an animated GIF to fields utilizing image responsive sizing or cropping (such as employee avatars or product images), no downscaling or cropping occurs for…
Behavior before: When uploading an animated GIF to fields utilizing image responsive sizing or cropping (such as employee avatars or product images), no downscaling or cropping occurs for sub-variants like 'image_128' or 'image_1024'. The responsive fields replicate the exact file size and data footprint of the original large image, leading to heavy storage overhead and unnecessary frontend asset loading. Behavior after: Animated GIF images scale down and crop correctly to match requested responsive dimensions and aspect ratios. Sub-variants take up significantly less space in the filestore, matching proportional dimensions without dropping or stripping the underlying animation loop. Large images that are smaller than requested boxes are safely left un-upscaled to maximize database deduplication. Root Cause: Historically, a legacy safeguard bypassed GIF resizing and cropping because older versions of the Pillow library did not gracefully handle multi-frame sequential image buffers. As a result, standard 'image.crop()', 'image.thumbnail()', or 'image.resize()' implementations would flatten multi-frame animated sequences down into a single, static first frame or throw dimension/mode mismatches during save operations. Fix: Intercept the image processing pipeline when encountering an asset identified as a GIF where 'is_animated' evaluates to True. Implemented a unified, in-place multi-frame helper routine (`_apply_gif_operation`) using PIL's 'ImageSequence.Iterator' to cleanly step through, normalize to a uniform color mode (RGBA), duplicate, and modify each animation frame individually. This single helper handles sequential workflows for both 'crop' and 'thumbnail' operations while preserving individual frame duration arrays and native loop metadata. Both 'resize' and 'crop_resize' leverage this logic to achieve precise dimensions cleanly. Crucially, upscaling (expanding) is intentionally unsupported for animated GIFs. Forcing a low-resolution, 256-color indexed animation to stretch beyond its native dimensions forces heavy color dithering across every single frame. This breaks the sequential LZW pattern compression, causing the resulting file sizes to skyrocket catastrophically. The logic utilizes thumbnail boundaries to completely block this expansion, protecting the filestore from accidental bloat. Benchmark: -------------------------------------------------------------------------------------------- | GIF size | Variant | Size Before (KB) | Size After (KB) | |---------------|--------------------|--------------------------|-----------------------| | (2.5MB) | image_1024 | 2475.87 | 2475.87 | | | image_128 | 2475.87 | 257.93 | |---------------|--------------------|--------------------------|-----------------------| | (3.8MB) | image_1024 | 3724.93 | 3724.93 | | | image_128 | 3724.93 | 463.62 | |----------------|-------------------|--------------------------|-----------------------| | (442KB) | image_1024 | 432.49 | 432.49 | | | image_128 | 432.49 | 36.14 | |----------------|-------------------|--------------------------|-----------------------| | (3.6MB) | image_1024 | 3491.98 | 3491.98 | | | image_128 | 3491.98 | 1728.25 | |----------------|-------------------|--------------------------|-----------------------| opw-6232841 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#273098
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associate
Original PR description
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the…
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associated with the product, including those that are not visible on ecommerce. **Current behavior before PR:** When a product only has non-visible tags, the tags table is displayed without any content. <img width="669" height="350" alt="image" src="https://github.com/user-attachments/assets/4758ea76-5186-4035-a065-aa4c71ce7053" /> **Desired behavior after PR is merged:** The tags table is only displayed when the product has at least one tag visible on ecommerce. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278604
9 changes
Enhancements to existing features
The `create_calendar_meeting` field on `hr.leave.type` allows users to choose if leave requests created with a given time off type generate a corresponding entry in the Calendar app. However, this field was not displayed on the form view. This commit adds `create_calendar_meeting` to the `hr.leave.type` form view inside the configuration section, along with dedicated help text explaining its behavior. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Original PR description
The `create_calendar_meeting` field on `hr.leave.type` allows users to choose if leave requests created with a given time off type generate a corresponding entry in the Calendar app. However, this field was not displayed on the form view. This commit adds `create_calendar_meeting` to the `hr.leave.type` form view inside the configuration section, along with dedicated help text explaining its behavior. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280593
Resolved issues and error corrections
This change sanitizes some post data before allowing the post, making sure the data received by `message_post` is clean based on the current user. part of task-6452761
Original PR description
This change sanitizes some post data before allowing the post, making sure the data received by `message_post` is clean based on the current user. part of task-6452761
This change cleans up the requested data from `/mail/thread/data` route, ensuring it aligns with what is actually needed depending on the user and thread. part of task-6452761
Original PR description
This change cleans up the requested data from `/mail/thread/data` route, ensuring it aligns with what is actually needed depending on the user and thread. part of task-6452761
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7
Original PR description
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10…
Steps to reproduce ------------------ 1. Configure a product with a subcontracted BoM and a subcontractor. 2. Create a purchase order of 10 units for that product and confirm it. 3. Receive the 10 units. 4. On the receipt, use "Return for Exchange" on 3 units and validate both the return and the exchange receipt. Issue ----- After the exchange, the 3 units stay in the subcontracting location instead of reaching `WH/Stock`, and the received quantity on the purchase order line stays at 7 instead of 10. `mrp_subcontracting` overrides `_prepare_move_default_values` to force the move `location_dest_id` to the subcontractor location for every `is_subcontract` move: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/wizard/stock_picking_return.py#L20-L25 That is correct for the return, but the same override also runs for the exchange re-receipt, an `incoming` picking whose destination should be the stock location from `return_type.default_location_dest_id`: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/wizard/stock_picking_return.py#L137-L153 The exchange move then goes from the subcontracting location back to itself, so validating it nets zero and `WH/Stock` never receives the units. Skipping the override when `new_picking.picking_type_id.code` is `incoming` lets the exchange land in stock. The received quantity must also count that receipt. `_should_count_for_quantity_received` only counts `supplier` or `transit` sources: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/stock/models/stock_move.py#L330-L331 so the exchange, sourced from the internal subcontracting location, is skipped while the return still subtracts its quantity. Counting subcontracting-sourced moves: https://github.com/odoo/odoo/blob/d9c06a66356dd9d5a50821b8cde6194967353c18/addons/mrp_subcontracting/models/stock_move.py#L312-L314 restores `qty_received` to 10. opw-6410978
`Image.Palette.ADAPTIVE` is not available in the Pillow version provided by Ubuntu Jammy, causing the animated GIF test to fail. Use `Image.ADAPTIVE` instead, which is compatible with both older and newer Pillow versions. 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
Original PR description
`Image.Palette.ADAPTIVE` is not available in the Pillow version provided by Ubuntu Jammy, causing the animated GIF test to fail. Use `Image.ADAPTIVE` instead, which is compatible with both older and newer Pillow versions. 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
## Issue When filtering projects using the "Timesheets >100%" filter, some projects with negative remaining hours (and with their `is_project_overtime` field set to True) won't be displayed, even though their expected hours are completed. This happens with projects which have tasks set to the "Done" or "Cancelled" state. The timesheets entries in those tasks are not taken into account when searching using the "Timesheets >100%" filter. ## Steps to reproduce 1. Install *Task Logs* (`hr_ti
Original PR description
## Issue When filtering projects using the "Timesheets >100%" filter, some projects with negative remaining hours (and with their `is_project_overtime` field set to True) won't be displayed, even…
## Issue
When filtering projects using the "Timesheets >100%" filter, some projects with negative remaining hours (and with their `is_project_overtime` field set to True) won't be displayed, even though their expected hours are completed.
This happens with projects which have tasks set to the "Done" or "Cancelled" state. The timesheets entries in those tasks are not taken into account when searching using the "Timesheets >100%" filter.
## Steps to reproduce
1. Install *Task Logs* (`hr_timesheet`)
2. Create a Project P (with Timehseets enabled)
3. Set the allocated hours of the project to 3:00 (3 hours)
4. Create two tasks:
- T1: State "In progress", and one timesheet entry of 2:00 (2 hours)
- T2: State "Done", and one timesheet entry of 2:00 (2 hours)
5. Back to the project view, set the filter to "Timesheets >100%"
6. **Project P is not shown, even though the total time spent on the project is 4 hours, completing the allocated hours set on the project.**
## Cause
The `_search_is_project_overtime` method filters out the tasks in "closed" states (Done/Cancelled) when computing the amount of time spent on the project.
https://github.com/odoo/odoo/blob/126b5bdd1e85771549198976f8570cd2ff167608/addons/hr_timesheet/models/project_project.py#L103-L114
This does not match with the behavior of the `_compute_is_project_overtime`, which does not take into account the state of the tasks to determine the value of the field:
https://github.com/odoo/odoo/blob/126b5bdd1e85771549198976f8570cd2ff167608/addons/hr_timesheet/models/project_project.py#L85-L94
This leads to a confusing behavior, where a project can have its `is_project_overtime` field set to True, but will still not be shown when using the "Timsheets >100%", even though that filter is defined as `[("is_project_overtime", "=", True)]`.
The compute method was updated by https://github.com/odoo/odoo/commit/d4252825f52a3172420dcda0ea394e42da9f8853, but the related search method was left unchanged, leading to this slight incoherence between the two methods.
opw-6422173
Forward-Port-Of: odoo/odoo#281997Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new c
Original PR description
Before this commit, the test "Shows warning badge on mic/camera on non-granted permission in meeting conversations" failed on runbot, on 19.0: Failed to find 1 of "button[title='Turn camera on']" (Timeout of 10 seconds). Found 0 instead. This happens because the mock server numbers a new record with the highest id of the model plus one, so a record created right after the last one is deleted takes its id back. Joining another call leaves the meeting call first, and the session of the new call carries the id of the one just left. Leaving broadcasts "discuss.channel.rtc.session/ended" for that id, and under load it lands after the join: the client reads it as its own session being closed and ends the call it has just joined. This commit numbers the records of a model with a counter, started above the ids its definition gives, as a database sequence does, so a notification about a deleted record can no longer name a live one. https://runbot.odoo.com/odoo/error/945965
## Description Law no. 30-26 of June 18, 2026 updates Dominican ISR withholdings effective **July 1, 2026**: - **Professional services, fees, commissions, and rentals paid to individuals:** 10% to 15%. - **Specific foreign-payment categories:** 15% for royalties or rights, software licenses, online advertising, and the use or storage of data. - **General remittances abroad:** remain at 27% when they are outside those specific categories. The DGII's current **IR-17-2026 (July 2026 onward)** al
Original PR description
## Description Law no. 30-26 of June 18, 2026 updates Dominican ISR withholdings effective **July 1, 2026**: - **Professional services, fees, commissions, and rentals paid to individuals:** 10% to…
## Description Law no. 30-26 of June 18, 2026 updates Dominican ISR withholdings effective **July 1, 2026**: - **Professional services, fees, commissions, and rentals paid to individuals:** 10% to 15%. - **Specific foreign-payment categories:** 15% for royalties or rights, software licenses, online advertising, and the use or storage of data. - **General remittances abroad:** remain at 27% when they are outside those specific categories. The DGII's current **IR-17-2026 (July 2026 onward)** also confirms that the concepts discussed in review are three distinct reporting rows: - **Row 4 — Transfers of titles and properties:** 2%. - **Row 17 — Other income under Decree 139-98, Article 70(a)/(b):** 3%. - **Row 18 — Other withholdings under General Rule 07-2007, as amended by Law 30-26:** 3%. The two 3% rows must not be conflated with each other or with the separate 2% transfer withholding. ## Implementation The changed-rate template entries use new, rate-explicit XML IDs, as recommended in review: - `ret_15_income_person`: new -15% fee withholding, replacing `ret_10_income_person` in the template; posts to `21030301`. - `ret_15_income_rent`: new -15% rental withholding, replacing `ret_10_income_rent` in the template; posts to `21030302`. - `ret_3_income_person`: new -3% General Rule 07-2007 withholding, replacing `ret_2_income_person` in the template; posts to `21030308`. - `tax_group_person_services_15`: new grouped tax using `ret_15_income_person`. - `tax_group_person_construction_3`: new grouped tax using `ret_3_income_person`. - `position_person_services_15`: new physical-services fiscal position mapped only to the current 15% grouped tax. The remaining legal concepts stay separate: - `ret_3_income_article_70`: new -3% tax for Decree 139-98, Article 70(a)/(b), posted to `21030309`. - `ret_2_income_transfer`: remains at -2% on `21030306`; its misleading “Materials” source metadata is corrected to transfers of titles and properties. - `ret_27_income_remittance`: remains active at -27% on `21030307`, with the general foreign-services fiscal position unchanged. - `ret_15_income_foreign_royalties_technology`: new -15% tax only for the foreign categories covered by Law 30-26. It posts to the new `21030310` Law 30-26 payable account rather than the L253-12 remittance account. - `position_exterior_royalties_technology`: new fiscal position mapping purchases only to that specific 15% tax. The specific foreign tax keeps the short invoice label `-15% ISR (L30-26)` to avoid wrapping in vendor-bill PDFs. The original manifest author entry is unchanged, as requested. The Git history and corporate CLA record this contribution by Grupo de Consultoria Henca. ### Existing-company reload behavior The superseded 10%/2% tax, grouped-tax, and physical-services fiscal-position rows are removed from the template rather than shipped as obsolete entries to new companies. On an existing company, Odoo's chart reload keeps those historical records and generated XML IDs unchanged, then creates the new current-rate records under the new XML IDs. On a newly loaded chart, only the current template entries are created. The physical-services fiscal position also has a new XML ID intentionally. Reload preserves existing fiscal-position mappings as user-configurable data and only appends mappings involving new taxes; reusing `position_person` could therefore leave both the historical and current destinations on one position. `position_person_services_15` keeps the current mapping isolated while the old position remains available for historical operations. `ret_2_income_transfer` keeps its existing XML ID because its legal rate, concept, and account remain 2% / transfers / `21030306`. The corrected label is present for new charts; backfilling label-only metadata on already-loaded charts would require an explicit upgrade migration because standard chart reload does not rewrite that user-visible metadata. ## Official references - DGII, current IR-17-2026 download page (July 2026 onward): https://dgii.gov.do/herramientas/formularios/formularioDeclaraciones/Paginas/impuestosRetencionesyRetribuciones.aspx - Law 30-26: https://www.consultoria.gov.do/Consulta/Home/FileManagement?documentId=3405887&managementType=1 - DGII implementation calendar, notice 10-26: https://dgii.gov.do/publicacionesOficiales/avisosInformativos/Documents/2026/10-26.pdf - DGII CA59, calculation for professional and technical services: https://ayuda.dgii.gov.do/conversations/retenciones-y-retribuciones-complementarias/ca59-qu-porcentaje-del-isr-deben-retener-las-personas-jurdicas-a-las-personas-fsicas-en-la-prestacin-de-servicios/5f3c175f8cd858ce879a130f - DGII clarification for General Rule 07-2007 and the construction sector: https://ayuda.dgii.gov.do/conversations/discusiones/aplicacin-de-la-ley-nm-3026-respecto-a-la-retencin-prevista-en-el-artculo-3-de-la-norma-general-072007-sector-construccin/6a45792cde3c6003da189ff1 - DGII legal basis for the 2% transfer withholding: https://ayuda.dgii.gov.do/conversations/discusiones/base-legal-retencion-2-transferencia-de-titulos-y-propiedades/5f6355928cd858ce872bb35a - Ministry clarification on the specific foreign technology and royalty categories: https://www.hacienda.gob.do/ley-30-26-no-dispone-impuestos-por-suscripciones-de-ciudadanos-a-plataformas-digitales-reduce-de-27-a-15-la-retencion-a-empresas-que-contratan-servicios-tecnologicos-en-el-exterior/ Legal basis cited by DGII: Law 11-92, article 309, as amended by Law 30-26, article 17; Regulation of Title II of the Tax Code, article 70. ## Validation - The branch is rebased on the current 17.0 head and contains one squashed commit. - The account, tax-template, and fiscal-position CSV files have consistent column counts, unique IDs, and valid child/account references. - A fresh Dominican chart contains only the new current-rate IDs; the superseded template IDs are absent. - The fresh chart was verified with fees/rentals at 15%; General Rule 07-2007 at 3% on `21030308`; Article 70(a)/(b) at 3% on `21030309`; transfers at 2% on `21030306`; general remittances at 27% on `21030307`; and the covered foreign categories at 15% on `21030310`. - The 15% services and 3% construction groups contain exactly the current tax children, and the new services fiscal position maps each purchase tax to only the 15% group. - An existing company loaded from the pre-law template was reloaded twice. Its historical 10%/10%/2% taxes, historical groups, and historical fiscal position retained their original record IDs and configuration; all current records were created once; the old and new positions each retained exactly two isolated mappings; and the second reload was idempotent. - `/account:TestChartTemplate`: 22 tests passed, 0 failures, 0 errors. This is the first contribution by Grupo de Consultoria Henca (https://www.consultoriahenca.com); the corporate CLA signature is included in `doc/cla/corporate/consultoriahenca.md` as instructed by `doc/cla/sign-cla.md`. Forward-Port-Of: odoo/odoo#275986
When loading the registry, borrow the request to avoid having using the cursor linked to it. Failing use case before the fix: install base odoo, in the web interface, activate and switch to a new language then install website. The installation will hang if the stable cache is invalidated because the installation may call `_` which will fallback to the request's language - reading the table from the request's cursor - while the installation tries to update the same of that same table in anothe
Original PR description
When loading the registry, borrow the request to avoid having using the cursor linked to it. Failing use case before the fix: install base odoo, in the web interface, activate and switch to a new language then install website. The installation will hang if the stable cache is invalidated because the installation may call `_` which will fallback to the request's language - reading the table from the request's cursor - while the installation tries to update the same of that same table in another cursor. Backport of odoo/odoo#281797 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281798
6 changes
Resolved issues and error corrections
Follow-up to the backport of the response module in commit 9ca317f99ed5acfca0da42b9cb99ffd995a41b8a . Currently (on 17.0) we do not send the supported identifiers when registering. So i.e. not the response service This is fixed in this commit. The IAP side had to be adapted so it was omitted in the original commit (since the main purpose was to backport PDP). IAP PR: https://github.com/odoo/iap-apps/pull/1757 task-None
Original PR description
Follow-up to the backport of the response module in commit 9ca317f99ed5acfca0da42b9cb99ffd995a41b8a . Currently (on 17.0) we do not send the supported identifiers when registering. So i.e. not the response service This is fixed in this commit. The IAP side had to be adapted so it was omitted in the original commit (since the main purpose was to backport PDP). IAP PR: https://github.com/odoo/iap-apps/pull/1757 task-None
#### Description of the issue/feature this PR addresses: Spanish amounts in words render "uno" where the grammar requires the apocopated "un" — on invoice PDFs, CFDI amounts, and anywhere num2words is called with a Spanish language. #### Current behavior before PR: "DOS MILLONES TRESCIENTOS UNO MIL CUATROCIENTOS TREINTA Y NUEVE PESOS 88/100 M.N." num2words applies the apocope only in to_currency(), never in to_cardinal(), and Odoo renders the plain cardinal then appends the currency label i
Original PR description
#### Description of the issue/feature this PR addresses: Spanish amounts in words render "uno" where the grammar requires the apocopated "un" — on invoice PDFs, CFDI amounts, and anywhere num2words…
#### Description of the issue/feature this PR addresses: Spanish amounts in words render "uno" where the grammar requires the apocopated "un" — on invoice PDFs, CFDI amounts, and anywhere num2words is called with a Spanish language. #### Current behavior before PR: "DOS MILLONES TRESCIENTOS UNO MIL CUATROCIENTOS TREINTA Y NUEVE PESOS 88/100 M.N." num2words applies the apocope only in to_currency(), never in to_cardinal(), and Odoo renders the plain cardinal then appends the currency label itself. Present in both versions pinned in requirements.txt (0.5.10, 0.5.13). #### Desired behavior after PR is merged: "DOS MILLONES TRESCIENTOS UN MIL CUATROCIENTOS TREINTA Y NUEVE PESOS 88/100 M.N." The apocope is applied in to_cardinal() through the num2words monkey patches, for es, es_CO and es_VE. Other languages are untouched. Note: the cardinal is now always apocopated, so a standalone count reads "un" rather than "uno". opw-6375677 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
the field l10n_sa_edi_is_production was introduced in 19.0, so it was never supposed to survive the backport to 17.0 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
Original PR description
the field l10n_sa_edi_is_production was introduced in 19.0, so it was never supposed to survive the backport to 17.0 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
Calling `/shop/payment/validate` as a portal user with an empty cart confirms the empty sale order. Steps to reproduce: - Sign in as a portal user. - Add a product to the cart. - Remove the product. - Go to `/shop/payment/validate`. - The empty sale order is confirmed. opw-6430637
Original PR description
Calling `/shop/payment/validate` as a portal user with an empty cart confirms the empty sale order. Steps to reproduce: - Sign in as a portal user. - Add a product to the cart. - Remove the product. - Go to `/shop/payment/validate`. - The empty sale order is confirmed. opw-6430637
When loading the registry, borrow the request to avoid having using the cursor linked to it. Failing use case before the fix: install base odoo, in the web interface, activate and switch to a new language then install website. The installation will hang if the stable cache is invalidated because the installation may call `_` which will fallback to the request's language - reading the table from the request's cursor - while the installation tries to update the same of that same table in anothe
Original PR description
When loading the registry, borrow the request to avoid having using the cursor linked to it. Failing use case before the fix: install base odoo, in the web interface, activate and switch to a new language then install website. The installation will hang if the stable cache is invalidated because the installation may call `_` which will fallback to the request's language - reading the table from the request's cursor - while the installation tries to update the same of that same table in another cursor. Backport of odoo/odoo#281797 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
**Steps to reproduce:** 1. Install Accounting 2. Import a new invoice with more than 1000 lines (xlsx file found in ticket attachments) 3. Test the imported records **Issue:** - `RecursionError: maximum recursion depth exceeded`. **Cause:** - In a previous commit (3e32d7b9eace62dfa7334009707a93967906c726) aimed at fixing stale analytic distribution totals, the assignment loop in `_compute_discount_allocation_needed` was changed from iterating over `self` to `self.move_id.line_ids`. -
Original PR description
**Steps to reproduce:** 1. Install Accounting 2. Import a new invoice with more than 1000 lines (xlsx file found in ticket attachments) 3. Test the imported records **Issue:** - `RecursionError:…
**Steps to reproduce:** 1. Install Accounting 2. Import a new invoice with more than 1000 lines (xlsx file found in ticket attachments) 3. Test the imported records **Issue:** - `RecursionError: maximum recursion depth exceeded`. **Cause:** - In a previous commit (3e32d7b9eace62dfa7334009707a93967906c726) aimed at fixing stale analytic distribution totals, the assignment loop in `_compute_discount_allocation_needed` was changed from iterating over `self` to `self.move_id.line_ids`. - While this ensured all lines generated updated distribution ratios, it violated the compute logic: assigning values to records outside the current compute batch (`self`). - By executing `line.discount_allocation_dirty = True` on external sibling lines, the method forced the ORM to trigger out-of-band `write()` calls. These writes re-triggered dependency checks (`_field_will_change`), which invoked the compute method again, leading to a recursive loop. **Fix:** 1. Revert the assignment iteration back to `for line in self:`. 2. To preserve the intention of the previous commit (ensuring all lines recompute their shared distribution pool when one line changes), modify the method's `@api.depends` to be `move_id.line_ids.discount` and `move_id.line_ids.analytic_distribution`. By declaring these relational dependencies, modifying a single line now batches all sibling lines into `self` from the start. This allows the lines to synchronize properly without triggering new ORM writes, eliminating the recursion. opw-6451854