Daily updates from Odoo
Friday, July 17, 2026
268 changes
20 changes
Enhancements to existing features
The journal creation wizard in Accounting now supports navigation with keyboard arrow keys. This makes the setup flow easier and faster to use, especially for users who prefer keyboard-based navigation or need improved accessibility.
Original PR description
This commit aims to allow for navigation through the journal create wizard via keybaord arrows. Related Odoofin PR: https://github.com/odoo/odoofin/pull/502 task-5796200 Forward-Port-Of: odoo/enterprise#105541
Bank reconciliation now highlights draft statement lines in blue, making them quicker to identify during review. This helps users distinguish unfinished items at a glance and reduces the chance of overlooking draft entries.
Original PR description
This commit will put the text in blue when the statement line is in draft to be able to see quickly which lines are in draft. task-6327308 Forward-Port-Of: odoo/enterprise#124418 Forward-Port-Of: odoo/enterprise#121775
The payment form no longer shows the Payment Identifier field because it does not provide useful information for users. This keeps the payment screen cleaner and helps users focus on relevant payment details.
Original PR description
- The Payment Identifier field has been permanently hidden from the payment form view, as it does not provide actionable or useful information to the end user. community pr - https://github.com/odoo/odoo/pull/266072 task- 6237870 Forward-Port-Of: odoo/enterprise#118183
Resolved issues and error corrections
This fixes an issue that could interrupt the signing workflow when the system is running in debug mode. The signing page now reliably selects the correct signing field even when extra hidden page markers are present, reducing errors for users preparing or completing documents.
Original PR description
Use lastElementChild when retrieving the sign item from the target element. In debug mode, inherited templates may introduce HTML comments into the DOM. Since lastChild return a comment node, accessing classList on the returned node raises an error. Using lastElementChild ensures that the last HTML element is always retrieved, regardless of comment nodes in the DOM. Forward-Port-Of: odoo/enterprise#123991 Forward-Port-Of: odoo/enterprise#122106
Payroll frequency labels now appear in the user's selected language across core payroll and multiple country-specific payroll modules. This improves clarity for HR users working in localized interfaces, such as French, by showing salary schedule options in the expected language.
Original PR description
Issue: ---------------------------------------- The values of the field `schedule_pay` aren't translated. Steps to reproduce: ---------------------------------------- - Switch the language to French - Open an employee form, "Paie" tab - The selection in the "Salaire" tab is not translated to French Cause: ---------------------------------------- When the selection values were moved to a method in 7a123d71925b25f26ba0a8abff0c4a159147bdd0. The strings were not declared as translatable. opw-6359395 Forward-Port-Of: odoo/enterprise#124504 Forward-Port-Of: odoo/enterprise#123718
Managers will no longer see the Print option twice in the Planning Gantt view. This makes the interface cleaner and reduces confusion when using planning actions.
Original PR description
Issue: - Managers see the "Print" action twice in the Gantt view: once as a standalone button and once in the Actions dropdown. Cause: - The standalone Print button is guarded on `!this.isManager`, but `isManager` lives on the model. The expression is therefore always truthy, so the button always renders. Fix: - Use `!this.model.isManager` instead. task-6364971 Forward-Port-Of: odoo/enterprise#123442
This fixes an error that could occur when validating certain stock operations involving kits in the Kenyan OSCU stock integration. Businesses using the Kenyan localization can complete affected inventory workflows more reliably without unexpected interruptions.
Original PR description
**CAUSE** super()._action_done() delete a record from self, and return a new recordset of stock.moves. We filter the old recordset instead of filtering the new one, leading to an MissingError traceback. **STEP TO REPRODUCE** On a fresh db, install: `l10n_ke,l10n_ke_edi_oscu,l10n_ke_edi_oscu_mrp,l10n_ke_edi_oscu_pos,l10n_ke_edi_oscu_stock,l10n_ke_edi_tremol,l10n_ke_hr_payroll,l10n_ke_hr_payroll_account,l10n_ke_reports` and run `TestKitPicking.test_add_sml_with_kit_to_confirmed_picking`. runbot-241262 Forward-Port-Of: odoo/enterprise#124663
This update prevents the Avalara tax integration from keeping a proxy connection active when a database is neutralized. It helps avoid unintended external communication from copied or non-production environments while keeping normal business data unchanged.
Original PR description
backport of https://github.com/odoo/enterprise/pull/122024 no-task Forward-Port-Of: odoo/enterprise#124681
This fixes an internal test setup issue in the Timesheet Grid module so automated checks run reliably. It helps prevent false test failures during development and release validation, with no direct change to end-user behavior.
Original PR description
Before this commit, the component was mounted in the `beforeEach` block before its `onRpc` mocks were registered. This caused the initial data fetch to fail because the mocks were not yet available during initialization. This commit fixes the test by moving the component mount (`doAction`) inside the test block, strictly after the mocks are defined. Forward-Port-Of: odoo/enterprise#123683
Coding editor activity in the timesheet assistant now uses the correct event category, so it displays with the intended code-related icon. This avoids confusing or missing visual cues when users review assistant events.
Original PR description
In the assistant, events from a coding editor use the "code" event type, which doesn't exist. This PR changes it to the correct value, which is "development". Task-6392585 Forward-Port-Of: odoo/enterprise#124502
Payroll schedule options in the Kuwait payroll localization now appear in the user's selected language. This fixes a missed localization issue so business users see consistent translated labels in payroll configuration.
Original PR description
The values returned by `_get_selection_schedule_pay()` wern't translated. This localization was missed by this forward port: 961bedf91d754cce5d3cdecabe1e504770902d05 opw-6359395
The Balance Sheet report now displays numeric column headers with the correct spacing. This small visual fix improves readability and consistency in financial reports without changing any report data or calculations.
Original PR description
Due to `padding: 0.25rem 0.75rem !important;` To reproduce: - Open Balance Sheet - Check the Balance column numeric-header
Opening the template picker from an audit report article without a parent no longer causes an error. Instead, users see a clear message when no article template is available, keeping the audit report editing flow usable.
Original PR description
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to…
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to reproduce: 1. Install `accountant_knowledge`. 2. Create and open a new audit report. 3. Delete all content from article (the root). 4. Click the "Load a Template" button in the helper. => Crash with `AssertionError: Invalid falsy real id.` The issue occurs because the method responsible for loading the annex to display (see: `get_suggested_templates`) expects at least one record in the recordset. When the article has no parent, the recordset is empty, causing the method to fail. Before attempting to load a template, we will check whether the article has a parent article. If no parent exists, no template will be provided to the template picker. In that case, the picker will display a helper message indicating that no article template is available to load. Task [link](https://www.odoo.com/odoo/project.task/6333859) Task-6333859 Forward-Port-Of: odoo/enterprise#121807
This fix ensures contract updates use the right template, preferring the current contract version when available or falling back to the template from the salary offer. This helps HR teams avoid incorrect contract documents during offer and contract update workflows.
Original PR description
contract update template should come from current version if any or from the offer's contract template. Task-6094733 Forward-Port-Of: odoo/enterprise#124638 Forward-Port-Of: odoo/enterprise#123450
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
Original PR description
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
The more() action helper caches the More Actions object and only refreshes its inner actions list, leaving disabledCondition unchanged. As a result, if the dropdown is created while disabled, it remains disabled even after the composer is re-enabled. Individual actions (e.g., Attach files) don't exhibit this issue because they use a dynamic callback `(({ owner }) => owner.areAllActionsDisabled)` that is evaluated when needed. task-6393956 --- I confirm I have signed the CLA and read th
Original PR description
The more() action helper caches the More Actions object and only refreshes its inner actions list, leaving disabledCondition unchanged. As a result, if the dropdown is created while disabled, it remains disabled even after the composer is re-enabled.
Individual actions (e.g., Attach files) don't exhibit this issue because they use a dynamic callback `(({ owner }) => owner.areAllActionsDisabled)` that is evaluated when needed.
task-6393956
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276870Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. - Create a sale order containing product and confirm it. Issue: --- - An access error is raised during order confirmation. Root cause: --- - In [commit], to handle inventory tracking, the `qty_available` field was moved to `product.product`. Unlike before, this value is increased or
Original PR description
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. -…
Steps to produce: --- - Install the `sales` module. - Create a product and `enable track inventory.` - Log in as user with only view access rights in products and also have the sales access rights. - Create a sale order containing product and confirm it. Issue: --- - An access error is raised during order confirmation. Root cause: --- - In [commit], to handle inventory tracking, the `qty_available` field was moved to `product.product`. Unlike before, this value is increased or decreased depending on the operation performed. - As a consequence, creating or updating a sale order triggers a write to this `qty_available` field on the related product. This write happens under the current user's permissions, so users who only have read access to products (but can create/edit sale orders) hit an `AccessError`, since they lack write access on `product.product`. Solution: --- - Use `sudo()` when accessing the required product quantity information to ensure the operation can be completed without requiring additional product access rights. The same issue also occurs when confirming a purchase order. [commit]: https://github.com/odoo/odoo/commit/ca96992919b11105da44238c3e522f8eec4a740b opw-6290608 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276563 Forward-Port-Of: odoo/odoo#270067
Version: --------- - 19.0+ Steps to Reproduce: ----------------------- 1. Install sale_management, purchase, stock modules. 2. Create a storable product with Tracking: By Lot, 3. Create two Purchase Orders, each for 10 units. Receive PO-1 → 10 units with tagged as lot-1 Receive PO-2 → 10 units with tagged as lot-2 4. Create two Sale Orders: SO-1 → deliver 2 units from lot-1 (validate) SO-2 → deliver 4 units from lot-2 (validate) 5. Open Inventory > Reporting > Stock,
Original PR description
Version: --------- - 19.0+ Steps to Reproduce: ----------------------- 1. Install sale_management, purchase, stock modules. 2. Create a storable product with Tracking: By Lot, 3. Create two Purchase…
Version:
---------
- 19.0+
Steps to Reproduce:
-----------------------
1. Install sale_management, purchase, stock modules.
2. Create a storable product with Tracking: By Lot,
3. Create two Purchase Orders, each for 10 units.
Receive PO-1 → 10 units with tagged as lot-1
Receive PO-2 → 10 units with tagged as lot-2
4. Create two Sale Orders:
SO-1 → deliver 2 units from lot-1 (validate)
SO-2 → deliver 4 units from lot-2 (validate)
5. Open Inventory > Reporting > Stock,
click "Total Value", then check the "Remaining Quantity" column
Issue:
-------
Observed : remaining_qty = 10 for lot-2 receipt, 4 for lot-1 receipt
Expected : remaining_qty = 8 for lot-1 receipt (10−2), 6 for lot-2 receipt (10−4)
Cause:
--------
When the "Remaining Quantity" column is computed, the following call
chain executes:
stock.move._compute_remaining_qty()
→ calls product.product._get_remaining_moves()
→ calls product._run_fifo_get_stack() ← HERE is the problem
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L372
`_get_remaining_moves` calls `_run_fifo_get_stack()` with NO lot
argument. Inside `_run_fifo_get_stack`, because no lot is given, it
computes the stack size from the TOTAL product qty across all lots:
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L583
fifo_stack_size = 14 (10 received lot-1 + 10 received lot-2
− 2 delivered lot-1 − 4 delivered lot-2)
It then builds a domain to find incoming moves with NO lot filter:
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L607
https://github.com/odoo/odoo/blob/f4d079cc5a9c47672cf1a6747bb073e8e74f7350/addons/stock_account/models/product.py#L614-L618
```Domain: [('is_in', '=', True), ('product_id', '=', X)]
↳ returns both receipts ordered:
[lot-2 receipt (10 qty), lot-1 receipt (10 qty)]
then walks this list consuming `fifo_stack_size = 14`:
So it take: [move_lot1_receipt(10)] First Lot
remaining_qty_on_first = min(10, 14) = 10
after consuming fifo_stack_size → 14−10=4 left → move_lot1 gets 4
```
So back in `_get_remaining_moves`:
qty_by_move = {
lot-2 receipt → 10, ← wrong (should be 6)
lot-1 receipt → 4, ← wrong (should be 8)
}
- The root cause: `_run_fifo_get_stack` is designed for products that
have one shared FIFO stack. For lot-valuated products, each lot is an
independent inventory layer. Running a single combined stack mixes both
lots together, so the deductions (2 from lot-1, 4 from lot-2) are not
attributed to the correct receipt moves — the algorithm just consumes
from the oldest receipts first with no awareness of which lot was
actually delivered.
Fix:
-----
`_run_fifo_get_stack` already accepts a `lot=` argument that:
- sets `fifo_stack_size = lot.product_qty` (correct per-lot qty)
- adds `('move_line_ids.lot_id', 'in', lot.id)` to the domain
so only the receipts that touched that specific lot are returned
The only missing piece was calling it per lot instead of once globally.
- With the fix, the stack for each lot is built correctly:
lot-1: fifo_stack_size = 8 → lot-1 receipt remaining_qty = 8 ✓
lot-2: fifo_stack_size = 6 → lot-2 receipt remaining_qty = 6 ✓
---
opw-6311341
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276690
Forward-Port-Of: odoo/odoo#272411Code cleanup and technical improvements
This update removes duplicate holiday-planning code because the same behavior is already provided by a related scheduling module. This reduces maintenance risk without changing how users manage planning or leave.
Original PR description
After commit e0b126c, `_handle_flexible_leave_interval` is already implemented in `hr_holidays_gantt` and covers the same case previously handled in `planning_holidays`. https://github.com/odoo/enterprise/blob/7ed917c1de11e29257a726efd4dafe403b9efba1/hr_holidays_gantt/models/resource_calendar.py#L17-L20 Since `planning_holidays` depends on `hr_holidays_gantt`, the duplicate implementation in `planning_holidays` is no longer necessary. **opw-6243778** Forward-Port-Of: odoo/enterprise#124477 Forward-Port-Of: odoo/enterprise#119229
Miscellaneous changes
Forward-Port-Of: odoo/enterprise#124221
Original PR description
Forward-Port-Of: odoo/enterprise#124221
14 changes
Resolved issues and error corrections
This fixes partner form views for Ecuador and Mexico localization features after a prior layout change caused tax ID-related fields to appear incorrectly or fail to load. Businesses using these localizations should see partner identification information display reliably again.
Original PR description
In previous refactor [1], we wrapped the "vat" field under a "vat_div" div. This broke some xpath depending on the "vat". This commit repair those views. Note: 19.4+ made a second change in the div around the vat and identifiers, the views should already be fixed in upper versions. [1]: https://github.com/odoo/odoo/commit/825e7c803a4effd601fb247c660416f1cc6d26d7 task-6382108
This fixes an error that could appear when payroll users in debug mode filtered the payslip list for Off-Cycle items. The payroll list now handles payslips without a linked pay run correctly, improving reliability for that view.
Original PR description
Before this commit, when the user is in debug mode, goes to payslips list view and applies `Off-Cycle` filter, a traceback is occured saying `Invalid props for component 'PayslipActionHelper': 'payrunId' is not a number`.
The reason is because we search the id of the payrun by parsing the domain applied to fetch payslips displayed inside the list view but `Off-Cycle` filter will apply the following domain: `[('payslip_run_id', '=', False)]` and so False is given to PayslipActionHelper but it is not a number.
This commit makes sure to set undefined to payrunId prop of PayslipActionHelper when `payrun_id` found is falsy.
runbot-error-241053The Timesheets menu icon now appears whenever a user has an active employee record in any selected company, not just the current company. This prevents users from missing access to timesheet entry when they can legitimately create timesheets through another selected company.
Original PR description
Steps to reproduce: - install Timesheets - create an employee for a user in company B - switch to company A (where the user has no employee) - the systray icon is hidden even though the user can…
Steps to reproduce: - install Timesheets - create an employee for a user in company B - switch to company A (where the user has no employee) - the systray icon is hidden even though the user can create timesheets in company B via the company selector Current behavior: the systray only checks the current company for a valid employee, ignoring other selected companies. Expected behavior: the systray should be visible whenever the user can create timesheets i.e. when they have an active employee in any of the selected companies. Issue: the check used a stored boolean on `res.partner` that has no company scope and becomes stale when an employee is archived (the stored dependency does not re-fire). the timesheet creation logic checks all selected companies for active employees, but the systray did not mirror that. Fix: use `employee_ids` a `One2many` that checks all selected companies and excludes archived employees, matching the timesheet creation logic exactly. task-6330539 Forward-Port-Of: odoo/enterprise#121730
Users can now open the template picker in audit report articles even when the article has no parent. Instead of showing an error, the picker will simply indicate that no article template is available to load.
Original PR description
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to…
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to reproduce: 1. Install `accountant_knowledge`. 2. Create and open a new audit report. 3. Delete all content from article (the root). 4. Click the "Load a Template" button in the helper. => Crash with `AssertionError: Invalid falsy real id.` The issue occurs because the method responsible for loading the annex to display (see: `get_suggested_templates`) expects at least one record in the recordset. When the article has no parent, the recordset is empty, causing the method to fail. Before attempting to load a template, we will check whether the article has a parent article. If no parent exists, no template will be provided to the template picker. In that case, the picker will display a helper message indicating that no article template is available to load. Task [link](https://www.odoo.com/odoo/project.task/6333859) Task-6333859 Forward-Port-Of: odoo/enterprise#121807
This fix ensures salary contract updates use the right template, preferring the current contract version when available and otherwise using the template from the original offer. This helps HR teams generate accurate contract update documents and reduces the risk of using an incorrect template.
Original PR description
contract update template should come from current version if any or from the offer's contract template. Task-6094733 Forward-Port-Of: odoo/enterprise#124638 Forward-Port-Of: odoo/enterprise#123450
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
Original PR description
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
In previous refactor [1], we wrapped the "vat" field under a "vat_div" div. This broke some xpath depending on the "vat". This commit repair those views. [1]: https://github.com/odoo/odoo/commit/825e7c803a4effd601fb247c660416f1cc6d26d7 task-6382108
Original PR description
In previous refactor [1], we wrapped the "vat" field under a "vat_div" div. This broke some xpath depending on the "vat". This commit repair those views. [1]: https://github.com/odoo/odoo/commit/825e7c803a4effd601fb247c660416f1cc6d26d7 task-6382108
A previous commit updated the `commonExtraData` from `GeneratePrinterData` in order to update the style from the pdis tickets. However, since `commonExtraData` is used for both receipt and pdis tickets, the change caused a bug for receipts tickets. This commit reverts the `commonExtraData` and update the code in order to still have the correct data dunble for pdis tickets. --- FIX Task: https://www.odoo.com/odoo/project/1737/tasks/6133403 Forward-Port-Of: odoo/odoo#276909
Original PR description
A previous commit updated the `commonExtraData` from `GeneratePrinterData` in order to update the style from the pdis tickets. However, since `commonExtraData` is used for both receipt and pdis tickets, the change caused a bug for receipts tickets. This commit reverts the `commonExtraData` and update the code in order to still have the correct data dunble for pdis tickets. --- FIX Task: https://www.odoo.com/odoo/project/1737/tasks/6133403 Forward-Port-Of: odoo/odoo#276909
### Steps to reproduce: - Open the Todo app - In the editor, insert the following content: `<p>a</p><div class="oe_unbreakable"><br></div><p>b</p>` - Double-click the empty unbreakable node - Open the color picker and hover over a color - Toolbar and color picker get closed ### Root cause: - Hovering a color in an empty unbreakable node replaces the `<br>` with a `<font data-oe-zws-empty-inline>` containing a ZWS (`\u200b`). This triggers a selectionchange where `isToolbarVisible()`
Original PR description
### Steps to reproduce: - Open the Todo app - In the editor, insert the following content: `<p>a</p><div class="oe_unbreakable"><br></div><p>b</p>` - Double-click the empty unbreakable node - Open…
### Steps to reproduce: - Open the Todo app - In the editor, insert the following content: `<p>a</p><div class="oe_unbreakable"><br></div><p>b</p>` - Double-click the empty unbreakable node - Open the color picker and hover over a color - Toolbar and color picker get closed ### Root cause: - Hovering a color in an empty unbreakable node replaces the `<br>` with a `<font data-oe-zws-empty-inline>` containing a ZWS (`\u200b`). This triggers a selectionchange where `isToolbarVisible()` finds no `<br>` and no visible text, returns false, and closes the toolbar — which reverts the preview, reopens the toolbar, and causes a flicker loop. ### Solution: - Instead of calling `fillEmpty()` (which inserts a ZWS placeholder) on empty blocks containing `<br>` preserve the `<br>` element by appending it directly inside the `<font>` tag which keeps toolbar open. task-6312933 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276776 Forward-Port-Of: odoo/odoo#271507
### Issue: The 'Schedule an appointment' and 'Next Events' CTA buttons were not updated even when their conditions were satisfied. ### Steps to reproduce: - Install only Website. - In the configurator, choose 'Schedule Appointments' as the main objective. - Complete the setup and create the website. - The CTA button remains 'Contact Us' instead of 'Schedule an appointment'. ### Reason: The `get_cta_data()` method is overridden in specific modules to update the CTA button base
Original PR description
### Issue: The 'Schedule an appointment' and 'Next Events' CTA buttons were not updated even when their conditions were satisfied. ### Steps to reproduce: - Install only Website. - In the configurator, choose 'Schedule Appointments' as the main objective. - Complete the setup and create the website. - The CTA button remains 'Contact Us' instead of 'Schedule an appointment'. ### Reason: The `get_cta_data()` method is overridden in specific modules to update the CTA button based on conditions. However, it is called before those modules are installed, so the overridden logic is never executed. ### Fix: Ensure that `get_cta_data()` is called and the CTA button is updated after the required modules are installed. task-[6383681](https://www.odoo.com/odoo/project/974/tasks/6383681) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261781
[FIX] fleet: fix vendor bill vehicle association bug Bug reprod: Go to 19.2 or above 1 - Go to vendor bills. 2 - Create an invoice line add vehicle. 3 - Click to vehicle via the link 4 - You will see fleet model and try to press to some smart buttons, traceback will occur (Odometer, Services...) Bug cause: 1 - When we press to smart buttons for Odometer or Services we are going to return_action_to_open function. 2 - In this function corresponding action's
Original PR description
[FIX] fleet: fix vendor bill vehicle association bug Bug reprod: Go to 19.2 or above 1 - Go to vendor bills. 2 - Create an invoice line add vehicle. 3 - Click to vehicle via the link 4 - You will see…
[FIX] fleet: fix vendor bill vehicle association bug
Bug reprod: Go to 19.2 or above
1 - Go to vendor bills.
2 - Create an invoice line add vehicle.
3 - Click to vehicle via the link
4 - You will see fleet model and try to press to some smart buttons, traceback will occur (Odometer, Services...)
Bug cause:
1 - When we press to smart buttons for Odometer or Services we are going to return_action_to_open function.
2 - In this function corresponding action's xml id is calculated and we are calling that action and that will load some view.
3 - self.env.context is passed directly as a context
4 - In the view_move_form (That include invoice lines, account_id and vehicle_id fields), account_id has a context list_view_ref="account.view_account_list_from_entry"
5 - This context is passed in self.env.context and that's why it tries to load this list_view when we press to odometer,service smart buttons, which shouldn't be the case.
6 - In 19.1 this context is not in self.env.context because >=19.2 m2o_cell_with_extra_m2o_fields is used for account_id and account_id and vehicle_id fields are combined in the single cell.
7 - That's why the context of account_id is passed to the vehicle page as well.
Bug solution:
1 - In the return_action_to_open function I'm dropping the list_view_ref context and we can load the correct related views about odometer or service or other ones.
task - 6385611
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#276167Before this commit, a crash could occur in kanban views but it required a very precise timing. If 2 renderings of the kanban renderer occurred at the same time, one coming from a group that has just been opened, and one coming from a new groupby being applied in the search view, we tried to scroll to the opened group to ensure that it is in the viewport, but we couldn't find it. Task~6391414 Forward-Port-Of: odoo/odoo#276750 Forward-Port-Of: odoo/odoo#276488
Original PR description
Before this commit, a crash could occur in kanban views but it required a very precise timing. If 2 renderings of the kanban renderer occurred at the same time, one coming from a group that has just been opened, and one coming from a new groupby being applied in the search view, we tried to scroll to the opened group to ensure that it is in the viewport, but we couldn't find it. Task~6391414 Forward-Port-Of: odoo/odoo#276750 Forward-Port-Of: odoo/odoo#276488
Code cleanup and technical improvements
This change removes duplicate internal logic for handling flexible leave intervals in Planning Holidays. The shared Holidays Gantt component already provides the same behavior, so this reduces maintenance risk without changing the user experience.
Original PR description
After commit e0b126c, `_handle_flexible_leave_interval` is already implemented in `hr_holidays_gantt` and covers the same case previously handled in `planning_holidays`. https://github.com/odoo/enterprise/blob/7ed917c1de11e29257a726efd4dafe403b9efba1/hr_holidays_gantt/models/resource_calendar.py#L17-L20 Since `planning_holidays` depends on `hr_holidays_gantt`, the duplicate implementation in `planning_holidays` is no longer necessary. **opw-6243778** Forward-Port-Of: odoo/enterprise#124477 Forward-Port-Of: odoo/enterprise#119229
Miscellaneous changes
Forward-Port-Of: odoo/enterprise#124221
Original PR description
Forward-Port-Of: odoo/enterprise#124221
34 changes
Enhancements to existing features
The journal creation wizard can now be navigated using keyboard arrow keys. This makes setup faster and more accessible for users who prefer or rely on keyboard navigation.
Original PR description
This commit aims to allow for navigation through the journal create wizard via keybaord arrows. Related Odoofin PR: https://github.com/odoo/odoofin/pull/502 task-5796200 Forward-Port-Of: odoo/enterprise#105541
It is mandatory in BE to add a legal note on the invoice when using a "Co-Contractant" tax task-5905176 Forward-Port-Of: odoo/odoo#275388 Forward-Port-Of: odoo/odoo#251797
Original PR description
It is mandatory in BE to add a legal note on the invoice when using a "Co-Contractant" tax task-5905176 Forward-Port-Of: odoo/odoo#275388 Forward-Port-Of: odoo/odoo#251797
In multicompany, it can happen that one company (let's call it company Origin) cannot handle a payment method in their country, so it uses another company's (company MoneyHandler), even if it's in another country. The accounting flows must be then adjusted: - In company Origin, the invoice must be matched by a clearing entry - In company MoneyHandler, payment must match its move (if it exists) with a clearing entry. The payment move doesn't exist if `account_accountant` is installed but no O
Original PR description
In multicompany, it can happen that one company (let's call it company Origin) cannot handle a payment method in their country, so it uses another company's (company MoneyHandler), even if it's in…
In multicompany, it can happen that one company (let's call it company Origin) cannot handle a payment method in their country, so it uses another company's (company MoneyHandler), even if it's in another country. The accounting flows must be then adjusted: - In company Origin, the invoice must be matched by a clearing entry - In company MoneyHandler, payment must match its move (if it exists) with a clearing entry. The payment move doesn't exist if `account_accountant` is installed but no Outstanding account is configured on the payment method line. Same but opposite thing must happen for credit notes in company Origin that match a reimbursement in company MoneyHandler. Cancellation of a payment must be reflected on the entries: deleting when feasible, reversing when not (unless a lock date/hash is present, which would block the cancellation) _(To do: testing/review, credit note, cancellation/reversal of the payment)_ Task [link](https://www.odoo.com/odoo/project.task/6037525) task-6037525 Forward-Port-Of: odoo/odoo#259197
Resolved issues and error corrections
The Timesheets systray icon now appears for users who have an active employee record in any selected company, not just the currently active company. This prevents eligible users in multi-company setups from losing quick access to timesheet entry.
Original PR description
Steps to reproduce: - install Timesheets - create an employee for a user in company B - switch to company A (where the user has no employee) - the systray icon is hidden even though the user can…
Steps to reproduce: - install Timesheets - create an employee for a user in company B - switch to company A (where the user has no employee) - the systray icon is hidden even though the user can create timesheets in company B via the company selector Current behavior: the systray only checks the current company for a valid employee, ignoring other selected companies. Expected behavior: the systray should be visible whenever the user can create timesheets i.e. when they have an active employee in any of the selected companies. Issue: the check used a stored boolean on `res.partner` that has no company scope and becomes stale when an employee is archived (the stored dependency does not re-fire). the timesheet creation logic checks all selected companies for active employees, but the systray did not mirror that. Fix: use `employee_ids` a `One2many` that checks all selected companies and excludes archived employees, matching the timesheet creation logic exactly. task-6330539
The test setup for Odoo Cloud Notifications now matches real behavior by registering devices only for internal users. This reduces false test coverage around users who should not receive these notifications and helps keep notification reliability checks accurate.
Original PR description
Only devices of internal users are registered in order to send them Odoo Cloud Notifications (OCN). However, the test setup registers devices for non-internal users as well. This commit ensures devices are only registered for internal users. Forward-Port-Of: odoo/enterprise#119956
Creating an employee contract from a template now also copies the template's analytic distribution. This prevents missing cost allocation information on new contracts and reduces manual correction for payroll accounting.
Original PR description
Problem: When creating a new contract from a template, the analytic distribution field is not copied from the template to the contract. Steps to reproduce: 1. Create a contract template with an analytic distribution. 2. Create a new contract for an employee from the template. 3. Check the analytic distribution field on the new contract. 4. Notice how the analytic distribution field is empty, even though it was set on the template. Cause: The field is not included in the list of whitelisted fields to copy from the template. https://github.com/odoo/odoo/blob/0133e46f89df7dce8c39d2bacd29579d57a83fad/addons/hr/models/hr_version.py#L443 opw-6370781 Forward-Port-Of: odoo/enterprise#123955
Uruguayan electronic invoices now avoid adding extra blank lines between configured addenda text and invoice terms. This helps prevent addenda content from being pushed onto a separate page when it should fit on the same page.
Original PR description
## Context When generating a CFE (Comprobante Fiscal Electrónico) that contains both a configured addenda (e.g. bank account details stored in `l10n_uy_edi_addenda_ids`) and terms & conditions from…
## Context
When generating a CFE (Comprobante Fiscal Electrónico) that contains both a configured addenda (e.g. bank account details stored in `l10n_uy_edi_addenda_ids`) and terms & conditions from the invoice's `narration` field, the resulting addenda string could end up with unnecessary blank lines between the two sections, causing the addenda to be rendered on a separate page even when the logical content fits within the 6-line threshold.
## Root Cause
`_l10n_uy_edi_get_addenda` joins both parts without stripping whitespace from either of them first, and adds two lines between addendas and terms and conditions:
addenda = addenda + "\n\n" + term_and_conditions if addenda else term_and_conditions
Two sources independently introduce extra newlines around the separator:
1. **Addenda content** — `_get_legends` returns the raw `content` field value of each addenda record. These fields commonly end with a trailing `\n`, so the addenda string already ends with a newline before the `"\n"` separator is concatenated.
2. **`html2plaintext`** — the `narration` field is stored as HTML. When converted to plain text, `html2plaintext` typically wraps paragraph content in leading/trailing newlines.
The combination of the trailing `\n` from the addenda, the explicit `"\n\n"` separator, and the leading/trailing `\n` from `html2plaintext` produces 2–3 consecutive newlines, which `splitlines()` counts as blank lines.
A realistic 4-line addenda + 1-line narration thus produces **7 lines** instead of the expected 5, crossing the 6-line threshold in `_get_report_params` and triggering `adenda=true` — which forces the addenda onto a separate page unnecessarily.
## Steps to Reproduce
1. Configure a `l10n_uy_edi.addenda` record of type `addenda` with multi-line content (4 lines)
2. Create and confirm an invoice with `narration` set to a short single-line term
3. Generate the CFE PDF via Uruware.
4. Observe that the addenda is rendered on a separate page despite the logical content being only 5 lines.
<img width="1042" height="448" alt="image" src="https://github.com/user-attachments/assets/b538211c-5f37-4648-979d-99cd75cf31c2" />
## Fix
Strip leading and trailing whitespace (including newlines) from both parts before joining them. The ternary is also replaced with an explicit `if/else` for clarity:
def _l10n_uy_edi_get_addenda(self):
addenda = self.l10n_uy_edi_document_id._get_legends("addenda", self)
if self.narration:
term_and_conditions = html2plaintext(self.narration).strip()
if addenda:
addenda = addenda.strip() + "\n" + term_and_conditions
else:
addenda = term_and_conditions
return self._l10n_uy_edi_clean_non_ascii_chars(addenda)
This guarantees exactly one `\n` separator between sections regardless of how the content fields were stored or how `html2plaintext` formatted the narration.
The threshold logic in `_get_report_params` is unchanged: addendas that genuinely exceed 6 lines (after wrapping at 140 chars) continue to be printed on a dedicated page.
Result
<img width="1117" height="456" alt="image" src="https://github.com/user-attachments/assets/3a2d942c-8c37-40f6-bc25-470c0bd25b08" />
Forward-Port-Of: odoo/enterprise#119283Long timesheet descriptions in the assistant now expand dynamically instead of being cut off. This makes it easier for users to review and understand detailed time entries without losing important context.
Original PR description
- changed the description field to expand dynamically to display long descriptions in full instead of truncating them in the assistant Task-6348575
Odoo Studio now handles field labels written with non-Latin characters, such as Arabic, without triggering an invalid custom field name error. This lets users rename fields in their preferred language without being blocked by the system.
Original PR description
Steps: - Install web_studio - Add any field (example char field) to any view - Rename it in arabic, example `السَّلَامُ عَلَيْكُمْ` - Error Custom field names cannot contain double underscores Webclient (view_editor_model) escape every non-alphabetic chars, so new label value contains nothing but a space which will be replaced by a _ this new label value will be concatenated to `x_studio_`. Resulting to the string `x_studio__`. A solution should be to prevent changing the technical name if the new label value (escaped) is empty. opw-6311027 Forward-Port-Of: odoo/enterprise#122094 Forward-Port-Of: odoo/enterprise#121343
Users with IoT access but without Point of Sale access can now enable LNA on an IoT box without encountering an access error. This removes an unnecessary permission-related blocker and makes IoT box setup smoother for authorized IoT users.
Original PR description
Before this commit, if a user who has IoT permissions but not POS permissions tries to enable LNA on an IoT box record, they will receive an Access Error. After this commit, a `sudo` is added to the `onchange` handler fixing the issue. task-6392548
Opening the template picker from a root audit report article no longer triggers an error. If no parent article exists, the picker now simply shows that no template is available, allowing users to continue their work without interruption.
Original PR description
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to…
Currently, users encounter a traceback when clicking the "Load a Template" button in the WYSIWYG article helper if the article is linked to an audit report and has no parent article. Steps to reproduce: 1. Install `accountant_knowledge`. 2. Create and open a new audit report. 3. Delete all content from article (the root). 4. Click the "Load a Template" button in the helper. => Crash with `AssertionError: Invalid falsy real id.` The issue occurs because the method responsible for loading the annex to display (see: `get_suggested_templates`) expects at least one record in the recordset. When the article has no parent, the recordset is empty, causing the method to fail. Before attempting to load a template, we will check whether the article has a parent article. If no parent exists, no template will be provided to the template picker. In that case, the picker will display a helper message indicating that no article template is available to load. Task [link](https://www.odoo.com/odoo/project.task/6333859) Task-6333859 Forward-Port-Of: odoo/enterprise#121807
Phone call records now hide related action buttons when no customer or contact is attached, preventing errors from unexpected clicks. Subscription shortcuts were also aligned with the standard customer view so users see consistent behavior across apps.
Original PR description
Same as in [1], we don't show smart buttons when no partner to prevent unexpected errors. Also remove `invisible="subscription_count == 0"` to make it same as smart button on res.partner. [1]: 0fbb730e02de22b196a45455f801e96321a75167
When creating or editing a portal billing address, the Company Name field was pre-filled from `commercial_company_name`. For contacts without a parent company, the commercial partner is the contact itself, so the contact name was shown as the company name. Use the partner's actual parent company name instead, so the field stays empty when no company is linked while still showing the existing parent company when one exists. see: https://github.com/odoo/odoo/commit/18a59cf26f2d9400f76deec483
Original PR description
When creating or editing a portal billing address, the Company Name field was pre-filled from `commercial_company_name`. For contacts without a parent company, the commercial partner is the contact itself, so the contact name was shown as the company name. Use the partner's actual parent company name instead, so the field stays empty when no company is linked while still showing the existing parent company when one exists. see: https://github.com/odoo/odoo/commit/18a59cf26f2d9400f76deec483f6ddab87da0c55 Task-6372638 Forward-Port-Of: odoo/odoo#274972
Problem: When a user attempts to create a new partner, and before saving the partner, they attempt to create a bank account in the same form view, they will be faced with a validation error for missing partner_id on the bank account. Solution: This commit solves this issue by only allowing the user to modify bank accounts for existing partners (with id). task-6373918
Original PR description
Problem: When a user attempts to create a new partner, and before saving the partner, they attempt to create a bank account in the same form view, they will be faced with a validation error for missing partner_id on the bank account. Solution: This commit solves this issue by only allowing the user to modify bank accounts for existing partners (with id). task-6373918
[1] added a bus channel for discuss categories. This can be done with or without any token. When the token is not passed, `verify_limited_field_access_token` is still called and crashes. When no token is provided, we should use category access rights instead and avoir verifying the token (which is `None`). [1]: https://github.com/odoo/odoo/pull/243131 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: ---
Original PR description
[1] added a bus channel for discuss categories. This can be done with or without any token. When the token is not passed, `verify_limited_field_access_token` is still called and crashes. When no token is provided, we should use category access rights instead and avoir verifying the token (which is `None`). [1]: https://github.com/odoo/odoo/pull/243131 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
### STEPS TO REPRODUCE: 1. Install AI, Livechat, and Website 2. Open Livechat and create a new channel 3. Ensure there is a welcome message 4. Add a rule with any AI Agent 5. Navigate to Settings > Website, enable Live Chat, and select the channel you just created 6. Go to the Website, click on the chat bubble, and verify that the first message shows "unnamed" as the sender of the welcome message ### CAUSE When a default message exists, the `author_id` is resolved by looking at the c
Original PR description
### STEPS TO REPRODUCE: 1. Install AI, Livechat, and Website 2. Open Livechat and create a new channel 3. Ensure there is a welcome message 4. Add a rule with any AI Agent 5. Navigate to Settings > Website, enable Live Chat, and select the channel you just created 6. Go to the Website, click on the chat bubble, and verify that the first message shows "unnamed" as the sender of the welcome message ### CAUSE When a default message exists, the `author_id` is resolved by looking at the current channel's history. However, it only checks `livechat_agent_history_ids`. Since a bot can also send the welcome message instead of an agent, `livechat_bot_history_ids` should also be checked.
A previous commit updated the `commonExtraData` from `GeneratePrinterData` in order to update the style from the pdis tickets. However, since `commonExtraData` is used for both receipt and pdis tickets, the change caused a bug for receipts tickets. This commit reverts the `commonExtraData` and update the code in order to still have the correct data dunble for pdis tickets. --- FIX Task: https://www.odoo.com/odoo/project/1737/tasks/6133403
Original PR description
A previous commit updated the `commonExtraData` from `GeneratePrinterData` in order to update the style from the pdis tickets. However, since `commonExtraData` is used for both receipt and pdis tickets, the change caused a bug for receipts tickets. This commit reverts the `commonExtraData` and update the code in order to still have the correct data dunble for pdis tickets. --- FIX Task: https://www.odoo.com/odoo/project/1737/tasks/6133403
The service worker required for push notifications is only available to internal users. This commit fixes the test setup by ensuring non-internal users are no longer registered, matching the expected flow. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269210
Original PR description
The service worker required for push notifications is only available to internal users. This commit fixes the test setup by ensuring non-internal users are no longer registered, matching the expected flow. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#269210
Steps to reproduce: 1. Drop a .s_tabs snippet 2. Click inside a tab to move the selection in it 3. Press backspace (remove each tab name + the last one should be empty) 4. Click on the "+" in the sidebar to add a Tab => Crash or on step 3: 3. Press backspace to delete one tab => Check the DOM: the tab has been removed, but the tab-pane element is still in the DOM and won't be deleted. This is easily fixed by adding `oe_unremovable` on tab links. task-4671317 Forward-Port-Of: odo
Original PR description
Steps to reproduce: 1. Drop a .s_tabs snippet 2. Click inside a tab to move the selection in it 3. Press backspace (remove each tab name + the last one should be empty) 4. Click on the "+" in the sidebar to add a Tab => Crash or on step 3: 3. Press backspace to delete one tab => Check the DOM: the tab has been removed, but the tab-pane element is still in the DOM and won't be deleted. This is easily fixed by adding `oe_unremovable` on tab links. task-4671317 Forward-Port-Of: odoo/odoo#275240
The partner credit limit warning on quotations and customer invoices depends on which company the user is currently working in, instead of the company of the document itself. Steps to reproduce: - Enable Sale Credit Limit in the settings of My Company (San Francisco) - Set a Credit Limit of 100 on a customer, e.g. Deco Addict - Create a draft quotation of 500 for that customer => The credit limit warning banner is displayed, as expected - Switch the active company to any other company, fo
Original PR description
The partner credit limit warning on quotations and customer invoices depends on which company the user is currently working in, instead of the company of the document itself. Steps to reproduce: -…
The partner credit limit warning on quotations and customer invoices depends on which company the user is currently working in, instead of the company of the document itself. Steps to reproduce: - Enable Sale Credit Limit in the settings of My Company (San Francisco) - Set a Credit Limit of 100 on a customer, e.g. Deco Addict - Create a draft quotation of 500 for that customer => The credit limit warning banner is displayed, as expected - Switch the active company to any other company, for example My Company (Chicago), keeping access to both companies - Open the same quotation again => The warning banner is gone, although neither the quotation nor the customer changed The credit fields used to build the warning are evaluated against the user's active company: credit_limit is a company-dependent field, and credit / credit_to_invoice are computed on the receivables of the current company. When the active company is not the document's company, the warning is checked against the wrong ledger and the wrong limit, so it can disappear on an over-limit customer or show up for a healthy one. Both computes already contain the line that was meant to handle this, but the result of with_company() was discarded, making it a no-op. Assign it, as every other compute in these files already does, so the warning is always evaluated in the document's company. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276308
\* : html_editor Commit [1]: Steps to reproduce: replacing image stuck issue when deleted 1. Go to Website > Edit. 2. Add any picture snippet (e.g., Text-Image). 3. Click the 'Replace' button and upload an image. 4. Open the media dialog again and delete the uploaded image. 5. Click the 'Discard' button. 6. Try to save the changes. Issue: - The website gets stuck in the same position and does not allow saving. - In the Python terminal, a missing error warning appears because the
Original PR description
\* : html_editor Commit [1]: Steps to reproduce: replacing image stuck issue when deleted 1. Go to Website > Edit. 2. Add any picture snippet (e.g., Text-Image). 3. Click the 'Replace' button and…
\* : html_editor Commit [1]: Steps to reproduce: replacing image stuck issue when deleted 1. Go to Website > Edit. 2. Add any picture snippet (e.g., Text-Image). 3. Click the 'Replace' button and upload an image. 4. Open the media dialog again and delete the uploaded image. 5. Click the 'Discard' button. 6. Try to save the changes. Issue: - The website gets stuck in the same position and does not allow saving. - In the Python terminal, a missing error warning appears because the image is deleted from both `ir.ui.view` and `ir.attachment`. Expected behaviour: - Saving should be allowed with a default image, that is similar to other images. This commit catch the warning response and replaces the deleted image, allowing the website to save changes without getting stuck. Commit [2]: resolve traceback when leaving edit mode via browser Steps to reproduce: 1. Go to Website > Edit. 2. Open the snippet modal and select any snippet. 3. Press the 'Back' button in your browser. 4. A dialog will appear asking to discard changes; click 'OK'. 5. A traceback error occurs, and an empty space appears in the editor. Issue: - Previously, a commit addressed a similar scenario, but that time the browser had an event listener bind on hashchange. - Now, that `hashchange` event of browser has been replaced with `popstate`, which triggers before the 'window' event listener. - As a result, the editor is left in an unstable state, causing a traceback error. Solution: - This commit ensures the 'window' event executes before the browser event. - It verifies if the editor is open and forces a `skipLoad`, preventing the `route_change` call in the browser. task-4570164 Forward-Port-Of: odoo/odoo#275334 Forward-Port-Of: odoo/odoo#199193
Issue: There is a missing closing curly bracket on line 67 in odoo/addons/stock/static/src/stock_forecasted/forecasted_details.xml (View) This PR corrects this error opw-6367046 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274575
Original PR description
Issue: There is a missing closing curly bracket on line 67 in odoo/addons/stock/static/src/stock_forecasted/forecasted_details.xml (View) This PR corrects this error opw-6367046 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#274575
The "Opening thread with needaction messages should mark all messages of thread as read" test opens a channel that holds an inbox (needaction) message and asserts mark_all_as_read is sent. Two flows can mark that message as read: the channel messages fetch, through set_message_done, and mark_all_as_read, sent by markAsRead when the channel gets focused on open. When the self member's new_message_separator is 0, opening the channel fetches its messages around 0, and that fetch marks the messag
Original PR description
The "Opening thread with needaction messages should mark all messages of thread as read" test opens a channel that holds an inbox (needaction) message and asserts mark_all_as_read is sent. Two flows can mark that message as read: the channel messages fetch, through set_message_done, and mark_all_as_read, sent by markAsRead when the channel gets focused on open. When the self member's new_message_separator is 0, opening the channel fetches its messages around 0, and that fetch marks the message as read and drops the needaction counter to 0 before markAsRead runs. mark_all_as_read is then skipped and the step assertion receives nothing. Give the member a non-zero separator (the pre-existing message is already read) so opening the channel no longer fetches around 0, leaving mark_all_as_read as the flow that marks the inbox message read. https://runbot.odoo.com/odoo/error/243651 Forward-Port-Of: odoo/odoo#276181
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The `AudioWorkletNode`'s port kept receiving tic messages briefly after `disconnect()`, since disconnecting only unroutes the audio graph and does not stop the worklet from posting pending messages. <img width="546" height="73" alt="voice_test_bug" src="https://github.com/user-attachments/assets/05a10d23
Original PR description
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The…
**Purpose of this PR:** Before this PR, the Voice detection sensitivity indicator in call settings could get stuck showing the last detected level after clicking "Stop" right after "Test". The `AudioWorkletNode`'s port kept receiving tic messages briefly after `disconnect()`, since disconnecting only unroutes the audio graph and does not stop the worklet from posting pending messages. <img width="546" height="73" alt="voice_test_bug" src="https://github.com/user-attachments/assets/05a10d23-fe60-4a85-b906-bfec6d235ec5" /> Steps to reproduce: 1. Open Voice & Video Settings. 2. Start the Voice detection sensitivity test. 3. Quickly click Stop immediately after clicking Test. 4. It may take a few tries, but eventually the Voice detection sensitivity indicator remains stuck at the last detected level. > [!NOTE] > this is timing-dependent. A tic message must already be in-flight from the worklet thread when `disconnect()` runs, so it won't happen every attempt. This race condition existed in the `disconnect` callback of `_loadAudioWorkletProcessor` since #66611, but stayed silent until #183969 introduced the Voice detection sensitivity feature in call settings, exposing it. This PR clears `port.onmessage` before disconnecting so late tic messages can no longer update the Voice detection sensitivity indicator after monitoring has stopped. Forward-Port-Of: odoo/odoo#275933
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via register_devices(). However, the VAPID public key was missing from the request kwargs. The server-side register_devices() always validates the VAPID key first and raises InvalidVapidError when it is absent. This caused the renewed subscription to never be saved in the database, silently breaking p
Original PR description
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via…
When a push subscription is renewed by the browser (typically every few days), the pushsubscriptionchange event fires and the service worker attempts to re-register the new subscription endpoint via register_devices(). However, the VAPID public key was missing from the request kwargs. The server-side register_devices() always validates the VAPID key first and raises InvalidVapidError when it is absent. This caused the renewed subscription to never be saved in the database, silently breaking push notifications after the first subscription renewal. Fix by extracting the applicationServerKey from the new subscription's options and encoding it as a base64url string (without padding) — matching the existing logic in webclient.js _arrayBufferToBase64(). Description of the issue/feature this PR addresses: Current behavior before PR: Subscriptions don't get renewed causing push notifications to stop eventually. Desired behavior after PR is merged: Subscriptions get renewed successfully. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276085 Forward-Port-Of: odoo/odoo#275217
This fixes two bugs in the web push subscription flow: - register_devices() compared partner records with 'is not' instead of '!='. Records loaded via sudo() live in a different environment than self.env.user, so 'is not' was always True and the ownership guard never behaved as intended. Use '!=', which compares record identity by model and id as Odoo's ORM intends. - webclient.js sent the previous subscription endpoint under the snake_case key 'previous_endpoint', while the server reads i
Original PR description
This fixes two bugs in the web push subscription flow:
- register_devices() compared partner records with 'is not' instead of '!='. Records loaded via sudo() live in a different environment than self.env.user, so 'is not' was always True and the ownership guard never behaved as intended. Use '!=', which compares record identity by model and id as Odoo's ORM intends.
- webclient.js sent the previous subscription endpoint under the snake_case key 'previous_endpoint', while the server reads it as 'previousEndpoint' (kw.get('previousEndpoint', endpoint)). The mismatch meant the lookup always fell back to the new endpoint, so a refreshed subscription created a duplicate device instead of updating the existing one. Send the camelCase key to match the server.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#276082Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
Original PR description
Since 414e55cf7c397, we can assign multiple users to a user-defined filter but because it's now a many2many, any user that got archived won't be shown in the `user_ids` fields anymore, it could mislead the filter being a global filter; whereas it's not. This commit also display archived users so we can see all users effectively assigned to the user-defined filter. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#275841
Remove the generic active 296 and 297 impairment accounts and make the existing French PCG 296/297 subaccounts active instead, because what we use in the balance sheet formulas are the subaccounts, and it's better to remove the generic ones to not give users the ability to post on these generic accounts, also adapt their translations to be aligned with PCG wording. Move pcg_2962 from the companies chart file to the base French chart file, as 2962 is a general PCG account and not a compa
Original PR description
Remove the generic active 296 and 297 impairment accounts and make the existing French PCG 296/297 subaccounts active instead, because what we use in the balance sheet formulas are the subaccounts, and it's better to remove the generic ones to not give users the ability to post on these generic accounts, also adapt their translations to be aligned with PCG wording. Move pcg_2962 from the companies chart file to the base French chart file, as 2962 is a general PCG account and not a company related one. Note: this is how things were already in 19.0 and this is how they should be, the changes happened by mistake as an unwanted side effect of commit 4f6068a6c88bf0530c19254df403e1194823b415 task-[6226138](https://www.odoo.com/odoo/project/967/tasks/6226138) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#265196
### Overview The SMS Queue Manager cron throws a traceback in a multi company environment when processing sms's that belong to distinct companies. ### Changes Before this commit, the _process_queue method tied to the cron was blindly batching sms's belonging to multiple companies without an sms_api context. The error results because the _send method that's called expects a singleton company when it tries to set the sms_api for the record set, but this isn't the case when the selected sm
Original PR description
### Overview The SMS Queue Manager cron throws a traceback in a multi company environment when processing sms's that belong to distinct companies. ### Changes Before this commit, the _process_queue…
### Overview The SMS Queue Manager cron throws a traceback in a multi company environment when processing sms's that belong to distinct companies. ### Changes Before this commit, the _process_queue method tied to the cron was blindly batching sms's belonging to multiple companies without an sms_api context. The error results because the _send method that's called expects a singleton company when it tries to set the sms_api for the record set, but this isn't the case when the selected sms batch is multi company. After this commit, the _process_queue method now follows the same pattern as the send method, grouping by sms_api / company within the batch, and eliminating the need to check for singleton, as all calls to _send will now have the sms_api context passed in. ### Steps to Reproduce on fresh 19.0 db: 1. Make sure sms / sms_twilio are installed. 2. Create two companies with their own SMS config. 3. Create two sms records, one with each company. 4. Ensure the state of the sms's is 'outgoing'. 5. Execute the SMS Queue Manager Cron. Observe the traceback: ValueError: Expected singleton... opw-6371272 Forward-Port-Of: odoo/odoo#276426
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into
Original PR description
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price…
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into a helper method so other localizations can override when needed. Current behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets incorrectly divided, resulting in negative discounts on the vendor bill. Expected Behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets parses as the exact unit price with no negative discounts applied. Task [link](https://www.odoo.com/odoo/project.task/6215466) task-6215466 Forward-Port-Of: odoo/odoo#276430 Forward-Port-Of: odoo/odoo#273129
Before this commit: If a user, with crm.leads linked to it, decided to request a password reset AND during that password reset process decided to activate the google oauth for their account, it would cause a crash. The reason is, during the password rest + oauth activation, self.env.user is an empty record set, which obviously will fail during the _is_portal check, due to its call to ensure_one() opw-6347228 Forward-Port-Of: odoo/odoo#275375
Original PR description
Before this commit: If a user, with crm.leads linked to it, decided to request a password reset AND during that password reset process decided to activate the google oauth for their account, it would cause a crash. The reason is, during the password rest + oauth activation, self.env.user is an empty record set, which obviously will fail during the _is_portal check, due to its call to ensure_one() opw-6347228 Forward-Port-Of: odoo/odoo#275375
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the website logo in the editor, the size shown is always a constant number ~5.9kB, regardless of the actual size of the uploaded logo. The correct size is visible in the browser DOM. This gives users the wrong impression that their image is being heavily compressed or losing quality when it isn'
Original PR description
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the…
Steps to reproduce: - Go to Website - Upload an image in the company logo (navbar) - Open the website editor and click on the logo - Open the image info panel Current behavior: When clicking on the website logo in the editor, the size shown is always a constant number ~5.9kB, regardless of the actual size of the uploaded logo. The correct size is visible in the browser DOM. This gives users the wrong impression that their image is being heavily compressed or losing quality when it isn't. Reason: When clicking the logo, the editor tries to find the original, unprocessed version of the image so it can support cropping and other edits. It does this by asking the server to match the image's URL to a stored attachment. The website logo is served through a dynamic link (`/web/image/website/<id>/logo/<name>`) that isn't tied to a regular attachment record the way normal content images are, since it isn't uploaded through the usual media picker. Because of this, the server can't find a matching original, and the editor is left without a valid image source to work with. As a fallback, the editor tries to load a placeholder path instead of a real image. This request fails and silently resolves to Odoo's generic "image not found" placeholder. All further processing (and the size calculation) then happens on this small placeholder image instead of the actual logo, which is why the size shown never changes. Fix: When `get_image_info` does not return a usable `original`, `loadImageInfo` now falls back to using the image's own current src as `originalSrc`, instead of leaving it unset. This ensures `loadImage` always receives a valid, resolvable URL, so image processing (and the size shown) reflects the actual logo. opw-6260496 Forward-Port-Of: odoo/odoo#273542
Code cleanup and technical improvements
This update removes duplicated internal logic for handling flexible employee leave in planning, because the same behavior is already provided by a related holidays module. It reduces maintenance risk without changing the expected user experience.
Original PR description
After commit https://github.com/odoo/enterprise/commit/e0b126cb1ec80743ef06784e230320a1c50041e9, `_handle_flexible_leave_interval` is already implemented in `hr_holidays_gantt` and covers the same case previously handled in `planning_holidays`. https://github.com/odoo/enterprise/blob/7ed917c1de11e29257a726efd4dafe403b9efba1/hr_holidays_gantt/models/resource_calendar.py#L17-L20 Since `planning_holidays` depends on `hr_holidays_gantt`, the duplicate implementation in `planning_holidays` is no longer necessary. **opw-6243778** Forward-Port-Of: odoo/enterprise#124322 Forward-Port-Of: odoo/enterprise#119229
Miscellaneous changes
Forward-Port-Of: odoo/enterprise#124221
Original PR description
Forward-Port-Of: odoo/enterprise#124221
7 changes
Enhancements to existing features
Move French e-invoicing technical statuses out of the main invoice UI and into the chatter. The PPF status is kept available in debug mode for troubleshooting, while regular users get a concise chatter summary with the PA status, PPF status, and any returned errors. Duplicate Lifecycle XML attachments are no longer posted in the chatter. Task-6273270 Forward-Port-Of: odoo/odoo#276770 Forward-Port-Of: odoo/odoo#269465
Original PR description
Move French e-invoicing technical statuses out of the main invoice UI and into the chatter. The PPF status is kept available in debug mode for troubleshooting, while regular users get a concise chatter summary with the PA status, PPF status, and any returned errors. Duplicate Lifecycle XML attachments are no longer posted in the chatter. Task-6273270 Forward-Port-Of: odoo/odoo#276770 Forward-Port-Of: odoo/odoo#269465
Resolved issues and error corrections
Bank reconciliation list views now show the same supporting attachments as the kanban view. This helps accounting users see the right documents consistently when reviewing and reconciling bank statement lines.
Original PR description
The aim of this commit is showing the same attachment in the bank reconciliation list view than in the kanban view. Before this commit, the field used to display the attachments was attachment_ids, this field were a related on the attachment_ids from account.move. This fix, removes the related to only keep a domain on the One2Many field. Thanks to the relational database, Odoo is giving us the right attachments when we want to display the field. task-6153002
Polish currency rates no longer need an extra one-day date adjustment because the system already uses the previous day’s rate. This prevents rates from being dated too far back and helps keep accounting calculations aligned with Polish requirements.
Original PR description
In Poland, it is mandatory to use the previous day's currency rate. We used to achieve this by shifting the rate's date by one day. Since https://github.com/odoo/odoo/pull/231948, we already use the previous day's rate, so the shift is no longer needed. See: https://www.odoo.com/mail/message/1118975071
Fixes how salary contract updates choose their template, using the current contract version when available or the offer's template otherwise. This helps HR teams generate contract updates with the expected document structure and reduces manual corrections.
Original PR description
contract update template should come from current version if any or from the offer's contract template. Task-6094733 Forward-Port-Of: odoo/enterprise#124638 Forward-Port-Of: odoo/enterprise#123450
Before this commit: If a user, with crm.leads linked to it, decided to request a password reset AND during that password reset process decided to activate the google oauth for their account, it would cause a crash. The reason is, during the password rest + oauth activation, self.env.user is an empty record set, which obviously will fail during the _is_portal check, due to its call to ensure_one() opw-6347228 Forward-Port-Of: odoo/odoo#275375
Original PR description
Before this commit: If a user, with crm.leads linked to it, decided to request a password reset AND during that password reset process decided to activate the google oauth for their account, it would cause a crash. The reason is, during the password rest + oauth activation, self.env.user is an empty record set, which obviously will fail during the _is_portal check, due to its call to ensure_one() opw-6347228 Forward-Port-Of: odoo/odoo#275375
Currently, overlays remain open when the POS screensaver is loaded [^1]. #### Steps to reproduce: - Open the POS interface. - Open a dropdown/popover menu (e.g., the navbar hamburger menu). - Wait for the screensaver (SaverScreen) to trigger due to inactivity. - The open dropdown menu remains visible on top of the screensaver. #### Issue Dropdowns and popovers are rendered as active overlays outside the main screen container. While the screensaver setup closes active dialogs, it doe
Original PR description
Currently, overlays remain open when the POS screensaver is loaded [^1]. #### Steps to reproduce: - Open the POS interface. - Open a dropdown/popover menu (e.g., the navbar hamburger menu). - Wait for the screensaver (SaverScreen) to trigger due to inactivity. - The open dropdown menu remains visible on top of the screensaver. #### Issue Dropdowns and popovers are rendered as active overlays outside the main screen container. While the screensaver setup closes active dialogs, it does not handle active overlays. #### Fix Retrieve the overlay service in SaverScreen and close all active overlays during its setup phase using a dedicated `closeAllOverlays` method. [^1]:  Forward-Port-Of: odoo/odoo#276672 Forward-Port-Of: odoo/odoo#274716
Miscellaneous changes
Forward-Port-Of: odoo/enterprise#124221
Original PR description
Forward-Port-Of: odoo/enterprise#124221
2 changes
Resolved issues and error corrections
Odoo Studio now handles field labels written with non-Latin characters, such as Arabic, without triggering an invalid field name error. This lets users rename fields in their own language more reliably and avoids interruptions when customizing views.
Original PR description
Steps: - Install web_studio - Add any field (example char field) to any view - Rename it in arabic, example `السَّلَامُ عَلَيْكُمْ` - Error Custom field names cannot contain double underscores Webclient (view_editor_model) escape every non-alphabetic chars, so new label value contains nothing but a space which will be replaced by a _ this new label value will be concatenated to `x_studio_`. Resulting to the string `x_studio__`. A solution should be to prevent changing the technical name if the new label value (escaped) is empty. opw-6311027 Forward-Port-Of: odoo/enterprise#122094 Forward-Port-Of: odoo/enterprise#121343
This fix makes the POS barcode lookup feature check product creation permissions immediately and consistently. It helps avoid situations where users might see incorrect product creation options due to delayed permission checks.
Original PR description
Replace the asynchronous `allowProductCreation` method with the `hasProductCreationAccess` getter to evaluate product creation permissions synchronously and ensure consistent behavior. Task-6361787 Related PR: https://github.com/odoo/odoo/pull/274420 Forward-Port-Of: odoo/enterprise#124527 Forward-Port-Of: odoo/enterprise#123073
6 changes
Resolved issues and error corrections
Odoo Studio now handles field labels written with non-Latin characters, such as Arabic, without creating an invalid internal field name. This prevents an error when users rename custom fields and makes Studio more usable for multilingual teams.
Original PR description
Steps: - Install web_studio - Add any field (example char field) to any view - Rename it in arabic, example `السَّلَامُ عَلَيْكُمْ` - Error Custom field names cannot contain double underscores Webclient (view_editor_model) escape every non-alphabetic chars, so new label value contains nothing but a space which will be replaced by a _ this new label value will be concatenated to `x_studio_`. Resulting to the string `x_studio__`. A solution should be to prevent changing the technical name if the new label value (escaped) is empty. opw-6311027 Forward-Port-Of: odoo/enterprise#122094 Forward-Port-Of: odoo/enterprise#121343
The point of sale barcode lookup now checks product creation permissions in a more consistent way. This helps ensure users only see or use product creation options when their access rights allow it, reducing confusing behavior at checkout.
Original PR description
Replace the asynchronous `allowProductCreation` method with the `hasProductCreationAccess` getter to evaluate product creation permissions synchronously and ensure consistent behavior. Task-6361787 Related PR: https://github.com/odoo/odoo/pull/274420 Forward-Port-Of: odoo/enterprise#123073
This fixes an issue where resetting certain Uruguay electronic invoicing records to draft could fail when triggered remotely. The change ensures the action returns a valid response, preventing avoidable errors during accounting workflows.
Original PR description
Calling the method in RPC causes an error: ``` TypeError: cannot marshal None unless allow_none is enabled ``` Forward-Port-Of: odoo/enterprise#124653
Users can no longer trigger a server error by creating a new commission adjustment recipient directly from the adjustment list. The change prevents an invalid entry path, keeping the Sales Commission adjustments screen stable.
Original PR description
Steps to reproduce: - Go to Sales > Commission > Adjustments - In Add to/Reduce From, type a name with no match - Click Create '<name>' -> server crashes (RPC_ERROR) Cause: `sale.commission.plan.user._rec_name` is a Many2one (`user_id`). Default `name_create` wrote the typed text into that integer column, which Postgres rejected. Fix: - Disable quick-create on `add_user_id`/`reduce_user_id` in the Adjustments list view Version: saas-18.3 to master opw-6384909
**Issue 1:** Steps to reproduce: - Install the `l10n_ar` module. - Go to Customers and create a new customer with the country set to Argentina. - Set `Identification Type` to `CUIL` and `Identification Number` to `1234567890a`. **Error:** `ValueError: invalid literal for int() with base 10: '1234567890a'` **Issue 2:** - Set `Identification Type` to any type other than `CUIT`, `DNI`, or `CUIL`. - Set `Identification Number` to `1234567890a`. **Observation:** All non-digit chara
Original PR description
**Issue 1:** Steps to reproduce: - Install the `l10n_ar` module. - Go to Customers and create a new customer with the country set to Argentina. - Set `Identification Type` to `CUIL` and…
**Issue 1:** Steps to reproduce: - Install the `l10n_ar` module. - Go to Customers and create a new customer with the country set to Argentina. - Set `Identification Type` to `CUIL` and `Identification Number` to `1234567890a`. **Error:** `ValueError: invalid literal for int() with base 10: '1234567890a'` **Issue 2:** - Set `Identification Type` to any type other than `CUIT`, `DNI`, or `CUIL`. - Set `Identification Number` to `1234567890a`. **Observation:** All non-digit characters are stripped, and the identification number is silently changed to `1234567890`. **Expected behaviour:** Any Identification Type other than CUIT (80), CUIL (86), and DNI (96) should be kept unchanged without stripping alphabetic characters. **Root Cause:** At [1], `_get_id_number_sanitize` sanitizes identification numbers based on the selected Identification Type. - For `CUIT` and `CUIL`, `stdnum.ar.cuit.compact()` only removes separators (e.g., spaces and dashes). If the identification number contains alphabetic characters, they are preserved and called `int()` on the resulting value, raising a `ValueError`. - For all other identification types, valid alphanumeric values are unintentionally modified by stripping non-digit characters. **Fix:** This commit validates identification numbers before sanitization for `CUIT` (80), `CUIL` (86), and `DNI` (96), ensuring only valid numeric identification numbers are converted. For all other identification types, it preserves alphanumeric characters by removing only non-alphanumeric separators. [1]: https://github.com/odoo/odoo/blob/08b75d753c638e9d2d7418b55e9107bda471cb31/addons/l10n_ar/models/res_partner.py#L124-L136 Related enterrpise PR: https://github.com/odoo/enterprise/pull/123729 opw-6333998
### Issue: The 'Schedule an appointment' and 'Next Events' CTA buttons were not updated even when their conditions were satisfied. ### Steps to reproduce: - Install only Website. - In the configurator, choose 'Schedule Appointments' as the main objective. - Complete the setup and create the website. - The CTA button remains 'Contact Us' instead of 'Schedule an appointment'. ### Reason: The `get_cta_data()` method is overridden in specific modules to update the CTA button base
Original PR description
### Issue: The 'Schedule an appointment' and 'Next Events' CTA buttons were not updated even when their conditions were satisfied. ### Steps to reproduce: - Install only Website. - In the configurator, choose 'Schedule Appointments' as the main objective. - Complete the setup and create the website. - The CTA button remains 'Contact Us' instead of 'Schedule an appointment'. ### Reason: The `get_cta_data()` method is overridden in specific modules to update the CTA button based on conditions. However, it is called before those modules are installed, so the overridden logic is never executed. ### Fix: Ensure that `get_cta_data()` is called and the CTA button is updated after the required modules are installed. task-[6383681](https://www.odoo.com/odoo/project/974/tasks/6383681) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261781
9 changes
Resolved issues and error corrections
Fixes translation-related issues in accounting screens so users see consistent guidance and correctly ordered labels in their chosen language. Bank Matching now keeps the same empty-state help after reloads or language changes, and the fiscal year wizard can use natural wording in languages such as French.
Original PR description
The empty-state message in Bank Matching differs depending on how the view was reloaded. In particular, after changing language from inside the view, the message loses the sentence explaining that users can create or import bank transactions. Ensure the Bank Matching empty-state help remains consistent across all reload flows. --- The fiscal year setup wizard was building the tax periodicity label from two separate translated parts. This produces an incorrect word order in some languages, such as "TVA Périodicité" in French which is supposed to be "Périodicité TVA". Use a single translatable label instead, so translations can place the tax label where it belongs in the sentence. task-6265141
This fix ensures the Avalara tax connection is disabled when a database is neutralized, such as in test or copied environments. It helps prevent those environments from accidentally connecting to the live tax proxy service.
Original PR description
Community: https://github.com/odoo/odoo/pull/272495
This change removes unused template markers from Helpdesk knowledge base search results and eCommerce subscription product pages. It prevents harmless backend warning messages during page rendering, keeping system logs cleaner without changing the customer-facing experience.
Original PR description
When rendering specific server-side pages (Knowledge Base search results and the eCommerce subscription product page), the Python QWeb engine logs the following warning:
"Unknown directives or unused attributes: {'t-key'} from..."
The `t-key` attribute is an OWL-specific directive required for client-side `t-foreach` loops. It is not recognized by the backend Python QWeb engine and serves no purpose in server-rendered templates.
This commit removes the inert `t-key` attributes from these Python-rendered templates.
task-6385543This fix makes map pin popovers open reliably after selecting a record from the pin list. It removes a timing issue that could cause the popover to disappear unexpectedly during automated mobile testing, improving stability without changing user-facing behavior.
Original PR description
Clicking a record in the "PinList" opens a marker popover. Until now this was handled by `centerAndOpenPin`, which closed the pin list popover and then, after two `delay(0)`, centered the map and…
Clicking a record in the "PinList" opens a marker popover. Until now this was handled by `centerAndOpenPin`, which closed the pin list popover and then, after two `delay(0)`, centered the map and opened the marker popover. This was racy. Closing `pinListPopover` triggers a re-render of the view, during which all markers are removed and re-added. If that re-render happened after the popover was opened, the marker element the popover was anchored to no longer existed, and the popover closed itself through `Popover.onTargetMutate()` (which closes the popover when its target element leaves the DOM). Depending on timing, the popover would sometimes be destroyed right after being opened, making the test flaky and leaving no popover open in the browser. To fix this, `centerAndOpenPin` is split in two parts: * it now only closes `pinListPopover` and raises a `shouldOpenMarkerPopover` flag; * on the next `onPatched`, once the markers have been re-rendered, the new `centerAndOpenPinOnPatched` method centers the map on the marker and opens the popover. Opening the popover after the re-render guarantees the marker element is present, removing the race condition. runbot-error-944199
This fixes an issue where opening a Belgian Dimona declaration could fail if an employee's private street information was missing. The change ensures payroll users can continue the declaration process without an unexpected error.
Original PR description
action_open_dimona guards on `self.employee_id.private_street` but then runs re.findall on `self.private_street` Forward-Port-Of: odoo/enterprise#124029
New planning slots now use the company’s working hours in the company timezone, so default start and end times appear correctly. This prevents schedule entries from being shifted by timezone differences when no resource is selected.
Original PR description
Issue: ---------------------------------------- When creating a new slot, no resrouces are set so we use the calendar of the company but the hours are offset because of the timezone. Steps to reproduce: ---------------------------------------- - Have planning Installed - Have an hour based calendar, from 8 to 16 each day for example - Have the company timezone in UTC+2, same for you the user - Go in Planning "Schedule By Resource" view - Click "New" - The default start and end time are 10am and 6pm (2h offset) Cause: ---------------------------------------- `default_get()` calls `_company_working_hours()` to get the company calendar hours. But they are returned in UTC, so when displaying them they are converted to the user timezone and are offsetted. Solution: ---------------------------------------- `_company_working_hours()` should return the compny hours in the company timezone. opw-6333993 Forward-Port-Of: odoo/enterprise#123033
Code cleanup and technical improvements
The grid view was updated to use the newer interface expected by the latest Odoo web framework. This keeps keyboard navigation and time entry behavior working reliably while reducing reliance on outdated internal code.
Original PR description
Replaced `useLayoutEffect` with `signal.ref` + `useEffect` && `onMounted()` + `onPatched()` && `useListener` because `useLayoutEffect` is deprecated in OWL3. The useLayoutEffect refactored in this PR had test coverage — below are some tests that failed when the effect was commented out, and are now passing: - @web_grid/grid_view/grid_view_desktop/Edition navigate with tab/shift+tab and enter key - `@web_grid/grid_view/grid_view_desktop/Edition navigate with tab/shift+tab and enter key` - `@web_grid/grid_cells/float_time_grid_cell/FloatTimeGridCell in grid view` see commented-out runbot build: https://runbot.odoo.com/runbot/batch/2624598/build/116553722 see commented-out runbot build: https://runbot.odoo.com/runbot/batch/2624595/build/116553684
This update simplifies internal test setups for sales and rental flows, making them easier to maintain. It also starts aligning some tests with standard salesperson permissions, helping ensure checks better reflect typical day-to-day usage.
Original PR description
Simplify specs, and start converting some tests to run with standard salesmen rights.
This change updates several Odoo Enterprise screens and components to use the supported OWL3 lifecycle approach instead of a deprecated method. It helps keep the platform maintainable and compatible with newer frontend framework versions while preserving existing behavior when screens open and close.
Original PR description
useLayoutEffect is deprecated in OWL3, so replace the calls that used an empty dependency array (run-once-on-mount) with the native OWL3 lifecycle hooks onMounted and onWillUnmount. Effects that returned a cleanup function have their teardown moved into onWillUnmount so the unmount behaviour is preserved. Community PR: https://github.com/odoo/odoo/pull/276619
4 changes
Resolved issues and error corrections
The salary contract flow now uses the right update template, taking it from the current contract version when available or from the offer's contract template otherwise. This helps ensure contract updates generated from offers follow the intended template and reduces manual corrections.
Original PR description
contract update template should come from current version if any or from the offer's contract template. Task-6094733 Forward-Port-Of: odoo/enterprise#123450
This fix prevents an intermittent error when the signing document preview closes or reloads at just the wrong moment. It helps keep automated checks stable and reduces the chance of unexpected signing interface errors.
Original PR description
Due to a race condition, in the PDFIframe sometimes the Iframe gets detached before we access it and we therefore have this.root.defaultView = null. Since when the Iframe is detached we don't really care about defining the eventBus, we can fix the problem related to its assignation by first checking the value of this.root.defaultView and only continuing in case it has one. This PR fixes a runbot error. Runbot Error: 233524
This update adds automated test coverage to ensure currency translation adjustments use the correct exchange rates when the company currency changes during the year. It helps prevent incorrect financial reporting values in multi-currency scenarios, especially around average and historical exchange rates.
Original PR description
Following the fix made in community branch, this adds a test veryfing the expected behavior in case of a fluctuating rate for the domestic currency. Scenario 2: fluctuating domestic (USD) rate USD rate=1 from Jan 1 to Jun 30, USD rate=3 from Jul 1 to Dec 31 EUR rates unchanged: 2 from Jan 1, 4 from Jul 1 Correct conversion factors (= USD_rate / EUR_rate): Jan 1 – Jun 30 (182 days): 1/2 = 0.50 Jul 1 – Dec 31 (184 days): 3/4 = 0.75 Current rate at 2020-12-31: 3/4 = 0.75 Correct average rate: (0.50 * 182 + 0.75 * 184) / 366 = 229/366 ≈ 0.62568 Previsouly bugged average rate (USD fixed at current=3): (1.50 * 182 + 0.75 * 184) / 366 = 411/366 ≈ 1.12295 Historical equity rates (correct vs previously bugged): Mar 1 (USD=1, EUR=2): correct = 1/2 = 0.50; buggy = 3/2 = 1.50 → 40 * 0.50 = 20 vs 40 * 1.50 = 60 Oct 1 (USD=3, EUR=4): correct = 3/4 = 0.75; buggy = 3/4 = 0.75 → 60 * 0.75 = 45 (same by coincidence) task-5953104
Odoo Studio no longer fails when users rename fields with Arabic or other non-Latin labels. This prevents an unnecessary validation error and makes field labeling more reliable for multilingual users.
Original PR description
Steps: - Install web_studio - Add any field (example char field) to any view - Rename it in arabic, example `السَّلَامُ عَلَيْكُمْ` - Error Custom field names cannot contain double underscores Webclient (view_editor_model) escape every non-alphabetic chars, so new label value contains nothing but a space which will be replaced by a _ this new label value will be concatenated to `x_studio_`. Resulting to the string `x_studio__`. A solution should be to prevent changing the technical name if the new label value (escaped) is empty. opw-6311027 Forward-Port-Of: odoo/enterprise#122094 Forward-Port-Of: odoo/enterprise#121343
3 changes
Resolved issues and error corrections
The Knowledge and Documents cards on the customer portal now use the same layout as the other portal cards. This fixes a visual inconsistency where those cards appeared slightly wider, making the portal page look more polished and consistent.
Original PR description
The knowledge and documents portal card was injected directly into "o_portal_docs", causing it to render wider than other cards. This happened because other cards sit inside "o_portal_category" (row g-2 mt-3) divs, while, knowledge was a direct child of o_portal_docs, giving it a different grid context despite both using col-md-6. To fix the issue, we wrapped the portal_docs_entry in an "o_portal_category row g-2 mt-3" div to match the structure of all other portal cards. Steps to reproduce: 1.Go to the website. 2.Click on name drop down menu on the navbar like "Mitchell Admin" 3.Click on "My Accont" from the drop down menu 4.Once the page loads properly, you can see the knowledge card width is a little bit larger than other cards. Forward-Port-Of: odoo/enterprise#119939
### Issue: The 'Schedule an appointment' and 'Next Events' CTA buttons were not updated even when their conditions were satisfied. ### Steps to reproduce: - Install only Website. - In the configurator, choose 'Schedule Appointments' as the main objective. - Complete the setup and create the website. - The CTA button remains 'Contact Us' instead of 'Schedule an appointment'. ### Reason: The `get_cta_data()` method is overridden in specific modules to update the CTA button base
Original PR description
### Issue: The 'Schedule an appointment' and 'Next Events' CTA buttons were not updated even when their conditions were satisfied. ### Steps to reproduce: - Install only Website. - In the configurator, choose 'Schedule Appointments' as the main objective. - Complete the setup and create the website. - The CTA button remains 'Contact Us' instead of 'Schedule an appointment'. ### Reason: The `get_cta_data()` method is overridden in specific modules to update the CTA button based on conditions. However, it is called before those modules are installed, so the overridden logic is never executed. ### Fix: Ensure that `get_cta_data()` is called and the CTA button is updated after the required modules are installed. task-[6383681](https://www.odoo.com/odoo/project/974/tasks/6383681) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#261781
Users cannot navigate to the next page in the message list view when hidden records cause the current page to appear incomplete. ### Steps to reproduce 1. Create many messages, ensuring some are hidden from a specific user via access rules. 2. Open the message list view as that user. 3. Pagination incorrectly shows a total matching the current page (e.g., "1-68 / 68") instead of the full count (e.g., "1-80 / 5000+"). 4. Because the system believes all records are displayed, the "Next" p
Original PR description
Users cannot navigate to the next page in the message list view when hidden records cause the current page to appear incomplete. ### Steps to reproduce 1. Create many messages, ensuring some are…
Users cannot navigate to the next page in the message list view when hidden records cause the current page to appear incomplete. ### Steps to reproduce 1. Create many messages, ensuring some are hidden from a specific user via access rules. 2. Open the message list view as that user. 3. Pagination incorrectly shows a total matching the current page (e.g., "1-68 / 68") instead of the full count (e.g., "1-80 / 5000+"). 4. Because the system believes all records are displayed, the "Next" page button is disabled, leaving the user stuck on the current page despite more records existing in the database. ### Cause The `mail.message` model filters records in Python after fetching them from the database to enforce complex access rules. 1. The system queries the database for a batch of records up to the view's limit. 2. The `_search` method removes inaccessible records from this batch. 3. The web client receives fewer records than the requested limit. 4. Interpreting this as the end of the dataset, the web client skips the count query and displays the current batch size as the total. ### Fix Add `'force_search_count': 1` to the action context. This forces the web client to execute a separate count query to determine the total number of records, regardless of the batch size returned. opw-5425361
1 change
Resolved issues and error corrections
Refreshing an accounting report now clears the previous search from the session, so downloaded Excel files reflect the partners currently shown on screen. This avoids confusing mismatches where the page shows all partners but the export still contains only an earlier search result.
Original PR description
**Steps to reproduce:** - Install account_reports - Open "Partner Ledger" (make sure there are several partners) - Make a search to only display 1 partner - Download XLSX - Without changing the search text, refresh the page - Download XLSX again **Issue:** After refresh, the search text is empty and all the partners are displayed in the report. However, in the XLSX file, only the partner from the previous search is present. **Cause:** The current search is kept in the session and used when getting the XLSX. When refreshing or leaving the page, it's still kept in the session even if the search bar has been reset. opw-6333212