Wednesday, July 29, 2026
13 changes · saas-18.3
Resolved issues and error corrections
The Timesheets overtime display now keeps the same unit when users switch to another language. This prevents remaining time from unexpectedly changing from days back to hours, making workforce reporting clearer for multilingual teams.
Original PR description
Steps to reproduce: ------------------- 1. Install Timesheets. 2. Create a new employee with a fully fixed working schedule (40h/week). 3. Open Timesheets > All Timesheets and create a new timesheet…
Steps to reproduce: ------------------- 1. Install Timesheets. 2. Create a new employee with a fully fixed working schedule (40h/week). 3. Open Timesheets > All Timesheets and create a new timesheet in the past week for this employee (e.g. 8 hours). 4. Observe the overtime indicator for the employee (it shows 32 hrs) (Click the left arrow to display it). 5. Change the timesheet encoding unit to "Days / Half-Days". 6. Return to Timesheets and observe the overtime indicator (it now correctly shows 4 days). 7. Install a language other than English (e.g. French). 8. Return to Timesheets and observe the overtime indicator again. Issue: -------- The remaining time value changes unexpectedly and displays 32 hours instead of 4 days. Cause: --------- In `get_timesheet_and_working_hours_for_employees`, the code determines whether the timesheet UoM is expressed in days by comparing the UoM name with the string `"days"`. Since UoM names are translatable, this comparison becomes invalid when the user language changes (e.g. `"jours"` in French), causing the logic to skip the day conversion and return values in hours instead. https://github.com/odoo/enterprise/blob/c48290e90fdeadf4f9ca8c44b035e601e7ed380a/timesheet_grid/models/hr_employee.py#L165-L169 Solution: ----------- Compare the timesheet UoM record with the day UoM record directly instead of relying on translated string values. see commit: https://github.com/odoo/enterprise/commit/5fbf194c5056566453a124812fd2614edfe19a82 opw-6279133 Forward-Port-Of: odoo/enterprise#120595
This fix prevents Swiss pension fund numbers from being included in status checks for BVG-LPP payroll declarations. It helps avoid incorrect declaration status handling and supports more reliable Swiss payroll processing.
Original PR description
Forward-Port-Of: odoo/enterprise#126040
The `bus_monitoring_service` test "connection considered as lost after failed reconnect attempt" fails about half of the time. Since [1], the reconnect delay in tests is much smaller. The retry therefore fires while the mock socket is still in the closing state. `_start` detects that socket and triggers the close event manually to keep the lifecycle consistent. In other cases, the error event will schedule a reconnect but in this case it will never arrive. The worker is then left with n
Original PR description
The `bus_monitoring_service` test "connection considered as lost after failed reconnect attempt" fails about half of the time. Since [1], the reconnect delay in tests is much smaller. The retry…
The `bus_monitoring_service` test "connection considered as lost after failed reconnect attempt" fails about half of the time. Since [1], the reconnect delay in tests is much smaller. The retry therefore fires while the mock socket is still in the closing state. `_start` detects that socket and triggers the close event manually to keep the lifecycle consistent. In other cases, the error event will schedule a reconnect but in this case it will never arrive. The worker is then left with no socket, no listeners and no pending timeout: it never reconnects. Schedule the reconnection when handling a manually triggered close, since no error event will follow to do it. [1]: https://github.com/odoo/odoo/pull/278075 runbot-944578 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#278779
**Issue:** `test_mail_template_dynamic_placeholder_tour` tour is failing sometimes with the following error: `Tour mail_template_dynamic_field_tour → Step Click on contact (trigger: div[name="model_id"] .ui-autocomplete). TypeError: Cannot read properties of undefined (reading 'click')` **Cause:** It happens that the element is not loaded yet after the delay and clicking on an undefined element triggers the error. **Solution:** Make sure to only click on the element when it's ready.
Original PR description
**Issue:** `test_mail_template_dynamic_placeholder_tour` tour is failing sometimes with the following error: `Tour mail_template_dynamic_field_tour → Step Click on contact (trigger: div[name="model_id"] .ui-autocomplete). TypeError: Cannot read properties of undefined (reading 'click')` **Cause:** It happens that the element is not loaded yet after the delay and clicking on an undefined element triggers the error. **Solution:** Make sure to only click on the element when it's ready. runbot-223306 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277954
For an MO with component tracked by lot is possible to close it without assigning the lot number even when they are marked for manual consumption. From the shopfloor this will close the MO that will disappear when it's gone it will throw a user error, without the MO reappearing. Steps to reproduce --------------------------- **Issue 1** 1) Create a product "final" 2) Create a product "component" tracked by lot 3) Update the on hand quantity and don't assign a lot number 4) Create a BoM
Original PR description
For an MO with component tracked by lot is possible to close it without assigning the lot number even when they are marked for manual consumption. From the shopfloor this will close the MO that will…
For an MO with component tracked by lot is possible to close it without assigning the lot number even when they are marked for manual consumption. From the shopfloor this will close the MO that will disappear when it's gone it will throw a user error, without the MO reappearing. Steps to reproduce --------------------------- **Issue 1** 1) Create a product "final" 2) Create a product "component" tracked by lot 3) Update the on hand quantity and don't assign a lot number 4) Create a BoM for "final" with "component" as component and manual consumption 5) Create a MO and confirm it 6) Go to shopfloor 7) Set "final" quantity 8) Set "component" quantity 10) Close the MO -> The MO disappear (leaving a user error) -> Since the "component" does not have a lot number it should not disappear **Issue 2** Same steps as Issue 1 until step 6: 6*) Add "comp" as barcode to the product component 7*) Go to barcode 8*) Open the manufacturing order 9*) Add the quantity to final 10*) Scan "comp" 11*) Scan a non-existent lot number, ex: "12345" -> It raise the error Observation ---------------------- **Issue 1** When clicking on "Closing production" it will call [onClickValidateButton](https://github.com/odoo/enterprise/blob/37b21f93828fae2caec87cfd8413e144751eefc0/mrp_workorder/static/src/mrp_display/mrp_display_record.js#L481-L482)() that will lead us to validate, and validate will [call](https://github.com/odoo/enterprise/blob/37b21f93828fae2caec87cfd8413e144751eefc0/mrp_workorder/static/src/mrp_display/mrp_display_record.js#L511) the function [pre_button_mark_done](https://github.com/odoo/enterprise/blob/37b21f93828fae2caec87cfd8413e144751eefc0/mrp_workorder/static/src/mrp_display/mrp_display_record.js#L507) in python. in pre_button_mark_done, we will skip any check on the lot number, since we already have set qty_producing (when setting "final" quantity), it will avoid _set_quantities where a check for lot number is made: https://github.com/odoo/odoo/blob/9218d302b0fd56c1854d95d3756c0f5e9c9c8700/addons/mrp/models/mrp_production.py#L2235-L2239 https://github.com/odoo/odoo/blob/9218d302b0fd56c1854d95d3756c0f5e9c9c8700/addons/mrp/models/mrp_production.py#L2839-L2844 And it will also skip the consumption wizard since it doesn't check for lot number nor allow to set lot numbers: https://github.com/odoo/odoo/blob/35f804995118e6ffe150c9c7a7bad4844bd0e0ed/addons/mrp/models/mrp_production.py#L1658-L1660 After avoiding both checks we go back to validate. In validate and we update the variable underValidation which will [trigger the fadeout animation](https://github.com/odoo/enterprise/blob/37b21f93828fae2caec87cfd8413e144751eefc0/mrp_workorder/static/src/mrp_display/mrp_display_record.js#L154) When the [animation ends](https://github.com/odoo/enterprise/blob/37b21f93828fae2caec87cfd8413e144751eefc0/mrp_workorder/static/src/mrp_display/mrp_display_record.js#L662-L665), the call to realValidation will be triggered and will call productionValidation that will send a call to [button_mark_done](https://github.com/odoo/enterprise/blob/37b21f93828fae2caec87cfd8413e144751eefc0/mrp_workorder/static/src/mrp_display/mrp_display_record.js#L551) In button_mark_done, when calling _action_done on the move_lines, we will finally check that there is a lot number, which will trigger an error : https://github.com/odoo/odoo/blob/35f804995118e6ffe150c9c7a7bad4844bd0e0ed/addons/stock/models/stock_move_line.py#L664-L669 but because the fadeout animation is already over, the mo will have disappeared, which means we can't correct the userError that has been raised. **Issue 2** In barcode when scanning a lot after a product, it will not create a lot but only apply a lot_name : https://github.com/odoo/enterprise/blob/7b57a2927aeb8abf84d0f6acc64a7e9bbc8f608d/stock_barcode/static/src/models/barcode_model.js#L1306-L1311 this cause an issue with the existing condition since it only pass the check if there is a lot, but in this case there only is a lot_name. When we click on "Produce" in a Mo in barcode, it will call validate, that in this case, will call button_mark_done: https://github.com/odoo/enterprise/blob/80aaa507fd2196b90cea724ecab0fa0ed5f13db6/stock_barcode/static/src/components/main.xml#L163 https://github.com/odoo/enterprise/blob/80aaa507fd2196b90cea724ecab0fa0ed5f13db6/stock_barcode/static/src/models/barcode_model.js#L602-L605 https://github.com/odoo/enterprise/blob/80aaa507fd2196b90cea724ecab0fa0ed5f13db6/stock_barcode_mrp/static/src/models/barcode_mrp_model.js#L14 opw-6060310 Forward-Port-Of: odoo/odoo#257635
While fixing the conflict of #278420 I missed the percentage of the taxes and they got set as 25 and 15% instead of 0% as required in task-6254727
Original PR description
While fixing the conflict of #278420 I missed the percentage of the taxes and they got set as 25 and 15% instead of 0% as required in task-6254727
Issue: --- Adding a rating with a message on website is causing TB as the portal user doesn't have the access to send rating message. opw-6316142 Forward-Port-Of: odoo/odoo#277291 Forward-Port-Of: odoo/odoo#271833
Original PR description
Issue: --- Adding a rating with a message on website is causing TB as the portal user doesn't have the access to send rating message. opw-6316142 Forward-Port-Of: odoo/odoo#277291 Forward-Port-Of: odoo/odoo#271833
The mail.message/delete bus handler accessed selfMember?.seen_message_id.id with optional chaining only on selfMember, not on seen_message_id. When a member has never seen any message in a channel (seen_message_id is False), accessing .id threw 'TypeError: can't access property id, selfMember.seen_message_id is undefined', breaking message deletion for that user (e.g. deleting a message in a channel the user never opened. Add optional chaining on seen_message_id so the unread counter is simpl
Original PR description
The mail.message/delete bus handler accessed selfMember?.seen_message_id.id with optional chaining only on selfMember, not on seen_message_id. When a member has never seen any message in a channel (seen_message_id is False), accessing .id threw 'TypeError: can't access property id, selfMember.seen_message_id is undefined', breaking message deletion for that user (e.g. deleting a message in a channel the user never opened. Add optional chaining on seen_message_id so the unread counter is simply not decremented when no message has been seen yet. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277417
The current implementation of the Peppol XML export incorrectly populates the <cac:InvoicePeriod> nodes with internal deferred entry dates. These dates are intended for the vendor's revenue recognition process, and the customer has nothing to do with these dates. This commit ensures that: - deferred entries are never created when importing vendor bills. - <cac:InvoicePeriod> is no longer exported in invoices (for now). task-6014315 --- I confirm I have signed the CLA and read the
Original PR description
The current implementation of the Peppol XML export incorrectly populates the <cac:InvoicePeriod> nodes with internal deferred entry dates. These dates are intended for the vendor's revenue recognition process, and the customer has nothing to do with these dates. This commit ensures that: - deferred entries are never created when importing vendor bills. - <cac:InvoicePeriod> is no longer exported in invoices (for now). task-6014315 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269491 Forward-Port-Of: odoo/odoo#265796
**Current behavior before PR:** Steps to reproduce the issue: - Go to a To-Do. - Insert a heading, write something. - Insert a table of content. - Make sure that editable is not scrollable. Clicking on heading label inside toc leads to traceback. The issue occurs after merging commit [1]. When editable is not scrollable then `closestScrollableY` returns null which leads to traceback. **Desired behavior after PR:** If editable is not scrollable then clicking on heading inside to
Original PR description
**Current behavior before PR:** Steps to reproduce the issue: - Go to a To-Do. - Insert a heading, write something. - Insert a table of content. - Make sure that editable is not scrollable. Clicking on heading label inside toc leads to traceback. The issue occurs after merging commit [1]. When editable is not scrollable then `closestScrollableY` returns null which leads to traceback. **Desired behavior after PR:** If editable is not scrollable then clicking on heading inside toc should hightlight the corresponding heading without traceback. [1]: https://github.com/odoo/odoo/commit/f5cf8565e7d09edd3a29fd95537381fb70d75785 task-6405894 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Steps to reproduce --- 1. Confirm a purchase order and receive it a few days later. 2. Open Purchase, Reporting, Purchase and add the "Effective Days To Arrival" measure. Observed: the value is counted from the line's scheduled date instead of the order date, and can even be negative when the scheduled date precedes the confirmation date. Issue --- The metric is meant to be the effective lead time, the number of days between the order confirmation and the actual receipt, falling back
Original PR description
Steps to reproduce --- 1. Confirm a purchase order and receive it a few days later. 2. Open Purchase, Reporting, Purchase and add the "Effective Days To Arrival" measure. Observed: the value is…
Steps to reproduce --- 1. Confirm a purchase order and receive it a few days later. 2. Open Purchase, Reporting, Purchase and add the "Effective Days To Arrival" measure. Observed: the value is counted from the line's scheduled date instead of the order date, and can even be negative when the scheduled date precedes the confirmation date. Issue --- The metric is meant to be the effective lead time, the number of days between the order confirmation and the actual receipt, falling back to the planned "days to receive" when nothing has been received yet according to [task](https://www.odoo.com/odoo/project/809/tasks/3691573). The query instead computes age(date_planned, COALESCE(date_done, date_order)), so once a receipt exists it returns date_planned - date_done (the gap between the scheduled date and the receipt) rather than date_done - date_order. https://github.com/odoo/odoo/blob/c06be48ce7277a667719fd756e0a1f63e91cda27/addons/purchase_stock/report/purchase_report.py#L20-L28 opw-6226523 Forward-Port-Of: odoo/odoo#268843
While preloading chart template data, if an account already exists in the db but has `active=False`, it is not found because archived records are excluded from the [`_search`]. As a result, the load attempts to create a new account. During creation, the Python constraint [`_ensure_code_is_unique`] searches with `active_test=False`, finds the archived account with the same code, and raises a ValidationError: ```py File "/home/odoo/src/odoo/saas-19.2/addons/account/models/chart_template.py"
Original PR description
While preloading chart template data, if an account already exists in the db but has `active=False`, it is not found because archived records are excluded from the [`_search`]. As a result, the load…
While preloading chart template data, if an account already exists in the db but has `active=False`, it is not found because archived records are excluded from the [`_search`].
As a result, the load attempts to create a new account. During creation, the Python constraint [`_ensure_code_is_unique`] searches with `active_test=False`, finds the archived account with the same code, and raises a ValidationError:
```py
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/chart_template.py", line 735, in _load_data
'noupdate': True,
^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/models.py", line 4519, in _load_records
records = self._load_records_create([data['values'] for data in to_create])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/models.py", line 4426, in _load_records_create
records = self.create(vals_list)
^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/odoo/orm/decorators.py", line 363, in create
return method(self, vals_list)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_account.py", line 1057, in create
records._ensure_code_is_unique()
File "/home/odoo/src/odoo/saas-19.2/addons/account/models/account_account.py", line 1135, in _ensure_code_is_unique
raise ValidationError(
odoo.exceptions.ValidationError: Account codes must be unique. You can't create accounts with these duplicate codes: 1.1.1.02.003, 1.1.1.02.004
```
Steps to reproduce:
- In a `v19.0` database, install `l10n_ar` with demo data.
- The accounts with codes `1.1.1.02.003` and `1.1.1.02.004` are created by [`_create_outstanding_accounts`].
- Archive those accounts and upgrade to `v19.2`.
- During the upgrade, [`_get_latam_check_outstanding_account_account`] attempts to create new accounts with the same codes, triggering the duplicate code validation.
tbg-2840
[`_search`]: https://github.com/odoo/odoo/blob/619d8ce9f31349ed06b11ead4988a4a1d9ca6bdd/addons/account/models/chart_template.py#L438
[`_ensure_code_is_unique`]: https://github.com/odoo/odoo/blob/19.0/addons/account/models/account_account.py#L1133
[`_create_outstanding_accounts`]: https://github.com/odoo/odoo/blob/06a46b083fde5688d71c5602553316c9e7160b6a/addons/account/models/chart_template.py#L925C9-L949
[`_get_latam_check_outstanding_account_account`]: https://github.com/odoo/odoo/blob/saas-19.2/addons/l10n_latam_check/models/account_chart_template.py#L60-L76
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prSteps to reproduce: - set the "Product Price" decimal precision to 3 - create two products with a price of 42.609 and a 15% excluded tax - create an automatic promotion granting a 20.4% discount on these specific products - in the PoS, sell both products: the reward line shows -20.00 and the order total 78.00 - pay 100.00 in cash and validate The synced order total becomes 78.01 while the customer paid 78.00. The discount reward line is created in the frontend with an unrounded price
Original PR description
Steps to reproduce: - set the "Product Price" decimal precision to 3 - create two products with a price of 42.609 and a 15% excluded tax - create an automatic promotion granting a 20.4% discount on…
Steps to reproduce: - set the "Product Price" decimal precision to 3 - create two products with a price of 42.609 and a 15% excluded tax - create an automatic promotion granting a 20.4% discount on these specific products - in the PoS, sell both products: the reward line shows -20.00 and the order total 78.00 - pay 100.00 in cash and validate The synced order total becomes 78.01 while the customer paid 78.00. The discount reward line is created in the frontend with an unrounded price unit: -(85.22 * 20.4%) = -17.384879999999995. The frontend computes taxes on the price unit rounded to the "Product Price" precision (see `get_unit_price()`): -17.385 -> -17.39 - 2.61 = -20.00 tax included, hence the 78.00 total displayed and paid. However, the raw value is what is sent to the server and stored as-is (`pos.order.line.price_unit` is declared with `digits=0`). Whenever the backend recomputes the amounts from the lines - e.g. when a cash change makes `_process_payment_lines()` call `_compute_prices()` - the taxes are computed on the raw value: -17.38 - 2.61 = -19.99, and the order total drifts to 78.01. Round the reward line price unit to the "Product Price" precision when creating it, as `_getRewardLineValuesProduct()` already does, so the stored value is the one the frontend computed its totals with. opw-6322031 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275961