Monday, August 17, 2026
15 changes · saas-19.2
Resolved issues and error corrections
Peruvian accounting reports now use the exchange rate already stored on each accounting entry instead of recalculating it when reports are generated. This reduces rounding differences and improves the reliability of reported figures.
Original PR description
Previously, the `_get_ple_report_data` method computed the currency rate when called. Since the calculation was based on the entry totals, it was prone to rounding errors. This PR makes it use the rate stored in the entry itself. This should lead to more accurate results. opw-6411322 Forward-Port-Of: odoo/enterprise#126882
## 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#282338
Forward-Port-Of: odoo/odoo#281997In this commit: - Ensure event ticket information is preserved during self-order processing and use the configured ticket price when recomputing order line prices. - This prevents ticket prices from being replaced by the product price after proceeding to payment and keeps the amounts consistent across the payment page. Task:6375899 Forward-Port-Of: odoo/odoo#275645
Original PR description
In this commit: - Ensure event ticket information is preserved during self-order processing and use the configured ticket price when recomputing order line prices. - This prevents ticket prices from being replaced by the product price after proceeding to payment and keeps the amounts consistent across the payment page. Task:6375899 Forward-Port-Of: odoo/odoo#275645
**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 Forward-Port-Of: odoo/odoo#282050
compute_date_from_to calls compute_work_entry_type_id calls _compute_duration, orm finds a cyclic dep so it doesn't call compute_date_from_to again) This commit makes _compute_work_entry_type_id an onchange method instead since it's only needed for the view and adds work_entry_type_id as dependency of date_from_to Task-6222907
Original PR description
compute_date_from_to calls compute_work_entry_type_id calls _compute_duration, orm finds a cyclic dep so it doesn't call compute_date_from_to again) This commit makes _compute_work_entry_type_id an onchange method instead since it's only needed for the view and adds work_entry_type_id as dependency of date_from_to Task-6222907
**Problem**: When creating a percentage down payment with a Brazilian tax, the divide by zero error occurs because the factor contains a zero value. **Steps to reproduce**: 1. Set up Brazil localization with ```l10n_br_avatax```, ```l10n_br_edi```. 2. Create a service product with ```LC116``` code set up. 3. Create a sales order with the customer has ```Automatic Tax Mapping (Avalara Brazil)``` in their Fiscal Position. 4. Add the service product to the sales order and confirm it. 5. Cr
Original PR description
**Problem**: When creating a percentage down payment with a Brazilian tax, the divide by zero error occurs because the factor contains a zero value. **Steps to reproduce**: 1. Set up Brazil localization with ```l10n_br_avatax```, ```l10n_br_edi```. 2. Create a service product with ```LC116``` code set up. 3. Create a sales order with the customer has ```Automatic Tax Mapping (Avalara Brazil)``` in their Fiscal Position. 4. Add the service product to the sales order and confirm it. 5. Create a percentage down payment for the sales order. 6. Divide by zero error occurs. **Fix**: Check if the ```sum_of_factors``` is zero and if so, set the ```delta_factor``` to zero. before ```normalize_results['plus_sum_of_factors'] / normalize_results['sum_of_factors']``` opw-6446986
Steps: - Install sale_management - Make sure you have 100 quotations with 1 activity each - Open activity view - Default pager displays `1-100/100` - Activity count display `To-do 80` ActivityController uses `useModel` which passes the raw `component.props` to `model.load()`, including the limit from `ir.actions.act_window` (default 80). This value ended up in `fetchActivityData` via `params.limit || this.initialLimit`, overriding `ActivityModel.DEFAULT_LIMIT` (100). The records li
Original PR description
Steps: - Install sale_management - Make sure you have 100 quotations with 1 activity each - Open activity view - Default pager displays `1-100/100` - Activity count display `To-do 80`…
Steps: - Install sale_management - Make sure you have 100 quotations with 1 activity each - Open activity view - Default pager displays `1-100/100` - Activity count display `To-do 80` ActivityController uses `useModel` which passes the raw `component.props` to `model.load()`, including the limit from `ir.actions.act_window` (default 80). This value ended up in `fetchActivityData` via `params.limit || this.initialLimit`, overriding `ActivityModel.DEFAULT_LIMIT` (100). The records list was not affected because `RelationalModel._getNextConfig` never reads `params.limit` (limit is not a `SEARCH_KEY`), so it always loaded 100 records correctly. But `fetchActivityData` used 80, causing a mismatch between the records shown and the activity counts in the column headers. ```js export const SEARCH_KEYS = ["comparison", "context", "domain", "groupBy", "orderBy"]; ``` The fix strips `params.limit` in `ActivityModel.load()` before passing params to `fetchActivityData`, so it falls back to `this.initialLimit (100)`. The pager `onUpdate` handler calls `fetchActivityData` directly with its own `params.limit` and is not affected. However, `ActivityController` never forwards `limit` to the model. This is why we always have `ActivityModel.DEFAULT_LIMIT (100)` without taking into account actions's limit. To fix this we have to add the limit via `this.props.limit`, as `ListController`. `useModelWithSampleData` already had the correct behavior by calling `model.load(getSearchParams(props))` which filters out non-search params like limit. In 19.0 useModel was updated to do the same, so the issue does not exist there. Link to 19.0 fix: https://github.com/odoo/odoo/pull/211697 opw-6281125 Forward-Port-Of: odoo/odoo#281227 Forward-Port-Of: odoo/odoo#273929
On highly loaded runbots, waiting a single animation frame might not be sufficient for the table deselection to be available in the DOM after a key press. This commit waits for the table to be deselected before continuing the test. runbot-944722 Forward-Port-Of: odoo/odoo#282008
Original PR description
On highly loaded runbots, waiting a single animation frame might not be sufficient for the table deselection to be available in the DOM after a key press. This commit waits for the table to be deselected before continuing the test. runbot-944722 Forward-Port-Of: odoo/odoo#282008
In this commit: - The feedback screen was not scaling properly on Android devices and tablet displays, causing content to appear too small or overflow. - Fixed by making the checkmark and text sizes responsive using units so the layout adapts correctly across different screen sizes. Task: 6420543 Forward-Port-Of: odoo/odoo#282078 Forward-Port-Of: odoo/odoo#279328
Original PR description
In this commit: - The feedback screen was not scaling properly on Android devices and tablet displays, causing content to appear too small or overflow. - Fixed by making the checkmark and text sizes responsive using units so the layout adapts correctly across different screen sizes. Task: 6420543 Forward-Port-Of: odoo/odoo#282078 Forward-Port-Of: odoo/odoo#279328
The dynamic field editor assumes that an `Applies To` model is always selected and that existing dynamic fields are always valid for the current model. As a result, trying to insert or edit a dynamic field without selecting a model raised an error. Editing an existing dynamic field after changing the selected model could also crash the field selector when the stored field path was no longer valid. Show a notification when users try to insert or edit a dynamic field without selecting a model,
Original PR description
The dynamic field editor assumes that an `Applies To` model is always selected and that existing dynamic fields are always valid for the current model. As a result, trying to insert or edit a dynamic field without selecting a model raised an error. Editing an existing dynamic field after changing the selected model could also crash the field selector when the stored field path was no longer valid. Show a notification when users try to insert or edit a dynamic field without selecting a model, and handle invalid field paths when initializing the field selector to avoid UI crashes. Task-6365420
### Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation on anything else than the company, name or workcenter - Go back to the MO, click the "Update Bom" button > The second operation is not unlinked and the first operation is not updated ### Cause of the issue: The `action_update_bom` updates the move raws and operations of the MO via t
Original PR description
### Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first…
### Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation on anything else than the company, name or workcenter - Go back to the MO, click the "Update Bom" button > The second operation is not unlinked and the first operation is not updated ### Cause of the issue: The `action_update_bom` updates the move raws and operations of the MO via the `_link_bom`: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L1214-L1218 For draft MO's all the work of these updates is done via the compute methods and by deleting all the records unrelevant to the new bom: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2603-L2626 And, in that case all the workorders that are not linked to an operation of the bom are expected to be deleted. However, when the MO is not in draft, the update of operations is expected to be performed here: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2647-L2664 However, since the operation of the bom has been deleted, the workorder that is expected to be deleted is not linked to any operation and hence does not satisfy the condition to be deleted: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2663-L2664 Concerning the non update of operations, it happens because the MO's operation are only updated on the three fields: `company_id`, `workcenter_id`, `name`: https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2647-L2664 https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2628-L2629 However, many other changes can and are actually relevant. ### Note: Prior to commit 80e6ed658fb43584bc2fad673ca40d9af6cf0ab6 operations were archived on boms rather than deleted: https://github.com/odoo/odoo/blob/4a5270218fe6fd7d30edb6d684b3340dc7423bab/addons/mrp/views/mrp_routing_views.xml#L53-L55 As such they would still be linked to an operation (but unrelated to the present values of the bom) and hence would fall into the condition of being unlinked from the MO. Since the bom operations are no longer archived there is no way to determine if an operation used to be linked to a bom and we therefore need to chose between deleting all operations unrelated to the present bom or to keep them all (when the MO has been confirmed). Enterprise: https://github.com/odoo/enterprise/pull/120709 opw-6285878 opw-6261738 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#273875 Forward-Port-Of: odoo/odoo#269747
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The `move_raw_ids` field on the MO form sets a context with `form_view_ref: 'mrp.view_mrp_stock_move_operations'`, so that editing a component's `stock.move` line in place opens that dedicated view: https://github.com/odoo/odoo/blob/20db2910d16fa6ebd07794fb9ed3fb1c8f57b0fc/addons/mrp/views/mrp_prod
Original PR description
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The…
**Steps to reproduce** - Open any MO - Click on 'catalog' - Click on the three dots button of any product - Try to edit -> Traceback: `"product.product"."state" field is undefined.` **Cause** The `move_raw_ids` field on the MO form sets a context with `form_view_ref: 'mrp.view_mrp_stock_move_operations'`, so that editing a component's `stock.move` line in place opens that dedicated view: https://github.com/odoo/odoo/blob/20db2910d16fa6ebd07794fb9ed3fb1c8f57b0fc/addons/mrp/views/mrp_production_views.xml#L411-L417 The 'Catalog' button lives inside that same field, and calls `action_add_from_catalog_raw`, which delegates to the mixin's `action_add_from_catalog`: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L17 Which conserve the `'form_view_ref'` from context: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L28 and that action requests its form view with `view_id=False`: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/product/models/product_catalog_mixin.py#L25 Which will try to load `view_mrp_stock_move_operations`, since: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/odoo/addons/base/models/ir_ui_view.py#L3002-L3005 `_get_view` never checks that the resolved view's `model` matches the model it was asked for, so it returns `view_mrp_stock_move_operations`for the `product.product` model: https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/mrp/views/stock_move_views.xml#L41 https://github.com/odoo/odoo/blob/3dd41395e2e4205fa477eb474d4a4dba0a976154/addons/stock/views/stock_move_views.xml#L128 `state` doesn't exist on `product.product`, hence the crash. opw-6433620 Forward-Port-Of: odoo/odoo#279811
At installation of the l10n_pl_bank_verification module or when upgrading from a lower version, the field l10n_pl_verification_id on the account.payment model gets computed and it makes the upgrade crash out. Adding a init to the model to create the column to prevent the ORM from computing the field at module installation no-task Forward-Port-Of: odoo/odoo#282504
Original PR description
At installation of the l10n_pl_bank_verification module or when upgrading from a lower version, the field l10n_pl_verification_id on the account.payment model gets computed and it makes the upgrade crash out. Adding a init to the model to create the column to prevent the ORM from computing the field at module installation no-task Forward-Port-Of: odoo/odoo#282504
Documentation and clarification updates
Add the individual CLA signature for Han Zaw Nyein (`HanZawNyein`). This signature covers contributions including odoo/odoo#281412 and odoo/odoo#281415. Forward-Port-Of: odoo/odoo#281609
Original PR description
Add the individual CLA signature for Han Zaw Nyein (`HanZawNyein`). This signature covers contributions including odoo/odoo#281412 and odoo/odoo#281415. Forward-Port-Of: odoo/odoo#281609
Miscellaneous changes
Allow resetting sent moves to draft. Ensures a rectificative flow exists or is created. Task: 6273211 Forward-Port-Of: odoo/odoo#273013
Original PR description
Allow resetting sent moves to draft. Ensures a rectificative flow exists or is created. Task: 6273211 Forward-Port-Of: odoo/odoo#273013