Daily updates from Odoo
Tuesday, August 11, 2026
235 changes
12 changes
Resolved issues and error corrections
The payroll off-cycle action now works when users select several payslips at once from the list view. This prevents an error screen and lets payroll teams process multiple payslips more smoothly.
Original PR description
Steps to reproduce: - Open payslip list view - Select multiple payslips - Select "Send to Off-cycle" action Issue: A traceback occurs with ValueError: Expected singleton when the action is executed on multiple payslips. Reason: - 'action_move_to_off_cycle()' assumes a singleton and directly accesses self.version_id.id - When multiple payslips are selected from list view then self contains several payslips and self.version_id returns multiple versions, causing the singleton error. Solution: - Iterate over each payslip individually when processing the off-cycle action and use the corresponding version for each record, allowing the action to work correctly in multi-record mode task-6320059
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.
Bank statement matching now uses an existing optimized database index when looking for unreconciled accounting entries. This should improve performance for related accounting workflows without changing user-facing behavior.
Original PR description
We have a very efficient index for searching unreconciled lines on known accounts. Let's use it.
```python
_unreconciled_index = models.Index("(account_id, partner_id) WHERE reconciled IS NOT TRUE")
```
Before this change, the query planner didn't recognize the index because of its definition being slightly different wrt the null values.
Forward-Port-Of: odoo/enterprise#127093This 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
A restaurant appointment test now properly closes an edited form before finishing. This prevents random test failures and helps keep future updates to the restaurant appointment feature stable.
Original PR description
The `test_appointment_kanban_view` tour test was randomly failing with the following error: `AssertionError: Tour finished with a dirty form view being open.` This occurred because the tour ended right after clearing a date field on a form, leaving the form in a "dirty" (unsaved changes) state. This commit fixes the issue by adding a final step to the tour that clicks the cancel/discard button. Forward-Port-Of: odoo/enterprise#109633
Fixed an issue where opening Bank Matching from a return could fail when no bank journal was selected. This prevents an error screen and lets users continue reconciliation from the working file flow.
Original PR description
When accessing the reconciliation widget from the working file check, there is no journal to be selected, hence no journal in the context. This was tracebacking since we were trying to send a read query to the server with an undefined id. To reproduce: * create a bank statement line without reconciling * set up the return on the misc journal * open the return, then "Bank Matching" Forward-Port-Of: odoo/enterprise#127421
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
The “My Team” default filter now appears only on the Time Off Overview Gantt page where it is intended. This prevents employees and managers from seeing an unexpected default filter on other HR Gantt views, reducing confusion and keeping each view’s results accurate.
Original PR description
A default filter would sometimes appear on all the HR Gantt views. That is not supposed to happen as this filter is only supposed to appear on the Time Off > Overview gantt page (`hr.leave.report.calendar`) This was because the SearchModel that was supposed to be applied only for `hr.leave.report.calendar` was used for the `hrHolidaysGanttView` and `time_off_report_calendar`. This PR makes it so that the SearchModel is only applied to the required gantt view and not all of them. task-5502544
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
This fixes an internal automated test so it correctly accounts for archived call activity records before removing activity types. The change helps prevent false test failures and improves confidence in the mail activity and VoIP-related test suite without changing customer-facing behavior.
Original PR description
test_create_call_activity attempts to delete any 'phonecall' activity types. This is so it can test the functionality of create_call_activity when there is no existing 'phonecall' activity type. However, demo data inside voip creates an archived activity with the type mail_activity_data_call. https://github.com/odoo/enterprise/blob/cc4456ac16aeee59830db08505a27877e149f3a7/voip/demo/voip_call.xml#L4-L13 When searching for phonecall_activities the test only gets active activities. We need to alter this to fetch all. Otherwise, the test will then try to delete the activity type when there are still records using the type. opw-6443475 runbot-242850 Forward-Port-Of: odoo/enterprise#126540
17 changes
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
Bank statement processing now uses the intended database shortcut when searching for unreconciled accounting lines. This should improve performance in reconciliation workflows without changing user-facing behavior.
Original PR description
We have a very efficient index for searching unreconciled lines on known accounts. Let's use it.
```python
_unreconciled_index = models.Index("(account_id, partner_id) WHERE reconciled IS NOT TRUE")
```
Before this change, the query planner didn't recognize the index because of its definition being slightly different wrt the null values.
Forward-Port-Of: odoo/enterprise#127093This 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
A test was corrected so it also considers archived call activity records before deleting a related activity type. This prevents false test failures caused by demo data and improves reliability of the enterprise test suite without changing user-facing behavior.
Original PR description
test_create_call_activity attempts to delete any 'phonecall' activity types. This is so it can test the functionality of create_call_activity when there is no existing 'phonecall' activity type. However, demo data inside voip creates an archived activity with the type mail_activity_data_call. https://github.com/odoo/enterprise/blob/cc4456ac16aeee59830db08505a27877e149f3a7/voip/demo/voip_call.xml#L4-L13 When searching for phonecall_activities the test only gets active activities. We need to alter this to fetch all. Otherwise, the test will then try to delete the activity type when there are still records using the type. opw-6443475 runbot-242850
This fix updates an automated payroll pay run test so it counts only rows containing actual data, rather than placeholder rows added by the interface. This prevents false test failures and helps keep payroll quality checks reliable without changing user-facing payroll behavior.
Original PR description
Issue: The original trigger was searching for 2 table rows, when it enforces 4 with added empty rows. The [getEmptyRowIds](https://github.com/odoo/odoo/blob/33dc65bbac165f33030ad3da59ea785b69482b3f/addons/web/static/src/views/list/list_renderer.js#L1104-L1110) enforces max of 4 rows. The condtional (one up the stack) !ctx["this"].props.list.isGrouped&&!ctx["this"].props.noContentHelp returns true, and it adds empty rows. Fix: Since this enforces 4 rows with empty rows we check the rows that have data instead of how many rows are added. Because anything less than or equal to 4 but greater than 0 records it will always be 4 table rows while the conditional above returns true . opw-6349513 <img width="1337" height="674" alt="Screenshot 2026-07-15 at 4 53 51 PM" src="https://github.com/user-attachments/assets/76f53521-ec9f-4807-9083-95533904b5de" />
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
Product images fetched through the Barcode Lookup service are now converted into the right format before being saved. This ensures newly created products display their images correctly, reducing manual cleanup for users.
Original PR description
Issue before this commit: ========================= When creating a product by adding a barcode using the Barcode Lookup service, the product image was fetched properly but was not displayed in the…
Issue before this commit: ========================= When creating a product by adding a barcode using the Barcode Lookup service, the product image was fetched properly but was not displayed in the UI. Steps to Reproduce: ========================= - Install the stock module. - Enable the Stock Barcode Database. - Add a valid API key for the Barcode Database. - Create a product by adding a barcode available in the Barcode Lookup API. - Notice that the product image is not displayed. Cause of the issue: ========================= This issue was introduced by this [PR](https://github.com/odoo/odoo/pull/244421), which improved the behaviour of **fields.Binary**. As a result, the image returned by the Barcode Lookup API was not converted to the expected binary format, preventing it from being displayed in the UI. With This Commit: ========================= With this commit, convert the fetched image to the expected binary format before assigning it to the product, ensuring that it is displayed correctly in the UI. opw-6434478
Users can now open Bank Matching from the working file check without encountering an error when no journal is preselected. This prevents a crash caused by looking up missing journal information and keeps the reconciliation flow accessible.
Original PR description
When accessing the reconciliation widget from the working file check, there is no journal to be selected, hence no journal in the context. This was tracebacking since we were trying to send a read query to the server with an undefined id. To reproduce: * create a bank statement line without reconciling * set up the return on the misc journal * open the return, then "Bank Matching" Forward-Port-Of: odoo/enterprise#127421
This fixes an issue in the Swiss payroll integration where today's date could be calculated without the right record context. It helps prevent errors during payroll-related transmissions and keeps processing reliable.
Original PR description
Fix https://github.com/odoo/enterprise/pull/126718 Forward-Port-Of: odoo/enterprise#127535
The Timesheets Assistant now ignores the employee's own email address when matching Gmail messages to customers. This prevents unrelated tasks or projects from being suggested simply because the current user appears as a recipient, improving the relevance of timesheet suggestions.
Original PR description
Before this commit, the Timesheets Assistant resolved every address found in a read or composed email to a partner, then matched the event to a task or project having that partner as its customer. The current user is a recipient of every email they receive, so their own address is present in the "To" or "Cc" fields of every `reading_email` event. As a result, any task whose customer was the current user could be suggested for those emails. This commit excludes the current user's partner from that lookup. task-6438374 Forward-Port-Of: odoo/enterprise#127422 Forward-Port-Of: odoo/enterprise#126448
6 changes
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 error that occurred when saving an employee declaration without selecting an employee. Payroll users can now create or edit these records without being interrupted by a technical traceback.
Original PR description
When creating an employee declaration without selecting an employee, a traceback occurs. Steps to reproduce the error: - Install ``l10n_be_hr_payroll`` module with demo data - Switch to Belgian company - Go to Payroll > Reporting > Individual Accounts > Create a new Individual Account > Click on Eligible Employees > Create a new employee declaration without employee > Save Traceback: ```py ValueError: Expected singleton: hr.employee() ``` https://github.com/odoo/enterprise/blob/000544c3d5b93e194264e15bb73d9599525106e3/hr_payroll/models/hr_payroll_employee_declaration.py#L71 The ``_compute_version_id()`` method calls ``_get_version()``. When ``employee_id`` is empty, ``_get_version()`` is invoked on an empty ``hr.employee`` record, and its ``ensure_one()`` call raises the above traceback at [1]. [1]: https://github.com/odoo/odoo/blob/3c358ae2badad69b125695a97b4a14e8ab77fccd/addons/hr/models/hr_employee.py#L745-L750 sentry-7625826444
Bank statement reconciliation now uses an existing optimized lookup when searching for unreconciled accounting lines. This helps improve performance in accounting workflows without changing user-facing behavior.
Original PR description
We have a very efficient index for searching unreconciled lines on known accounts. Let's use it.
```python
_unreconciled_index = models.Index("(account_id, partner_id) WHERE reconciled IS NOT TRUE")
```
Before this change, the query planner didn't recognize the index because of its definition being slightly different wrt the null values.
Forward-Port-Of: odoo/enterprise#127093The Timesheets Assistant no longer treats the user's own email address as a customer match when analyzing Gmail messages. This prevents irrelevant task or project suggestions for emails where the user appears only because they received the message.
Original PR description
Before this commit, the Timesheets Assistant resolved every address found in a read or composed email to a partner, then matched the event to a task or project having that partner as its customer. The current user is a recipient of every email they receive, so their own address is present in the "To" or "Cc" fields of every `reading_email` event. As a result, any task whose customer was the current user could be suggested for those emails. This commit excludes the current user's partner from that lookup. task-6438374 Forward-Port-Of: odoo/enterprise#126448
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
7 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
This fix helps Odoo use an existing optimized database index when finding unreconciled accounting lines for known accounts. It should improve performance for related bank statement and reconciliation workflows without changing user-facing behavior.
Original PR description
We have a very efficient index for searching unreconciled lines on known accounts. Let's use it.
```python
_unreconciled_index = models.Index("(account_id, partner_id) WHERE reconciled IS NOT TRUE")
```
Before this change, the query planner didn't recognize the index because of its definition being slightly different wrt the null values.
Forward-Port-Of: odoo/enterprise#127093This fixes a crash that could happen when users opened Bank Matching from a working file without a selected journal. The reconciliation screen now avoids sending an invalid request, helping users continue bank statement follow-up without interruption.
Original PR description
When accessing the reconciliation widget from the working file check, there is no journal to be selected, hence no journal in the context. This was tracebacking since we were trying to send a read query to the server with an undefined id. To reproduce: * create a bank statement line without reconciling * set up the return on the misc journal * open the return, then "Bank Matching" Forward-Port-Of: odoo/enterprise#127421
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#126156This update corrects an internal date lookup used by Swiss payroll transmission processing. It helps prevent errors in payroll-related workflows that depend on the current date being calculated correctly.
Original PR description
Fix https://github.com/odoo/enterprise/pull/126718
Rental 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
The Timesheets Assistant now correctly identifies Gmail recipient email addresses when the recipient name includes parentheses. This prevents misread email addresses and helps ensure timesheet-related assistance works reliably for affected users.
Original PR description
Before this commit, the Timesheets Assistant extracted the address of a Gmail recipient with a regex capturing everything between the first opening and the last closing parenthesis. When the display name itself contains parentheses, as in `"Maan Patel (maap) (maap@odoo.com)"`, the regex captured `"maap) (maap@odoo.com"` instead of the address. This commit extracts the address from the last pair of parentheses instead. task-6438374 Note: This PR only needs to be merged into `saas-19.1`. From `saas-19.2` onwards, it will be fixed by task 6073770.
7 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#126773The UK CIS report now correctly shows payments linked to receipts that include CIS taxes. This fixes a reporting gap so businesses have a more complete view of CIS activity across both vendor 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
Fixes 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#126156Tests were moved to the module that owns the optional no-follow-up setting. This helps ensure customers who do not use that optional module can still generate follow-up invoice reports without errors.
Original PR description
`no_followup` is a field defined in `account_no_followup` that used in commit https://github.com/odoo-dev/enterprise/commit/2c1164bb9888f8dbc7d434a95b4be6d42c0f9143 in the module `account_followup`. This leads to issues where customers that don't have the module `account_no_followup` installed can't call `_get_invoices_to_print` without getting an error Fixed by https://github.com/odoo-dev/enterprise/commit/de73ef1d2b9a52fdfef4f5c5e28c7876e927a6b8 This commit moves the tests in the appropriate module opw-6402268 Forward-Port-Of: odoo/enterprise#127409 Forward-Port-Of: odoo/enterprise#125006
This fix prevents Swiss payroll settings from assigning a Swiss contract type to employees outside Switzerland. It avoids incorrect employee contract data and restores reliability for related automated checks.
Original PR description
[FIX] l10n_ch: fix default contract type This task is runbot error fix that occured from 19.0 to 19.2 Bug reproduction: 1 - Go to version 19.0, install l10n_ch_hr_payroll_account 2 - Execute…
[FIX] l10n_ch: fix default contract type
This task is runbot error fix that occured from 19.0 to 19.2
Bug reproduction:
1 - Go to version 19.0, install l10n_ch_hr_payroll_account 2 - Execute test_version_timeline_auto_save_tour tour test 3 - It fails in .o_arrow_button_wrapper[data-tooltip^='Contract:'] step
Bug cause:
1 - When l10n_ch_hr_payroll_account is installed:
1.1 - contract type becomes "Permanent contract with monthly salary"
1.2 - the employee is not swiss but it has this CH contract type
2 - data-tooltip starts with Permanent contract instead of contract
2.1 - Tour fails
3 - contract_type_id is overwritten in swiss modules
3.1 - Default is assigned without looking to the country of self.env
Bug solution:
1 - If the country is not swiss, the default is assigned as False
1.1 -> fixed in l10n_ch_hr_payroll/hr_version
1.2 instead of assigning swiss contract type to the non-swiss emp.
Note: This is fix from saas-18.4 to master.
task-6392040
runbot error: https://runbot.odoo.com/odoo/runbot.build.error/9413585 changes
Resolved issues and error corrections
Users with Accounting Read-Only access can now see the General section in the Accounting tab on contact records, including bank account details. This fixes a view setup issue that accidentally hid information those users were already allowed to access.
Original PR description
Problem: The General group of the Accounting tab of the partner form view is not visible to some users, even if they have the access rights to see it. Steps to reproduce: 1. Create a user or edit an existing one, giving them Accounting Read-Only access rights. 2. Log in with that user. 3. Go to Contacts and select a partner. 4. Open the Accounting tab 5. Notice how the General group (with the bank account details) is not visible. Cause: In the account_accountant module, the partner form view is inherited in one of the views to add additional groups to the General group of the Accounting tab. However, it doesn't add the new group, but instead replaces the existing groups with the new one. opw-6413683
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
27 changes
Resolved issues and error corrections
Overtime time-off entries in Belgian payroll will no longer be incorrectly marked as sickness relapses after a recent sick leave. The relapse check now only considers actual sickness leave types, improving payroll accuracy and reducing manual corrections.
Original PR description
Before this commit, "_compute_can_relapse" scoped its computation to any Belgian, HR-validated leave, regardless of its work entry type. As a result, creating an overtime time-off entry shortly after a validated sick leave would incorrectly trigger the "Sickness relapse" field on the overtime entry, since it fell within the legal relapse window of the previous sick leave. Add a check on `work_entry_type_id.code` to the initial filter so only actual sickness leaves (LEAVE110, LEAVE214, LEAVE280) are considered for the relapse computation. Task: 6415930
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 update corrects demo payroll data so Belgian employee records are linked to the right company instead of defaulting to a US company. It also ensures required languages and payroll warning checks are handled correctly, reducing access errors during demo payslip generation.
Original PR description
Task #6453043
Fixed an issue in Belgian payroll where voluntary overtime could fail when an employee had overtime across multiple work days. This helps payroll processing complete reliably and reduces manual corrections for affected payslips.
Original PR description
Forward-Port-Of: odoo/enterprise#127407 Forward-Port-Of: odoo/enterprise#127378
This 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
This fix prevents Philippine payroll validation from failing when older or test dates do not have a matching tax office rule parameter. Regular payroll use remains unchanged, while unusual historical data or automated tests can proceed without unnecessary errors.
Original PR description
While in regular usage the current solution works; the constrains may break if during tests and other flows where the date could be far in the past. To avoid such issues; we will not raise if the rule param is not found and instead simply return early. Runbot error 945678
Belgian payroll declarations without XML no longer trigger an online schema download during validation. This prevents database setup from failing on systems without internet access while keeping normal validation for declarations that do include XML.
Original PR description
Before this commit, the validation state compute downloaded the XSD from socialsecurity.be even for a declaration with no XML to validate, so the database initialisation crashed on a host without internet. The download came with odoo/enterprise#104908 and only shows up once the test_l10n_be_hr_payroll_account demo data is installed, since that demo creates a DMFA without any XML. After this commit, the schema is fetched only when there is an XML, and empty declarations are just marked as normal. task-6460394
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
The Belgian payroll configuration has been adjusted so a related action appears in the payroll configuration area instead of the general settings area. This helps payroll users find the relevant option in the expected place and reduces confusion during setup.
Original PR description
Task: 6461789
This fix ensures Indian payroll calculations are only applied to employees using the Indian localization. It prevents irrelevant Indian payroll fields from appearing in change logs for employees managed under other country rules, such as Belgium.
Original PR description
[FIX] l10n_in: fix some l10n_in fields computed for other localizations
Bug reproduction:
1 - in localhost install below modules:
→ l10n_in_hr_payroll,l10n_be_hr_payroll,l10n_be_hr_contract_salary
2 - Select BE, IN localizations, but as active one select Belgium. 3 - Go to employee, Laura.
4 - Change her wage to 8000
5 - In the chatter, you will see some indian fields are tracked.
→ Shouldn't be, Laura is Belgium, only BE fields should be tracked
Bug cause:
1 - In the hr.version of l10n_in_hr_payroll:
→ there isn't enough caution in compute methods for other l18ns.
→ e.g., _l10n_in_get_montly_wage returns self.wage
→ self.wage is 8000 and that function leads to positive computations
Bug solution:
1 - Non-indian versions are carefully handled in compute methods
task-6411960
Forward-Port-Of: odoo/enterprise#125350Payroll 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
System administrators can once again delete any equity transaction, even when they are not listed as the seller or subscriber. This restores the intended administrative control and prevents support issues caused by blocked cleanup or correction tasks.
Original PR description
Before this PR https://github.com/odoo/enterprise/pull/120158 system admin had the access to delete any transaction (which was intended). The PR however made admins no longer able to delete a transaction that they don't belong to as seller or subscriber. This PR fixes this issue by allowing admins to delete any transaction. opw-6413350 Forward-Port-Of: odoo/enterprise#126672
Bank reconciliation now safely handles imported statement lines whose payment reference contains only spaces. This prevents an unexpected error when setting accounts and keeps reconciliation workflows running smoothly even with imperfect imported data.
Original PR description
When reconciling bank statements with an account, the system will look for past statement lines already reconciled with that account and create a reconciliation model based on common substring in…
When reconciling bank statements with an account, the system will look for past statement lines already reconciled with that account and create a reconciliation model based on common substring in payment_ref. If this payment refs contains only spaces (eg. ' '), it will trigger an index out of range traceback. This is explained by the fact that spaces are striped then '' is considered as False in some filtering leaving the list empty. From the UI, putting ' ' is not supposed to be possible because spaces are striped before write but there is many ways to import statement lines which may lead to this hence the decision of handling this scenario to make the code more robust. Steps to reproduce: 1/ Create two statement lines with payment_ref as ' ' (you can force it using a write) 2/ Click "Set account" on first one and pick 100000 Issued Capital 3/ Do the same for the second statement line => Traceback In this commit, we do not check for common substring if there is less than two labels. opw-6379977 Forward-Port-Of: odoo/enterprise#126729 Forward-Port-Of: odoo/enterprise#125779
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
This fix makes an automated product merge test consistently choose the intended main product before merging. It prevents random test failures caused by unreliable creation-date ordering, improving build stability without changing customer-facing behavior.
Original PR description
Version: - saas-19.4 Steps to reproduce: - Run the test_merge_success_single_variant test case multiple times with different products created each time. Issue: The master record is selected based on its creation date, but that ordering is not always reliable. As a result, the wrong product may be chosen as the master,causing the incorrect product to be archived and the runbot test to fail. Fix: Before merging, we now manually set product 1 as the master. This makes the test predictable and stops the failures. Build error - 941476 Forward-Port-Of: odoo/enterprise#125180
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
This fix clears cached routing information so barcode-related test overrides are correctly applied when larger test suites run. It helps prevent false test failures in inventory and picking barcode workflows, improving release stability without changing day-to-day user behavior.
Original PR description
test_barcode_create_serials_in_batch_with_single_scan keeps failing on master and 19.4 as call_count !=2, but instead = 0 This does not happen when running only the tests in stock_barcode but instead when you run a larger suite of tests such as the following: https://runbot.odoo.com/runbot/build/119533820 Once a route is hit it is stored in the cache, causing the later override to be missed. [Runbot-238760](https://runbot.odoo.com/odoo/error/238760) Forward-Port-Of: odoo/enterprise#126122
The accounting reconciliation process now uses an existing optimized database lookup more reliably. This should improve performance when matching unreconciled accounting entries, especially for larger accounting datasets, without changing user-facing workflows.
Original PR description
We have a very efficient index for searching unreconciled lines on known accounts. Let's use it.
```python
_unreconciled_index = models.Index("(account_id, partner_id) WHERE reconciled IS NOT TRUE")
```
Before this change, the query planner didn't recognize the index because of its definition being slightly different wrt the null values.
Forward-Port-Of: odoo/enterprise#127093This fix prevents a restaurant appointment test from ending with unsaved changes left open. It makes automated checks more reliable and reduces random test failures without changing customer-facing behavior.
Original PR description
The `test_appointment_kanban_view` tour test was randomly failing with the following error: `AssertionError: Tour finished with a dirty form view being open.` This occurred because the tour ended right after clearing a date field on a form, leaving the form in a "dirty" (unsaved changes) state. This commit fixes the issue by adding a final step to the tour that clicks the cancel/discard button. Forward-Port-Of: odoo/enterprise#109633
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
12 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
Opening Bank Matching from a working file could fail when no bank journal was selected. This fix prevents that error, allowing users to continue reconciliation from that flow without interruption.
Original PR description
When accessing the reconciliation widget from the working file check, there is no journal to be selected, hence no journal in the context. This was tracebacking since we were trying to send a read query to the server with an undefined id. To reproduce: * create a bank statement line without reconciling * set up the return on the misc journal * open the return, then "Bank Matching"
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 payroll process now correctly checks only payslips that actually have issues before gathering issue details. This prevents incorrect results when a pay run includes both problematic and clean payslips, helping payroll teams review exceptions reliably.
Original PR description
Commit e4e573e1219f38561ae74e2086cee6d54cda6f46 introduced a bug. Steps to reproduce: - Generate a payrun with at least two payslips: one with an issue and another without any issue This commit fixes the issue by filtering the issues payslips before getting their issues. task-5230921
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.
Payroll now correctly finds work entries after a related date-handling change. This helps ensure payslips use the right work entry records and reduces the risk of incorrect payroll calculations.
Original PR description
Since this PR: https://github.com/odoo/odoo/pull/216234, work entries have only a date. This commit fixes the domain.
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#1259807 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 prevents a system error when an Australian Single Touch Payroll submission is attempted without any payslips or employees. Instead, users receive a clear validation message so they can correct the missing payroll data before submitting to the ATO.
Original PR description
When submitting payroll data to the ATO from an STP record without any payslips or employees, a traceback is raised. Steps to reproduce the error: - Install ``l10n_au_hr_payroll_account`` module -…
When submitting payroll data to the ATO from an STP record without any payslips or employees, a traceback is raised. Steps to reproduce the error: - Install ``l10n_au_hr_payroll_account`` module - Switch to ``My australian Company`` - Go to Payroll > Configuration > Settings > In Australian Localization, Set BMS ID > Set STP Responsible and his date of birth - Go to Payroll > Reporting > Single Touch Payroll > Create a new record > Set Payment Date > Submit to ATO > Sign & Submit to ATO Traceback: ```py IndexError: tuple index out of range ``` https://github.com/odoo/enterprise/blob/b0e48baaf99bdc4faefd2ffdd3bd5637fb548593/l10n_au_hr_payroll_account/models/l10n_au_stp.py#L228-L233 The traceback occurs because ``_get_fiscal_year_start()`` assumes that the STP record always contains at least one payslip or one employee. When these recordsets are empty, indexing the first element raises an IndexError. Solution: This commit validates that the required payslips or employees are present before the submission and raises a validation error instead of a traceback.
The French VAT reimbursement declaration now includes the bank account holder's name, which is required for form 3519. This helps businesses submit complete declarations and reduces the risk of rejection or follow-up requests.
Original PR description
For reimbursement declarations, the name of the holder of the account is required This commits adds holder's name to the account data zone no-task-id
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