Daily updates from Odoo
Tuesday, August 11, 2026
86 changes
11 changes
Enhancements to existing features
The spreadsheet date picker now inserts actual date values instead of number values. This makes spreadsheet entries clearer and helps avoid confusion or incorrect formatting when users choose dates.
Original PR description
Task: 6353692
Payroll no longer automatically changes the current driver of company cars when benefits are updated. Employees selecting or receiving a company car are instead marked as the future driver, keeping fleet assignments clearer and avoiding unnecessary follow-up tasks.
Original PR description
. Remove the auto-assignment of the Driver based on the payroll benefits. . If an employee signs a contract and selects the car or the car gets added to the employee's benefits, he should become the car's future driver. . Don't offer in the salary configurator cars for which the future driver is filled. . Don't generate a task every time the payroll officer assigns a new driver to the car . Add the corresponding tests task-6425360 Forward-Port-Of: odoo/enterprise#126016
The Call Debrief view now makes better use of larger screens by showing video and transcription side by side when both are available. It also adapts more smoothly in full-screen mode, making review sessions easier to follow across different screen sizes.
Original PR description
This PR improves the Call Debrief experience across screen sizes and adapts the UI by: - Use a two-column layout on larger screens when both video and transcription are available. - Make the layout adapt correctly in full-screen mode. task-6328684 Requires: - https://github.com/odoo/odoo/pull/272719
All signers can now choose whether to add a frame when adopting their signature, not just internal Odoo users. The public signing page also has clearer visible borders on signature-related fields and buttons, making the signing flow easier to use.
Original PR description
Version: 19.0 Before this PR: The 'Frame' checkbox in the 'Adopt Your Signature' dialog was only shown to internal Odoo users (users with the `base.group_user` group).Also, on the public signing page, the Full Name input, the Frame checkbox and the Auto/Draw/Load buttons had no visible border After this PR: The 'Frame' checkbox is now rendered for every signer in the 'Adopt Your Signature' dialog. The Full Name input, Frame checkbox and Auto/Draw/Load buttons now have a visible border. Taskid-4610728 Forward-Port-Of: odoo/enterprise#127439 Forward-Port-Of: odoo/enterprise#126789
The timesheet timer menu now loads with fewer server requests and reuses already available information where possible. This should make the systray experience feel faster and more responsive for users tracking time, especially when opening or switching contexts.
Original PR description
This PR removes some blocking RPC calls and caches information to make the loading of the systray as lightweight as possible. Changes include: - Move `field_get` to the lazy session info, so the field metadata is available client-side without a dedicated round-trip. - Cache the pre-filled form: it does not change as long as the task / project context stays the same, so it is computed once and reused. - Drop the `get_server_time` RPC and rely on the client-side clock. - Add a client-side systray cache service to avoid redundant requests. Task-6131386 Forward-Port-Of: odoo/enterprise#124549 Forward-Port-Of: odoo/enterprise#120429
Resolved issues and error corrections
Users who have both Partner Commissions access and Purchase user rights can now create and view purchase orders as expected. The fix keeps normal purchasing access available for mixed-role users while preserving commission-only restrictions.
Original PR description
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new…
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new Purchase Orders, and existing Purchase Orders are also not visible in the Purchase module. ## Expected behavior: The expected behavior is that the user should be able to create and view Purchase Orders with these access rights. Additionally, clarification is required regarding the purpose of the new Partner Commissions access group. ## Steps to reproduce: - Go to user and assign Partner Commission rights as All or own document. - On Purchase, select group as User. ## Cause of the issue: partner_commission adds commission-specific purchase order record rules, but purchase users have no matching purchase-order rule in that module. For mixed-role users, the commission rule ends up restricting standard purchase orders as well. ## Fix: Apply the module's explicit all-purchase rule to purchase users so mixed users keep base procurement access while commission-only users remain restricted by the commission rules. opw-6366074 Forward-Port-Of: odoo/enterprise#126003
Large accounting reports could get stuck in a browser rendering loop, especially when moving from Profit and Loss to General Ledger with multiple companies or localizations. This fix changes how report line status information is prepared so the report opens reliably even with many lines.
Original PR description
## Description Opening an accounting report containing many lines can trigger Owl's maximum render iteration error, leading to an infinite rendering loop. This could notably occur when navigating…
## Description Opening an accounting report containing many lines can trigger Owl's maximum render iteration error, leading to an infinite rendering loop. This could notably occur when navigating from a Profit and Loss report to a General Ledger in multi-company setting or with a few l10n. `AccountReportLineName` used `asyncComputed` to create the audit status record, although its computation contained no asynchronous operation. Each line therefore published its result after the initial render and scheduled another render. With enough lines, the report exceeded Owl's limit of 1000 render iterations before the DOM could be updated. This commit uses `computed` instead, allowing the status record to be calculated and cached synchronously. Keep the record construction untracked so its internal reactive state does not become a dependency. opw-6432000 ## Steps to reproduce - Go on a runbot instance saas-19.4, enterprise build, select all companies, make sure some localications are installed (since we need quite a few lines). - P&L, select date range the whole year 2026, unfold the Expense account, kebab button -> General Ledger. If you have enough lines, you will enter an infinite loop in the front-end. Not always obvious to reproduce it. The test provided is deterministic in term of reproduction.
This fix prevents occasional errors when an UrbanPiper delivery order acceptance is processed twice at nearly the same time. It makes the preparation-ticket marking step safely ignore already processed orders, reducing intermittent failures in point-of-sale order flows.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#127467 Forward-Port-Of: odoo/enterprise#125840
This update fixes several issues in the Timesheets Assistant so users see a clearer default timeline, avoid duplicate loading, and no longer have dismissed suggestions reappear. It also improves visual selection behavior and better recognizes Discord browser activity, making assisted timesheet creation more trustworthy and easier to use.
Original PR description
## [FIX] timesheet_grid: remove duplicate rpc call Before this commit, the `loadTimesheets` method is called 2 times in a row, that method does a rpc call to load the existing timesheets and so, it…
## [FIX] timesheet_grid: remove duplicate rpc call Before this commit, the `loadTimesheets` method is called 2 times in a row, that method does a rpc call to load the existing timesheets and so, it is not needed to call it 2 times since the rpc will return the exact same result. This commit removes the rpc call when we compute the suggestions to only load the timesheets when we load all the data. ## [FIX] timesheet_grid: show chronological view instead of project view Before this commit, the `by project` view were loaded first in the timesheet assistant action, to group the suggestion by project, the problem is at the beginning the view will not really show a perfect matching and so the user could think the feature does not work and he will not understand how to correctly match the suggestions shown in the view. This commit changes the view loaded by default in Timesheets Assistant to first show the chronological view, that view is more logical for the current user to rethink what he did in the past to correctly map the events to a project and a task when he generates his timesheets thanks to those events. The by project view is still useful afterwards when the system has learned the choices made by the current user. ## [FIX] timesheet_grid: ensure events are consumed forever Before this commit, the suggestions removed by the current user comes back when he changes the date and come back to the day he removes the suggestions. The reason is because a shallow copy of events consumed is made and that copy alters the duration of the initial object. This commit avoids copying the consumed events object to make sure the initial object is not altered when processing the events to remove them if they are removed before by the user. ### Steps to reproduce the issue: 1. install timesheet_grid and Activity watch, makes sure Activity watch collects some activities on your computer. 2. Go to Assistant menu in timesheets app. 3. Remove some suggestions displayed in the right panel. 4. Go to next date. 5. Come back to previous date. ### Expected Behavior: The suggestions removed should not appear again. ### Actual Behavior: The suggestions removed come back in the view. ## [FIX] timesheet_grid: fix flicker when suggestion selected Before this commit, when the user selects a suggestion in timesheet assistant, there is a small flicker appears because the height of the row grows because of the border added to highlight the suggestion selected. This commit reviews a bit the style to make sure the border bottom in the previous element is removed if the element is not selected or if the 2 consecutives suggestions are selected. ## [FIX] timesheet_grid: fix discord rules to handle discord in web Before this commit, when the user uses discord in its browser instead of the app on his computer, the discord rules don't catch the activity watch events because the tab title is different than the windows name in the app. This commit adapts the regex of Discord rules to handle the both use cases. task-[6385639](https://www.odoo.com/odoo/project.task/6385639) Forward-Port-Of: odoo/enterprise#125599 Forward-Port-Of: odoo/enterprise#124855
Fixed an error that could occur when users turned the No Follow-Up option on or off for invoices paid in multiple installments. This prevents server crashes in the Follow-Up Report when some installments are already settled, helping accounting teams manage customer follow-ups reliably.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#126785
Forward-Port-Of: odoo/enterprise#126156Planning reports now include shifts for employees with flexible schedules even when no specific start and end hours are set. This keeps the Schedule views and Planning / Timesheets Analysis report consistent, giving managers more reliable planned time data.
Original PR description
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning…
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning / Timesheets Analysis** report. ## Steps to Reproduce 1. Create an employee with a Flexible Working Schedule in the Employee form, or configure working hours where both `Hour from` and `Hour to` are left unset. 2. Add a shift for this employee linked to a project and a task. 3. Publish the shift. 4. Navigate to **Planning → Schedule → By Project**, switch to the pivot view, and observe that the shift created in step 2 appears and is counted. 5. Navigate to **Planning → Reporting → Planning / Timesheets Analysis**, switch to the pivot view, and observe that the same shift does not appear. ## Behavior After the PR When an employee does not have explicit `hours_from` and `hours_to` values, their shift is now considered valid in both the **Schedule by X** views and the **Planning / Timesheets Analysis** report. ## Additional Notes - In earlier versions of Odoo, the `Work From` and `Work To` fields were mandatory. With a change to flexible working schedules and the option to define only the total number of hours per day, these fields may now be left empty. This change exposed the underlying issue addressed by this fix. task-[5969788](https://www.odoo.com/odoo/project/4105/tasks/5969788) Forward-Port-Of: odoo/enterprise#110606
12 changes
Enhancements to existing features
All signers can now choose whether to add a frame when adopting their signature, not just internal Odoo users. The signing dialog also has clearer borders around key inputs and buttons, making the public signing experience easier to use.
Original PR description
Version: 19.0 Before this PR: The 'Frame' checkbox in the 'Adopt Your Signature' dialog was only shown to internal Odoo users (users with the `base.group_user` group).Also, on the public signing page, the Full Name input, the Frame checkbox and the Auto/Draw/Load buttons had no visible border After this PR: The 'Frame' checkbox is now rendered for every signer in the 'Adopt Your Signature' dialog. The Full Name input, Frame checkbox and Auto/Draw/Load buttons now have a visible border. Taskid-4610728 Forward-Port-Of: odoo/enterprise#127280 Forward-Port-Of: odoo/enterprise#126789
Document links are now calculated more efficiently by narrowing the search work and avoiding an expensive filter. This should improve performance when working with documents and attachments without changing user-facing behavior.
Original PR description
* Prefetching attachment_ids in sudo allows to limit the scope of the documents search * Removing the location filter on the document, not worth the performance hit. Follow-up of Task-5882406
Resolved issues and error corrections
Users with both Partner Commissions and Purchase access can now create and view purchase orders as expected. This prevents commission-related access rules from unintentionally blocking normal purchasing work while keeping commission-only restrictions in place.
Original PR description
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new…
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new Purchase Orders, and existing Purchase Orders are also not visible in the Purchase module. ## Expected behavior: The expected behavior is that the user should be able to create and view Purchase Orders with these access rights. Additionally, clarification is required regarding the purpose of the new Partner Commissions access group. ## Steps to reproduce: - Go to user and assign Partner Commission rights as All or own document. - On Purchase, select group as User. ## Cause of the issue: partner_commission adds commission-specific purchase order record rules, but purchase users have no matching purchase-order rule in that module. For mixed-role users, the commission rule ends up restricting standard purchase orders as well. ## Fix: Apply the module's explicit all-purchase rule to purchase users so mixed users keep base procurement access while commission-only users remain restricted by the commission rules. opw-6366074 Forward-Port-Of: odoo/enterprise#126003
Accepting an UrbanPiper order could intermittently fail because the system treated a repeated print-marking request as an error. This change makes that step safely ignore duplicates, reducing random failures during order acceptance and related automated checks.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
This fix prevents errors when creating operation steps in a multi-company setup where only the basic quality module is installed. It ensures quality team mail aliases have the right company context, so users are not blocked by a configuration issue they cannot resolve from the available screens.
Original PR description
This commit actually reverts [1] and manually forwards [2]. Suppose `mrp_workorder` installed and `quality_control` uninstalled. Because of the default value provided by [1], the only existing quality team is linked to the first company. As a result, when using another company, if the user tries to create an operation step (i.e., a QCP), it will raise an error when the onchange tries to load the default team in charge: https://github.com/odoo/enterprise/blob/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e/quality/models/quality.py#L141-L142 However, the `quality` module doesn't provide any view to create such a team. tldr The module raises an error that is actually impossible to solve... Let's avoid it in the above situation. [1] https://github.com/odoo/enterprise/commit/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e [2] https://github.com/odoo/enterprise/commit/8cd5c9322bef7db49a90d4aef844dd0ba267058e Forward-Port-Of: odoo/enterprise#127314 Forward-Port-Of: odoo/enterprise#126364
VoIP contact search and keypad suggestions now recognize phone numbers even when country codes or formatting differ. This helps users find the right contact more consistently when placing calls, reducing failed or missing suggestions.
Original PR description
Before this fix, the keypad's callee suggestions only matched the search term against the raw `phone` field of contacts. When the user input was automatically prefixed with a country code (e.g. +86), the match could fail if the stored phone number lacked the international prefix. Now `phone_sanitized` is also sent to the frontend via the Store, and the callee suggestion matching falls back to the E164 sanitized number when the raw phone field does not match. Task-6290760 compr https://github.com/odoo/odoo/pull/278018 Forward-Port-Of: odoo/enterprise#127437 Forward-Port-Of: odoo/enterprise#124797
Tasks created from project templates will now ignore archived users when assigning people through project roles. This prevents inactive employees or former users from being added to new customer project work, keeping task ownership accurate.
Original PR description
Steps to reproduce: ------------------------------------------------- 1. Install the `sale_project` module 2. Create a test user with Project User rights 3. Create a Project Role with the Created…
Steps to reproduce:
-------------------------------------------------
1. Install the `sale_project` module
2. Create a test user with Project User rights
3. Create a Project Role with the Created User as a Team Member
4. Create a Template Project as follows:
* Add one task to the template project
* Add the created Project role to the Task
5. Create a Service Type Product with:
* Create on order: Project
* Project Template: Created Template
6. Archive the Created User
7. Create and Confirm the Sale Order with the Created Product
Observation:
-------------------------------------------------
The generated task is assigned to the archived user, although the archived user is no longer part of the Project Role.
Issue:
-------------------------------------------------
While creating Project and Tasks from template, the context disable active record filtering (e.g., `active_test=False`), causing the assignment logic to fetch both active and inactive/archived users linked to the role. https://github.com/odoo/odoo/blob/8ec646e51497b38d34ea59296e0fc8644a50ee3a/odoo/orm/models.py#L4868
After that, during the `copy_data` method, It takes all the users from the roles without checking weather user is active or not
https://github.com/odoo/odoo/blob/8ec646e51497b38d34ea59296e0fc8644a50ee3a/addons/project/models/project_task.py#L890-L904
And even if we pass only Active users from this method, on moving further, it reassigns the users from roles without checking the Active field of the user
https://github.com/odoo/enterprise/blob/5abb147f9bf725daafc202d8259a5bb8a9b78d94/project_enterprise/models/project_task.py#L501-L503
https://github.com/odoo/enterprise/blob/5abb147f9bf725daafc202d8259a5bb8a9b78d94/project_enterprise/models/project_task.py#L544-L553
Due to this, the Archived User is also assigned to the tasks from the project roles
Solution:
-------------------------------------------------
Apply a `filtered('active')` check directly on the project role's users `(role.user_ids)` within the core task-copying logic in both `project` and `project_enterprise` modules. This ensures archived users are universally excluded from task assignments during template copying, regardless of what triggers the template instantiation.
Related Community PR: https://github.com/odoo/odoo/pull/274426
opw-6350841
Forward-Port-Of: odoo/enterprise#125637HR Gantt views now apply search filters consistently when deciding which employees to show, including employees without leave or attendance records. The views also standardize employee-based grouping to avoid confusing or unsupported results from other grouping options.
Original PR description
`user_domain` (in the context) is supposed to contain the domain defined by the user (in the search bar). It was not the case for all gantt views, and produced inconsistent results, as that domain is…
`user_domain` (in the context) is supposed to contain the domain defined by the user (in the search bar). It was not the case for all gantt views, and produced inconsistent results, as that domain is used to know when to display the employees without leaves/attendances. The PR fixes that issue by creating the `HrGanttModel` class, that takes care of defining the `user_domain` correctly. This class also disables the *Group By* menu, and defaults to grouping by employees. This was decided for the following reasons: - All gantt views inheriting this class would group by employee - Grouping by other fields would already not work in some cases - It's very difficult to add employees without records if the gantt is grouped by multiple fields at once Affected `_get_gantt_data()` functions have been adapted accordingly The access models (the ones defined in the `access.csv` files) are implied, and thus never passed as a parameter to `get_gantt_data()`, so we need to also manually add them when converting the model to the related field used in `groupby` task-5502544
This fix prevents access errors when HR users view salary offers by ensuring required payroll-related values are calculated with the right permissions. It also updates the salary configurator test flow to reflect realistic HR user access instead of relying on administrator rights.
Original PR description
A field displayed inside the offer should have been computed with sudo as it accesses some payroll field to compute. Also, the salary configurator tour has been adapted to use a HR user instead of an admin
Fixed an error that could occur when users turned the No Follow-Up option on or off for invoices with multiple payment installments. This prevents Follow-Up Reports from crashing when some installments have already been paid and reconciled.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#126785
Forward-Port-Of: odoo/enterprise#126156The Attendance Gantt view now includes employees without contracts when they have attendance records in the selected period. This prevents valid attendance entries from being hidden in the default grouped view, giving managers a more complete schedule overview.
Original PR description
Issue: The default Employee grouped Attendance Gantt hides an attendance when its employee has no contract. The record remains accessible from the list view or after removing the default grouping.…
Issue: The default Employee grouped Attendance Gantt hides an attendance when its employee has no contract. The record remains accessible from the list view or after removing the default grouping. Steps to reproduce: - Create an attendance-based employee without a contract. - Create an attendance for the employee. - Open Attendances with the default Date and Employee groupings. Cause: In https://github.com/odoo/enterprise/blob/c8c91758b148cced1b7b5c59479fba7aaea033c1/hr_attendance_gantt/models/hr_attendance.py#L151-L164 `_get_gantt_data_group_by_employee()` builds `employees_on_page` using only employees whose versions overlap the displayed period. It then adds those employee IDs to `attendances_domain`. A contractless employee is therefore excluded before its otherwise valid attendance is fetched. Solution: Build the employee visibility domain from employees having either an overlapping contract or an attendance matching the effective Gantt domain. Apply the same domain to the employee query and group count so contractless employees with visible attendances are included while contractless employees without attendances remain hidden. opw-6416111
Shifts for employees with flexible schedules are now counted consistently in planning and timesheet reports, even when specific start and end hours are not set. This fixes a reporting gap so managers see the same planned work across schedule views and analysis reports.
Original PR description
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning…
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning / Timesheets Analysis** report. ## Steps to Reproduce 1. Create an employee with a Flexible Working Schedule in the Employee form, or configure working hours where both `Hour from` and `Hour to` are left unset. 2. Add a shift for this employee linked to a project and a task. 3. Publish the shift. 4. Navigate to **Planning → Schedule → By Project**, switch to the pivot view, and observe that the shift created in step 2 appears and is counted. 5. Navigate to **Planning → Reporting → Planning / Timesheets Analysis**, switch to the pivot view, and observe that the same shift does not appear. ## Behavior After the PR When an employee does not have explicit `hours_from` and `hours_to` values, their shift is now considered valid in both the **Schedule by X** views and the **Planning / Timesheets Analysis** report. ## Additional Notes - In earlier versions of Odoo, the `Work From` and `Work To` fields were mandatory. With a change to flexible working schedules and the option to define only the total number of hours per day, these fields may now be left empty. This change exposed the underlying issue addressed by this fix. task-[5969788](https://www.odoo.com/odoo/project/4105/tasks/5969788) Forward-Port-Of: odoo/enterprise#110606
4 changes
Enhancements to existing features
The spreadsheet date picker now writes an actual date value instead of a numeric representation. This makes spreadsheet entries clearer and reduces confusion for users working with date fields.
Original PR description
Task: 6353692
Resolved issues and error corrections
When projects and tasks are created from templates, archived users linked through project roles are now excluded from task assignments. This prevents inactive employees from being assigned work automatically and keeps generated project tasks aligned with current team membership.
Original PR description
Steps to reproduce: ------------------------------------------------- 1. Install the `sale_project` module 2. Create a test user with Project User rights 3. Create a Project Role with the Created…
Steps to reproduce:
-------------------------------------------------
1. Install the `sale_project` module
2. Create a test user with Project User rights
3. Create a Project Role with the Created User as a Team Member
4. Create a Template Project as follows:
* Add one task to the template project
* Add the created Project role to the Task
5. Create a Service Type Product with:
* Create on order: Project
* Project Template: Created Template
6. Archive the Created User
7. Create and Confirm the Sale Order with the Created Product
Observation:
-------------------------------------------------
The generated task is assigned to the archived user, although the archived user is no longer part of the Project Role.
Issue:
-------------------------------------------------
While creating Project and Tasks from template, the context disable active record filtering (e.g., `active_test=False`), causing the assignment logic to fetch both active and inactive/archived users linked to the role. https://github.com/odoo/odoo/blob/8ec646e51497b38d34ea59296e0fc8644a50ee3a/odoo/orm/models.py#L4868
After that, during the `copy_data` method, It takes all the users from the roles without checking weather user is active or not
https://github.com/odoo/odoo/blob/8ec646e51497b38d34ea59296e0fc8644a50ee3a/addons/project/models/project_task.py#L890-L904
And even if we pass only Active users from this method, on moving further, it reassigns the users from roles without checking the Active field of the user
https://github.com/odoo/enterprise/blob/5abb147f9bf725daafc202d8259a5bb8a9b78d94/project_enterprise/models/project_task.py#L501-L503
https://github.com/odoo/enterprise/blob/5abb147f9bf725daafc202d8259a5bb8a9b78d94/project_enterprise/models/project_task.py#L544-L553
Due to this, the Archived User is also assigned to the tasks from the project roles
Solution:
-------------------------------------------------
Apply a `filtered('active')` check directly on the project role's users `(role.user_ids)` within the core task-copying logic in both `project` and `project_enterprise` modules. This ensures archived users are universally excluded from task assignments during template copying, regardless of what triggers the template instantiation.
Related Community PR: https://github.com/odoo/odoo/pull/274426
opw-6350841
Forward-Port-Of: odoo/enterprise#125637This fixes an intermittent error when accepting UrbanPiper delivery orders in Point of Sale. Orders that are already marked as printed are now handled gracefully, preventing occasional failed accept flows and unstable automated tests.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
This fixes an issue where planning managers without HR access could unintentionally publish planning slots when changing the assigned resource. The update uses public employee information so managers can make the change without triggering the wrong publication behavior.
Original PR description
For planning manager without HR access, if the user change the resource of the planning slot it will publish it automatically as the employee_ids field cannot be used without HR access. Prefer to use public employee to have better condition without using explicit sudo Caused-by: https://github.com/odoo/enterprise/commit/e88dcd0e545183b3f03e06b62158c52a1e6d2103
3 changes
Resolved issues and error corrections
Accepting an UrbanPiper delivery order no longer fails when the system receives duplicate print requests at nearly the same time. This makes order acceptance more reliable and prevents intermittent server errors during point-of-sale workflows and automated checks.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
Fixes an error that could occur when users turned the No Follow-Up setting on or off for invoices with multiple installments after one installment was already paid. This helps accounting teams manage customer follow-up reports without server interruptions.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#126785
Forward-Port-Of: odoo/enterprise#126156Rental orders that use stock transfers now keep track of picked up and returned serial numbers. This prevents the return wizard from opening with no available serial numbers after a partial return, allowing staff to complete later rental returns reliably.
Original PR description
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return…
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return wizard. **Steps to reproduce** - Activate "Rental Transfers" in the settings - Create a rental product P, tracked by serial number - Create two serial numbers for P - Create and confirm a rental order for 2 units of P - Validate the pickup transfer - Partially validate the return transfer without creating a backorder - Open the rental order and click on "Return" -> The return wizard opens without any available serial number and validation fails with a serial number-related error. **Cause** When clicking on "Return", if there is no pending pickup/return transfer: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/models/sale_order.py#L62-L68 the rental return wizard is opened directly: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/models/sale_order.py#L316 No serial number is prefilled in the wizard because `returned_lot_ids` is empty: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L122-L124 This is because `returnable_lot_ids` is empty as well. `returnable_lot_ids` is computed while generating the wizard lines: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L38 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L47-L48 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L99-L106 and `returnable_lots` is empty because both `pickedup_lots` and `returned_lots` are. Those fields are currently only populated through the rental wizard flow: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L42-L43 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L160-L161 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L166-L167 Since this flow uses stock pickings instead of the rental wizard, those fields are never updated, preventing the wizard from determining any returnable serial number. opw-6150305 Forward-Port-Of: odoo/enterprise#119257
4 changes
Resolved issues and error corrections
Odoo now recognizes five new SII response codes introduced by Chile's latest electronic invoicing rules. This prevents supplier electronic tax documents from getting stuck during processing when the tax authority returns these new statuses.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
Fixes an intermittent error that could occur when accepting UrbanPiper delivery orders if the same preparation-print step was triggered twice at nearly the same time. The system now safely ignores the duplicate request instead of showing a server error, improving reliability for point-of-sale order handling and automated tests.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
This fixes an error that could block users from customizing worksheet design templates in Studio after an upgrade. Templates linked to multiple companies are now handled correctly, so users can continue editing reports without encountering a crash.
Original PR description
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field. For example, in v17, a single worksheet template linked to 3 companies via the m2m field…
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field.
For example, in v17, a single worksheet template linked to 3 companies via the m2m field was returned as 1 record when opening Design Template. After the upgrade in v18, company_id became m2o, and the same data is split into 3 separate records (one per company).
When trying to add a customization via Studio, the search [fetches](https://github.com/odoo/enterprise/blob/18.0/worksheet/controllers/main.py#L12) records based on the model set on the worksheet. In the new version, Studio
[creates](https://github.com/odoo/enterprise/blob/18.0/worksheet/models/worksheet_template.py#L112)
a new model, but for existing records the
model is the same across the 3 worksheet records tied to the same template. This causes the search to match all 3 records and raise a SingletonError.
```py
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2856, in __call__
response = request._serve_db()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2331, in _serve_db
raise self._update_served_exception(exc)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/industry_fsm_report/controllers/main.py", line 9, in edit_view
action = super().edit_view(view_id, studio_view_arch, operations, model, context)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/worksheet/controllers/main.py", line 17, in edit_view
worksheet_template_to_change._generate_qweb_report_template()
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 490, in _generate_qweb_report_template
new_arch = self._get_qweb_arch(worksheet_template.model_id, report_name, form_view_id)
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 460, in _get_qweb_arch
if 'name' in row_node.attrib and row_node.attrib['name'] not in self._get_qweb_arch_omitted_fields() and row_node.attrib['name'] in form_view_fields:
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 378, in _get_qweb_arch_omitted_fields
'x_%s_id' % self.res_model.replace('.', '_'), 'x_name', # redundant
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1657, in __get__
record.ensure_one()
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5942, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: worksheet.template(3, 14, 18)
```
OPW: 6389190
Forward-Port-Of: odoo/enterprise#126773Fixes an error that could occur when users turn the No Follow-Up option on or off for invoices with multiple payment installments. This helps accounting teams manage follow-up settings reliably even when some installments have already been paid and reconciled.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#1261564 changes
Resolved issues and error corrections
Fixes an error that could block users from editing worksheet design templates after upgrading multi-company data. This ensures Studio customizations work reliably when the same worksheet template exists across multiple companies.
Original PR description
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field. For example, in v17, a single worksheet template linked to 3 companies via the m2m field…
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field.
For example, in v17, a single worksheet template linked to 3 companies via the m2m field was returned as 1 record when opening Design Template. After the upgrade in v18, company_id became m2o, and the same data is split into 3 separate records (one per company).
When trying to add a customization via Studio, the search [fetches](https://github.com/odoo/enterprise/blob/18.0/worksheet/controllers/main.py#L12) records based on the model set on the worksheet. In the new version, Studio
[creates](https://github.com/odoo/enterprise/blob/18.0/worksheet/models/worksheet_template.py#L112)
a new model, but for existing records the
model is the same across the 3 worksheet records tied to the same template. This causes the search to match all 3 records and raise a SingletonError.
```py
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2856, in __call__
response = request._serve_db()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2331, in _serve_db
raise self._update_served_exception(exc)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/industry_fsm_report/controllers/main.py", line 9, in edit_view
action = super().edit_view(view_id, studio_view_arch, operations, model, context)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/worksheet/controllers/main.py", line 17, in edit_view
worksheet_template_to_change._generate_qweb_report_template()
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 490, in _generate_qweb_report_template
new_arch = self._get_qweb_arch(worksheet_template.model_id, report_name, form_view_id)
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 460, in _get_qweb_arch
if 'name' in row_node.attrib and row_node.attrib['name'] not in self._get_qweb_arch_omitted_fields() and row_node.attrib['name'] in form_view_fields:
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 378, in _get_qweb_arch_omitted_fields
'x_%s_id' % self.res_model.replace('.', '_'), 'x_name', # redundant
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1657, in __get__
record.ensure_one()
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5942, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: worksheet.template(3, 14, 18)
```
OPW: 6389190
Forward-Port-Of: odoo/enterprise#126773The UK CIS report now correctly shows payments linked to receipts that include CIS tax, not only vendor bills. This ensures businesses get a more complete and accurate CIS reporting view for affected transactions.
Original PR description
With the l10n_uk_reports_cis module installed: - Create a vendor bills and add a CIS tax --> This vendor's bills appear correctly in the report. - Create a receipt and add a CIS tax --> This type of bill appears in the report, but the payment is not showing up. opw-6282548 Forward-Port-Of: odoo/enterprise#124043
Odoo now recognizes five new response codes introduced by Chile's tax authority for supplier electronic documents. This prevents affected supplier documents from getting stuck during processing and keeps the workflow aligned with the latest SII rules.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
Global invoices for Mexican Point of Sale now ignore cancelled refund orders. This prevents invoice creation errors when a customer refund was started, cancelled, and later completed correctly.
Original PR description
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click…
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click "Return Products" to make a refund, but don't pay it, cancel it instead. 4. From the same order, click "Return Products" again to make a second refund, and pay it normally. 5. Go to the orders list, select the main order and the paid refund (not the cancelled one), then Actions > Create Global Invoice. -> Observation: error in the global invoice. In the CFDI tab of the main order the line is "Send Global In Error", and hovering on it the detail says "Failed to distribute some negative lines". Why: ---- When we make the global invoice, we remove the refunds from the order. A cancelled refund was never paid, so we should not count it. But we were counting it too. So we removed the refund amount twice in our case, one for the paid refund, and one for the cancelled one, and we end up with an order with negative amount that cannot be distributed. The fix: -------- We now skip the cancelled orders when we search the refunds, the same way it is done above when we collect the refunded orders. opw-6261404 Forward-Port-Of: odoo/enterprise#120996
33 changes
Enhancements to existing features
Restaurant appointment table timers now use the same timing display and behavior as the core restaurant point-of-sale experience. This gives staff a more consistent, easier-to-read view of how long tables have been active, improving day-to-day floor management.
Original PR description
In this commit - -------------------------- - Match base timer formatting and behavior - Improve overall UX for table time tracking - Remove basic timer functionality from appointment and move it to restaurant Task-6152646 Related PR - https://github.com/odoo/odoo/pull/262756
Payroll warning checks now focus only on the relevant employee or payslip records instead of scanning broader sets of data. This reduces unnecessary processing and helps make some payroll warnings easier to maintain and translate, including for Belgian payroll workflows.
Original PR description
To reduce the amount of records fetched by domain warnings, active_ids will only fetch the ones we care about. This also makes it possible to translate some python warnings into domain now. task-6448866
Payroll accounting payments have been reorganized to make payment handling more consistent across payslips, employees, salary rules, and country-specific payroll flows. This should help payroll teams process and track salary payments more reliably, including payment registration and bank file workflows.
Original PR description
task-4592721
The Belgian payroll rules for double holiday pay have been corrected and improved. This helps payroll teams calculate and validate holiday-related payslips more reliably, reducing the risk of payroll errors.
Original PR description
task-5068001
This update reorganizes how payroll wage pay periods are managed across contracts, salary configuration, and payslip batches. It should make payroll setup and processing more consistent for HR teams, reducing confusion when employees are paid on different schedules.
Original PR description
task-5043999
Belgian payroll now better handles complementary salary payments made outside the normal monthly payroll cycle. The update adds regularization for social security contributions and withholding tax, helping corrections be taxed as if they were part of the regular salary calculation.
Original PR description
Sometimes a complementary salary need to be paid outside of the monthly pay (especially in case of corrections). The difference between a complementary salary and a bonus is the type of withholding tax that must be paid, complementary salary are those for which a standard withholding tax has to be paid. When some complementary salary are paid outside of the monthly pay, the withholding tax amount may be "wrong" and should be regularized. Example: a bonus of 250€ alone on a payslips will lead to a withholding tax of 0€ but if you consider it with the monthly pay it should be have been taxed. This commit introduces two new rules to regularize the ONSS and withholding tax amount. task-6001677
Payroll no longer uses a separate work entry type for periods outside an employee contract. This simplifies payslip calculations and reporting across payroll localizations by avoiding special handling for out-of-contract days.
Original PR description
task-6205747
Calendar events linked to a specific project or task are now matched using that direct link before relying on partner history. This helps employees create more accurate timesheets from calendar events and reduces manual corrections.
Original PR description
…the 'linked to' field Before this commit: - Calendar events are matched based on the most timesheet project of the partners. After this commit: - Calendar events are matched to projects/tasks via the "linked to field" before looking to partners projects. task-6238332 Forward-Port-Of: odoo/enterprise#123158
The spreadsheet date picker now enters dates as actual dates rather than plain numbers. This makes spreadsheet data clearer and helps avoid confusion or incorrect calculations when users work with dates.
Original PR description
Task: 6353692
Payroll now uses one clear rule to identify unpaid time: work entry types with a zero pay rate. This reduces configuration complexity across payroll localizations and helps prevent duplicate deductions, while extra hours are now identified through a dedicated payroll category.
Original PR description
The `unpaid_structure_ids` field was removed from work entry types to simplify the configuration. Previously, a work entry type could be marked as unpaid either by adding payroll structures to…
The `unpaid_structure_ids` field was removed from work entry types to simplify the configuration. Previously, a work entry type could be marked as unpaid either by adding payroll structures to `unpaid_structure_ids` or by setting `amount_rate` to 0. From now on, `amount_rate = 0` is the single source of truth for unpaid work entries. As part of this change: * Set `amount_rate` to 0 for out of contract entries, unpaid leaves, and all work entry types that were previously unpaid in monthly structures across localizations. * Kept `l10n_be_work_entry_type_notice` with `amount_rate = 1`. Although it was marked as unpaid for the 13th month structure, it remains paid in the monthly structure. Since work day lines are not used in the 13th month computation, keeping its rate would have no functional impact on the 13th month payroll calculation. * Removed unpaid work entry types from the Belgian `REMUNERATION_BASE`, `ONSS_BASE`, and `WITHHOLDING_BASE` categories, as they now have `amount_rate = 0` and therefore no impact on these bases. * Removed Egyptian salary rules that deducted unpaid leaves and out of contract periods. Since these work entries now contribute 0 to the base salary, keeping the deduction rules would result in double deductions. --- Introduce the `EXTRA_HOURS` category on work entry types as the source of truth for identifying extra hours. The `is_extra_hours` field is moved from `hr_work_entry` to `hr_payroll` and turned into a stored computed field based on the work entry type categories. This preserves its usage in payroll computations while making the category the source of truth. Task: 6292486
Belgian payroll now supports creating customized premium pay child categories and related rules. This gives payroll teams more flexibility to model company-specific premium pay needs while keeping calculations organized and test-covered.
Original PR description
Allow to create customized premium pay children task-6394804
Payslip salary rule lines are now shown in either the main payslip body or the extra information section, not both. This makes payslips clearer and ensures correction payslips show only the relevant difference in the extra information section too.
Original PR description
For salary rules, the "Visibility" (`appears_on_payslip`) and the "Show in extra info section" (`display_in_pdf_extra_info`) fields were unrelated. That means that a payslip line could be shown twice if (for e.g.) a salary rule with ```python appears_on_payslip = 'always' display_in_pdf_extra_info = True ``` would be shown twice in the payslip (once in the body and once in the extra info section) This PR fixes that to make it intuitive, now `appears_on_payslip` only affects the visibility, and `display_in_pdf_extra_info` only affects the placement of the line. So that it is never shown twice. The PR also makes it so that the "delta" logic (i.e.: showing only the difference in correction payslips) is also done in that section (it wasn't the case previously) task-6376147
Helpdesk customer rating settings are now managed per ticket stage instead of per team. This makes rating visibility and rating request emails more reliable, so businesses can control exactly when customer feedback is requested.
Original PR description
- Before this commit, the customer rating configuration was managed on `helpdesk.team`, which led to inconsistent behavior: - ratings could still appear even when disabled on the team - rating request emails could still be sent after disabling the feature - After this commit, the rating configuration is moved to `helpdesk.stage`, aligning the behavior with the Project and ensuring that ratings and rating request emails are controlled reliably through stage configuration. - The `Customer Ratings` option is removed from the helpdesk team form view. task-5119003
The Time Off planning calendar has been improved to make leave scheduling easier to review and manage. This should help HR teams work more efficiently when viewing and selecting multiple time off entries in the Gantt view.
Original PR description
task-6449840
US payroll configuration is improved with clearer field labels and updated default accounting mappings for salary rules and payroll tax categories. This helps payroll teams produce more consistent accounting entries while removing an obsolete California ETT tax field.
Original PR description
. Change fields labels . Remove CA ETT Tax field . Add default debit account Employee Payroll Taxes to Taxes rules category . Add default debit account Payroll Taxes and default credit account Employee Payroll Taxes to Employer Deductions rules category . Add default debit account Salaries and Wages to Basic salary rule . Remove default debit account and default credit account to Gross salary rule task-6374068
Belgian payroll users can now generate meal voucher reports from branch companies, not only the main company. This helps meet Partena requirements and makes payroll reporting more flexible for businesses with branch structures.
Original PR description
Currently, It's not possible to compute a meal voucher report from branch company. This PR introduces support for generating meal voucher reports from a branch company to meet Partena's requirements. Removing this constraint allows meal voucher reports to be generated from branch companies. task-6428777
All people signing a document can now choose whether their signature includes a frame, not just internal Odoo users. The signing dialog also has clearer borders around the name field and signature options, making the public signing experience easier to understand and use.
Original PR description
Version: 19.0 Before this PR: The 'Frame' checkbox in the 'Adopt Your Signature' dialog was only shown to internal Odoo users (users with the `base.group_user` group).Also, on the public signing page, the Full Name input, the Frame checkbox and the Auto/Draw/Load buttons had no visible border After this PR: The 'Frame' checkbox is now rendered for every signer in the 'Adopt Your Signature' dialog. The Full Name input, Frame checkbox and Auto/Draw/Load buttons now have a visible border. Taskid-4610728 Forward-Port-Of: odoo/enterprise#127439 Forward-Port-Of: odoo/enterprise#126789
Resolved issues and error corrections
The payroll pay run status display now marks earlier steps as completed once a pay run is validated, closed, paid, or cancelled. This gives payroll teams a clearer and more accurate view of pay run progress, reducing confusion during validation and closing.
Original PR description
When a pay run reaches '02_close' or beyond ('03_paid', '04_cancel'), step states remain unchanged because their compute methods lack `@api.depends('state')`, omit '02_close', and check if the field is already set.
Add the missing dependency, include '02_close', and remove the restrictive field check so step states update properly.
Task: 6428646This fix prevents Field Service task completion from getting stuck after a delivery has been returned and then re-delivered. By avoiding repeated processing of the same stock movements, the system no longer risks running out of memory in this workflow.
Original PR description
## **Steps to reproduce:** 1. Create a Service product with Create on Order set to Task and Project set to Field service project. 2. Create a Sales Order containing a storable product and a service…
## **Steps to reproduce:** 1. Create a Service product with Create on Order set to Task and Project set to Field service project. 2. Create a Sales Order containing a storable product and a service product. 3. Confirm the Sales Order to generate the project, task, and delivery order. 4. Validate the delivery order. 5. Create and validate a return for the delivery. 6. Create a return of the return to deliver the products again, but do not validate this new delivery. 7. Open the related task and click on Mark as Done button. ## **Issue:** In a delivery -> return -> return of return workflow, the stock move goes into this code https://github.com/odoo/enterprise/blob/59b86f106862c3a364ba633a1580d9051f2fe7ca/industry_fsm_stock/models/project_task.py#L89-L90 the traversal repeatedly revisits the same stock moves through move_dest_ids, causing the loop to alternate between the same move recordsets indefinitely. As a result, the loop never terminates, eventually exhausting the memory and raising a `MemoryError`. ## **Solution:** Track the stock moves that have already been visited and continue the traversal only with unseen destination moves. Runbot Video : [Video](https://drive.google.com/file/d/1wiqggjx8T-Mtl4T1JgCsfwBbgQU18nYF/view?usp=drive_link) OPW - 6420961 Forward-Port-Of: odoo/enterprise#125962
The bank reconciliation screen now consistently shows the "to review" option when users set an account, apply a reconciliation model, or handle payable and receivable items. This helps accounting teams flag transactions for follow-up across more reconciliation workflows and avoids missed review steps.
Original PR description
This commit will allow to have the "to review" button when using different action: - Set account - Reco model - Payable and receivable task-6409437 Forward-Port-Of: odoo/enterprise#125287
Fixed an issue in Belgian payroll where the public holidays wizard showed only one available time type. Users can now choose from the expected time types when loading public holidays, reducing setup errors for Belgian companies.
Original PR description
**Steps to Reproduce:** 1. Install the `l10n_be_hr_payroll` module. 2. Switch to the Belgium company. 3. Go to the Public Holidays tab and click on `Load Public Holidays`. 4. In the "Time Type" field, you will only see one time type record. **Cause:** The `l10n_be_hr_payroll` module has an overridden computed method that updates the incorrect value. **Fix:** Remove the computed method, as the logic is already covered in the standard method. Task-6448165
Financial reports now show current month and quarter choices correctly when opened with a yearly default period. This prevents misleading date filter options and ensures custom comparison periods default to dates up to today rather than future year-end dates.
Original PR description
When opening reports with `default_opening_date_filter='this_year'`(e.g., P&L, Partner Ledger), the date filter dropdown showed incorrect defaults for non-selected period types: - Month showed the…
When opening reports with `default_opening_date_filter='this_year'`(e.g., P&L, Partner Ledger), the date filter dropdown showed incorrect defaults for non-selected period types: - Month showed the last month of the fiscal year (e.g., December) instead of the current month - Quarter showed Q4 instead of the current quarter This happened because `initDateFilterState()` used the backend's `date_to` (fiscal year end) as the reference for computing all filter periods. For `this_year`, `date_to` is the year-end date (e.g., 2026-12-31), so `computePeriodRange()` for month/quarter returned periods containing that date rather than today's date. Reports with `this_month` or `today` defaults were unaffected because their `date_to` is naturally close to today. Now, non-selected filters use today as their reference date on initial load whenever today falls within the report period, while the selected filter continues to use the backend's `date_to`, preserving the alignment behavior introduced in the date filter refactor (https://github.com/odoo/enterprise/commit/40484f985f511edd7ba2ae759ce63ef564bcf1f7). Additionally, selecting the custom comparison filter now triggers an immediate reload so its default date range is recomputed by the backend. The custom comparison range is initialized using the current fiscal year up to today, capping its end date to today instead of inheriting the report's `date_to`, which could otherwise default to a future date for yearly reports. task-6229588 Forward-Port-Of: odoo/enterprise#127316 Forward-Port-Of: odoo/enterprise#121638
Payroll users can now validate several draft payslips from the list view without the action silently failing. If a country-specific payroll flow needs an extra wizard, such as Belgium payroll language handling, that wizard now appears and the selected payslips can be completed together.
Original PR description
Selecting draft payslips in the list and clicking Validate did nothing, no error, no dialog. `action_validate` called `action_payslip_done` but dropped what it returned, so a localization asking for a wizard got lost on the way. For fixing it, we pass it along now, same for compute_sheet. taskid-6435024
Belgian payroll now correctly excludes retired employees from ONSS contribution types 825 and 835. This prevents incorrect payroll deductions and helps keep employer social security reporting compliant.
Original PR description
If an employee is retired, they should not contribute to 825 and 835 ONSS contributions. Task: 6314865
Users can once again choose a business record after selecting a model from a document’s details panel. This fixes a broken linking flow that showed a notification but did not open the record selection dialog, helping teams correctly attach documents to the right records.
Original PR description
Reproduce: 1. Go to Inbox 2. Select a file 3. Open the details panel 4. Click on the link to record field 5. Select a model -> You get a notification but no dialog to select a record. Cause: since the replacement of useState with useEffect, the state is updated when we save the record which occured as initial step, leading to the temporary resModel stored in the state to be immediately reset. Fix: remove the intermediate save entirely and clean state usage. Task-6313958
Payroll pay run summary figures now refresh automatically when payslips or related time data change, avoiding stale KPI values that previously required a manual page refresh. The fix also ensures cancelled payslips are excluded from employer cost totals and corrects Belgian payroll KPI calculations.
Original PR description
Bug : - create an unvalidated leave - on the payrun Time view, when validating the view -> click on continue -> error popup appears says you have unvalidated leave (BUG 1) - on the payrun Payslips…
Bug : - create an unvalidated leave - on the payrun Time view, when validating the view -> click on continue -> error popup appears says you have unvalidated leave (BUG 1) - on the payrun Payslips view, when canceling a payslip , the KPIs values don't change unless you refresh (BUG 2) Reason : - PayRunMixin now subscribes to its model's "update" bus event and forwards it to updatePayRun, so any pay run view whose model emits "update" keeps the summary in sync. - The natural trigger is the model's "update" bus event, but the mixin never listened to it. On top of that the relational payslip list never even emits "update" on a programmatic reload: model.load() (cog actions) and root.load() (form close, view-button reloads) rebuild the reactive root without calling notify(), which only fires on search/pager changes. The Time view gantt already worked around this because GanttModel.fetchData() calls notify() itself. Fix : - Subscribe payroll mixing to the "update" notif that will come from the underlaying models. - override the onRootLoad hook in payslipListCOntroller to call notify() which sends an "update" message when loading the data.(added a guarderail: only call the notify on the model if it's mounted) task - 6387933 Forward-Port-Of: odoo/enterprise#125194
The barcode flow now correctly blocks scanning products that were not reserved when extra products are not allowed, even after leaving and reopening a transfer. It also restores the ability to add products in immediate delivery transfers where that action is still valid, reducing inventory processing errors and workflow interruptions.
Original PR description
This [PR] made sure it was not possible to scan unreserved products when `allow_extra_product` was disabled, even when exiting and re-entering a transfer. It worked under the assumption that an immediate transfer always stays in draft, which is wrong for deliveries. The 2nd commit of this PR partly address this issue by allowing the user to add multiple products with the "Add Product" button when the transfer is immediate. While working on this issue, we encountered a bug in the scanning prevention that should have been caught by a tour but was not. This is fixed in the 1st commit. More details in the commit messages. [PR]: https://github.com/odoo/enterprise/pull/123793 Forward-Port-Of: odoo/enterprise#125906 Forward-Port-Of: odoo/enterprise#125313
HR Gantt views now handle employee grouping and search filters more consistently, so employees without related records can appear correctly when relevant. The change also prevents a team filter meant for the Time Off overview from showing in other HR planning views.
Original PR description
Every gantt that needed to display employees without records (without leaves, attendances, etc.) would implement their own `_get_gantt_data_group_by_employee()` function. They all do the exact same…
Every gantt that needed to display employees without records (without leaves, attendances, etc.) would implement their own `_get_gantt_data_group_by_employee()` function. They all do the exact same thing. This PR creates the reusable function `_get_gantt_data_with_empty()`, so that all the `_get_gantt_data()` functions can reuse that one. That function will guess the relation model from the `groupby` variable. `user_domain` (in the context) is supposed to contain the domain defined by the user (in the search bar). It was not the case for all gantt views, and produced inconsistent results, as that domain is used to know when to display the employees without leaves/attendances. The PR fixes that issue by creating the `HrGanttModel` class, that takes care of defining the `user_domain` correctly. This class also disables the *Group By* menu, and defaults to grouping by employees. This was decided for the following reasons: - All gantt views inheriting this class would group by employee - Grouping by other fields would already not work in some cases - It's very difficult to add employees without records if the gantt is grouped by multiple fields at once task-5502544
DHL Express deliveries sent through EasyPost no longer fail validation when an international order is split into multiple packages. The system now treats DHL's multi-package rate notice as informational while still blocking genuine shipping errors.
Original PR description
Steps to reproduce --- 1. Configure a `delivery_easypost` shipping method using the DHL Express carrier. 2. On an international delivery, use Put in Pack to create two or more packages. 3. Validate…
Steps to reproduce --- 1. Configure a `delivery_easypost` shipping method using the DHL Express carrier. 2. On an international delivery, use Put in Pack to create two or more packages. 3. Validate the delivery. Issue --- Validation is blocked with `DHLExpress: rate_error -- DHLExpress multi-shipment rate includes this shipment.` For an EasyPost multi-shipment order, DHL Express returns the aggregate rate on the first (master) shipment only and adds an informational `rate_error` on the order stating that this rate already covers the whole order, so the remaining shipments carry no rate of their own. The message is not a real error, but `send_shipping` raises on any carrier message whose type is not exempt: https://github.com/odoo/enterprise/blob/534b42def8ae5dc884da4398377f828c49557d6e/delivery_easypost/models/easypost_request.py#L359-L364 `_post_process_ship_response` already detects and clears exactly this harmless message, but only for a hardcoded carrier allowlist introduced in a2de5bc5a14 (`Purolator`, `DPD UK`, `UPS`) that was never extended to DHL Express, so for DHL Express the message survives, reaches the raise, and aborts an otherwise valid shipment: https://github.com/odoo/enterprise/blob/a2de5bc5a14f99b8674c3ee234e4d221b406da0f/delivery_easypost/models/easypost_request.py#L416-L434 `DHL Express` is added to that allowlist so the harmless multi-shipment `rate_error` is posted on the picking and cleared instead of raised. The guard still requires a single `rate_error` carrying the "multi-shipment rate includes this shipment." text with the rate present only on the master shipment, so genuine DHL Express errors keep blocking validation. opw-6450365 Forward-Port-Of: odoo/enterprise#127164
Online orders imported through UrbanPiper now calculate the per-item price correctly when taxes are included and customers order more than one unit. This prevents overstated order line prices and helps keep POS totals, tax amounts, and reporting accurate.
Original PR description
Steps to reproduce: --- - Configure a Point of Sale with UrbanPiper credentials. - Create a product priced at 100 with a 5% GST (Tax Included). - Sync the product with UrbanPiper. - Place an online order with a quantity greater than 1. Issue: --- - `total_with_tax` was incorrectly treated as the unit price for multi-quantity tax-included orders. Fix: --- - Calculate the unit price by dividing `total_with_tax` by the ordered quantity before creating the POS order line. task-6427634 Forward-Port-Of: odoo/enterprise#126940 Forward-Port-Of: odoo/enterprise#125989
Belgian payroll now automatically applies the legal minimum employee deduction for meal vouchers when a lower amount is configured. HR managers receive a non-blocking payslip warning so they know the amount was adjusted for compliance.
Original PR description
**What:** - Refactored the meal voucher salary rule computation to take the maximum between the configured employee share and the parameter-defined minimum threshold (€1.09). - Added a non-blocking warning message on the payslip to notify HR managers when an employee's configured share is below the legal minimum and has been automatically adjusted. task-6428585 Forward-Port-Of: odoo/enterprise#127444 Forward-Port-Of: odoo/enterprise#126194
Code cleanup and technical improvements
Time zone settings are being moved from individual work schedules to the company level. This simplifies configuration and helps keep attendance, leave, payroll, planning, project, and maintenance calculations consistent across business processes.
Original PR description
This commit removes the timezone field on resource_calendar and adds a new timezone field on the company. task-5124688
The pricing label management features have been moved into their own Pricer app so they can be reused beyond Point of Sale, such as in Inventory. This also makes Pricer store and tag management accessible from the Inventory app, supporting broader operational use.
Original PR description
We extract `pricer` logic from `pos_pricer` to later use pricer in other modules (e.g. `stock`). task-6377740
9 changes
Resolved issues and error corrections
Peruvian invoices no longer get stuck when SUNAT has received them but the confirmation file is not ready yet. Odoo now keeps retrying automatically until the confirmation becomes available, reducing manual follow-up and delays in invoice processing.
Original PR description
Steps to reproduce: - Post a Peruvian invoice so it is sent to SUNAT (directly or through Estela/Digiflow). - SUNAT's sendBill call hangs and Odoo's request times out (ReadTimeout / ConnectionError),…
Steps to reproduce: - Post a Peruvian invoice so it is sent to SUNAT (directly or through Estela/Digiflow). - SUNAT's sendBill call hangs and Odoo's request times out (ReadTimeout / ConnectionError), even though SUNAT actually finishes registering the document on its side a moment later. - Odoo retries sending the same invoice (either automatically through the EDI cron, or manually). SUNAT now replies with a "document already exists" SOAP fault (code 1033/4000), since it processed the previous attempt. - Odoo tries to recover from this by fetching the CDR through getStatusCdr, but SUNAT has not finished generating it yet, so the lookup also fails. Cause of the issue: _l10n_pe_edi_post_invoice_web_service() already has recovery logic for error codes 1033/4000: it calls _l10n_pe_edi_retrieve_cdr() to fetch the CDR and treat the invoice as sent. But when that lookup itself fails (CDR not generated yet), the resulting error keeps the 'blocking_level' set to 'error' from the original SOAP fault. Documents with blocking_level 'error' are excluded from the automatic EDI cron retries (see account.edi.document._cron_process_documents_web_services), so the invoice gets stuck needing a manual retry, which can lose the same race against SUNAT again and again. Solution: When the CDR can't be retrieved yet after a 1033/4000 duplicate error, mark the result as 'blocking_level': 'warning' instead of leaving it at 'error'. This keeps the invoice eligible for the automatic EDI cron retries, so Odoo keeps polling SUNAT until the CDR becomes available, instead of requiring manual intervention every time this race is lost. opw-6393231
UrbanPiper delivery orders can now be accepted reliably even when the same print request is triggered twice at nearly the same time. Instead of causing an error, duplicate preparation ticket requests are safely ignored, reducing intermittent failures in point-of-sale order flows.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
Fixes an issue where editing a worksheet design template could fail for companies sharing upgraded worksheet data. Businesses can now customize worksheet templates in Studio without encountering an error caused by duplicate company-specific records.
Original PR description
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field. For example, in v17, a single worksheet template linked to 3 companies via the m2m field…
Since `company_id` on `worksheet.template` changed due to this a973d7d from a Many2many to a Many2one field.
For example, in v17, a single worksheet template linked to 3 companies via the m2m field was returned as 1 record when opening Design Template. After the upgrade in v18, company_id became m2o, and the same data is split into 3 separate records (one per company).
When trying to add a customization via Studio, the search [fetches](https://github.com/odoo/enterprise/blob/18.0/worksheet/controllers/main.py#L12) records based on the model set on the worksheet. In the new version, Studio
[creates](https://github.com/odoo/enterprise/blob/18.0/worksheet/models/worksheet_template.py#L112)
a new model, but for existing records the
model is the same across the 3 worksheet records tied to the same template. This causes the search to match all 3 records and raise a SingletonError.
```py
Traceback (most recent call last):
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2856, in __call__
response = request._serve_db()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2331, in _serve_db
raise self._update_served_exception(exc)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2329, in _serve_db
return service_model.retrying(serve_func, env=self.env)
File "/home/odoo/src/odoo/19.0/odoo/service/model.py", line 188, in retrying
result = func()
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2384, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 2599, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "/home/odoo/src/odoo/19.0/odoo/addons/base/models/ir_http.py", line 353, in _dispatch
result = endpoint(**request.params)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/industry_fsm_report/controllers/main.py", line 9, in edit_view
action = super().edit_view(view_id, studio_view_arch, operations, model, context)
File "/home/odoo/src/odoo/19.0/odoo/http.py", line 838, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "/home/odoo/src/enterprise/19.0/worksheet/controllers/main.py", line 17, in edit_view
worksheet_template_to_change._generate_qweb_report_template()
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 490, in _generate_qweb_report_template
new_arch = self._get_qweb_arch(worksheet_template.model_id, report_name, form_view_id)
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 460, in _get_qweb_arch
if 'name' in row_node.attrib and row_node.attrib['name'] not in self._get_qweb_arch_omitted_fields() and row_node.attrib['name'] in form_view_fields:
File "/home/odoo/src/enterprise/19.0/worksheet/models/worksheet_template.py", line 378, in _get_qweb_arch_omitted_fields
'x_%s_id' % self.res_model.replace('.', '_'), 'x_name', # redundant
File "/home/odoo/src/odoo/19.0/odoo/orm/fields.py", line 1657, in __get__
record.ensure_one()
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5942, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: worksheet.template(3, 14, 18)
```
OPW: 6389190
Forward-Port-Of: odoo/enterprise#126773Odoo now recognizes five new response codes introduced by Chile's SII for supplier electronic tax documents. This prevents affected documents from getting stuck during processing and keeps the acceptance or claim workflow aligned with the latest regulation.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
The UK CIS report now correctly includes payment information for receipts that use CIS tax, matching the behavior already available for vendor bills. This helps businesses get a complete and accurate CIS view across both bills and receipts.
Original PR description
With the l10n_uk_reports_cis module installed: - Create a vendor bills and add a CIS tax --> This vendor's bills appear correctly in the report. - Create a receipt and add a CIS tax --> This type of bill appears in the report, but the payment is not showing up. opw-6282548 Forward-Port-Of: odoo/enterprise#124043
This fix ensures Belgian payroll calculations handle employees working on two-week calendars correctly. It helps prevent incorrect payroll-related amounts for affected employees and adds test coverage for this scenario.
This fix improves how Odoo detects fiscal country codes for Avalara tax settings, so relevant tax and address validation fields appear only when appropriate. It helps reduce incorrect or missing Avalara-related options after an underlying country-code logic change.
Original PR description
**Changes:** - Updated the logic for showing address validation in `res_partner.py` to handle fiscal country codes more robustly. - Modified visibility conditions for `is_avatax`, `avatax_category_id`, `avatax_unique_code`, `avalara_partner_code`, and `avalara_exemption_id` fields in XML views to correctly parse and check fiscal country codes. **Purpose:** These changes ensure that the application correctly identifies when to display certain fields based on the fiscal country codes, enhancing the accuracy of the Avatax integration. This is made necessary because of changes to the _compute_fiscal_country_codes method introduced in commit https://github.com/odoo/odoo/commit/c518589716ebde6fb418d907ee01799dd7b889e9.
Refunding Ecuador POS orders made with the "Consumidor Final" customer now shows the intended error message instead of crashing. This prevents a confusing checkout failure and helps staff understand why the refund cannot proceed.
Original PR description
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to…
When attempting to refund orders that were created with the "Consumidor Final" customer. The refund validation would crash with a TypeError instead of showing the proper error message. Steps to reproduce: ------------------- In POS with l10n_ec_edi module activated: * Create a new order with "Consumidor Final" as customer * Add products and pay the order * Validate the order * Attempt to refund this order > Observation: The refund validation would crash with: TypeError: Cannot read properties of undefined (reading 'add') at OrderPaymentValidation.isOrderValid Why the fix: ------------ The code was trying to access `this.dialog` which is undefined in the OrderPaymentValidation class context. The dialog service should be accessed via `this.pos.dialog`, which is the correct pattern used throughout the base OrderPaymentValidation class. This fix ensures the error dialog is properly displayed when attempting to refund orders for the anonymous final consumer, instead of crashing with a TypeError. opw-6427113
Accounting users in Spanish companies can now export VAT record books that include Point of Sale data without needing POS access rights. The report safely reads the necessary POS information internally, avoiding access errors during tax reporting.
Original PR description
Steps to reproduce:
- With an ES Company
- Open a POS session, add product with tax and pay
- As a user with only accounting access
- Go to Accouting > Reporting > Tax report
- Select Generic Tax report
- Print "VAT record Books"
Issue:
An AccessError will raise
```
Access Error
You are not allowed to access 'Point of Sale Session' (pos.session) records.
This operation is allowed for the following groups:
- Point of Sale/User
Contact your administrator to request access if necessary.
```
Analysis:
Vat Record Books handler for POS needs to read pos.session and pos.order records. Currently, the action is performed with the rights of the user running the report, so accounting-only user face an error.
As POS records are only read internally to build the report, we add sudo call to get the data.
opw-5862529
Forward-Port-Of: odoo/enterprise#126254
Forward-Port-Of: odoo/enterprise#1259805 changes
Resolved issues and error corrections
Fixed an issue in Documents where clicking inside the “Search More” selection dialog could unexpectedly close it while editing document details. Users can now sort, resize columns, and select contacts or related records without losing the dialog or their current document selection.
Original PR description
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the…
Steps to reproduce: 1. Install Documents 2. In the Documents list view, select a document to display the inspector. 3. Edit a field such as Owner or Customer which uses a Many2one widget. 4. In the field dropdown, click "Search More..." to open a modal dialog. 5. Click inside the "Search More..." modal (e.g., to sort columns or resize headers). Issue: - The modal dialog immediately closes, and the contact cannot be selected. Root cause: - When an inspector field is edited, the record row is put into edit mode. While in edit mode, the documents list renderer listens for global clicks. Clicking inside the "Search More..." modal dialog targets elements that have `.o_list_renderer` (since the modal dialog renders a list view). Because the click target is within a list renderer but is not a document row, `DocumentsListRenderer.onGlobalClick` executes and clears the selection of the main list view. Clearing the selection unmounts the edited field in the inspector, thereby destroying the modal dialog stack. Solution: - Modify DocumentsListRenderer.onGlobalClick to scope click handling to the current Documents list renderer. Ignore clicks outside this.root.el, so interactions in nested UI such as Search More... do not clear the main selection and destroy the inspector field. opw-6253360 Forward-Port-Of: odoo/enterprise#119262
Belgian Acerta payroll exports now include weekend days when an eligible leave period, such as sick leave, overlaps a weekend. This helps ensure reports match Acerta requirements and avoids missing leave information in payroll submissions.
Original PR description
## Steps to reproduce: - Install l10n_be_hr_payroll_acerta - Create an employee in a belgian company - Create a sick time off for the created employee that overlaps with a weekend - Export acerta report for the employee - Notice the weekend that overlaps with the time off is not present in the report ## Cause: While exporting the report file we only loop over the created work entries' dates and since weekends doesn't have work entries we don't consider them in the report. ## Fix: When generating the line of a leave's start date we check if the leave overlaps with a WE, we fetch the WE's date and we generate a line for each day of the WE. According to Acerta this is the correct behavior for their reports for specific types of leaves. **opw-6313534**
Odoo now recognizes five new response codes introduced by Chile's tax authority for supplier electronic documents. This prevents affected documents from getting stuck during processing and keeps the workflow aligned with the latest SII requirements.
Original PR description
**Before this PR:** After the implementation of Resolution 161 of November 13th 2025, SII responses included keys not supported by the current l10n_cl_edi implementation. This resulted in supplier DTEs not being processed as they were before the change, because the five new keys were not found in Odoo's current `l10n_cl_claim` field, causing that the documents with these responses, were kept in a loop not solved. **After this PR:** The five new values from the resolution, along with their translations, were added to the selector field, fixing the process flow. **SII Reference:** https://www.sii.cl/normativa_legislacion/resoluciones/2025/reso161.pdf (see Event Code, page 5) Forward-Port-Of: odoo/enterprise#121833
This fix ensures Mexican electronic payment documents use the required six-decimal precision for tax amounts. It prevents valid customer payments from being rejected due to small rounding mismatches in CFDI validation.
Original PR description
**Steps to reproduce:** - Checkout the source code before this commit: https://github.com/odoo/enterprise/commit/57e999b6a0d0639a09ee3518045669b3258eeb8f - Install Accounting and l10n_mx_edi -…
**Steps to reproduce:** - Checkout the source code before this commit: https://github.com/odoo/enterprise/commit/57e999b6a0d0639a09ee3518045669b3258eeb8f - Install Accounting and l10n_mx_edi - Checkout the source code after the commit - Switch to a Mexican company (e.g. ESCUELA KEMPER URGATE) - Make sure that "Outstanding Receipts accounts" are set on the Bank journal - Create an invoice: * Customer: [any] (e.g. Acme Corporationà * Payment Way: Efectivo * Payment Terms: [10 Days after End of Next Month] (in order to set "Payment Policy" to "PPD") * Product: [any] * Quantity: 1.0 * Price: 295.17 * Taxes: 16% - Confirm the invoice - Send CFDI - Register payment - "Upate Payments" from the invoice **Issue:** The payment CFDI is in error: `Code : CRP20274 Message : El campo ImporteP que corresponde a Traslado, no es igual a la suma de los importes de los impuestos registrados en el documento relacionado donde el impuesto del documento relacionado sea igual al campo ImpuestoP de este elemento y la TasaOCuotaP del documento relacionado sea igual al campo TasaOCuotaP de este elemento.` **Cause:** This commit https://github.com/odoo/enterprise/commit/57e999b6a0d0639a09ee3518045669b3258eeb8f introduces 6 decimals for the payment CFDI taxes. However, if `l10n_mx_edi` module is not upgraded, the required changes in `l10n_mx_edi.payment20` template won't be applied and the amounts will not sum up correctly. opw-646062
The Dutch tax reporting status check now handles cases where a tax return record is missing its closing entry. This prevents one incomplete or broken record from stopping status updates for all other Digipoort tax returns.
Original PR description
The `l10n_nl_reports_sbr_status_info` contains the `l10n_nl_reports_sbr.status.service` class. The class is responsible for fetching the status of sent Digipoort tax returns. The status is then posted as a chatter message to the tax return's closing entry. Issues can arise when one of the status service records is, for whatever reason, missing a closing entry. In such case, the message cannot be posted, resulting in an exception being raised. Since the records are processed in a loop without a try-catch, this causes the whole action to fail. This can lead to one broken record effectively shutting down the whole module's functionality. This PR adds some if-else checks to gracefully handle the case where the closing entry is missing. Related tickets: opw-5901446 and opw-6410082 Forward-Port-Of: odoo/enterprise#125996
1 change
Enhancements to existing features
The signing app now limits changes to a signer's email address so only the person who created the signing request can make them. This helps prevent unintended or unauthorized changes to who receives or signs a document, improving trust in the signing process.