Tuesday, August 11, 2026
12 changes · saas-19.3
Enhancements to existing features
All signers can now choose whether to add a frame when adopting their signature, not just internal Odoo users. The signing dialog also has clearer borders around key inputs and buttons, making the public signing experience easier to use.
Original PR description
Version: 19.0 Before this PR: The 'Frame' checkbox in the 'Adopt Your Signature' dialog was only shown to internal Odoo users (users with the `base.group_user` group).Also, on the public signing page, the Full Name input, the Frame checkbox and the Auto/Draw/Load buttons had no visible border After this PR: The 'Frame' checkbox is now rendered for every signer in the 'Adopt Your Signature' dialog. The Full Name input, Frame checkbox and Auto/Draw/Load buttons now have a visible border. Taskid-4610728 Forward-Port-Of: odoo/enterprise#127280 Forward-Port-Of: odoo/enterprise#126789
Document links are now calculated more efficiently by narrowing the search work and avoiding an expensive filter. This should improve performance when working with documents and attachments without changing user-facing behavior.
Original PR description
* Prefetching attachment_ids in sudo allows to limit the scope of the documents search * Removing the location filter on the document, not worth the performance hit. Follow-up of Task-5882406
Resolved issues and error corrections
Users with both Partner Commissions and Purchase access can now create and view purchase orders as expected. This prevents commission-related access rules from unintentionally blocking normal purchasing work while keeping commission-only restrictions in place.
Original PR description
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new…
## Current behavior: The user Partner Commissions access rights as All Documents or Own Documents and Purchase access rights as User. With this configuration, the user is unable to create new Purchase Orders, and existing Purchase Orders are also not visible in the Purchase module. ## Expected behavior: The expected behavior is that the user should be able to create and view Purchase Orders with these access rights. Additionally, clarification is required regarding the purpose of the new Partner Commissions access group. ## Steps to reproduce: - Go to user and assign Partner Commission rights as All or own document. - On Purchase, select group as User. ## Cause of the issue: partner_commission adds commission-specific purchase order record rules, but purchase users have no matching purchase-order rule in that module. For mixed-role users, the commission rule ends up restricting standard purchase orders as well. ## Fix: Apply the module's explicit all-purchase rule to purchase users so mixed users keep base procurement access while commission-only users remain restricted by the commission rules. opw-6366074 Forward-Port-Of: odoo/enterprise#126003
Accepting an UrbanPiper order could intermittently fail because the system treated a repeated print-marking request as an error. This change makes that step safely ignore duplicates, reducing random failures during order acceptance and related automated checks.
Original PR description
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice…
mark_urbanpiper_prep_order_as_printed() raised ValueError when the 'urbanpiper_printed' flag was already set, to stop the same order from printing its preparation ticket twice (https://github.com/odoo/enterprise/pull/103894, Task-5353283). Accepting an UrbanPiper order fires this RPC from two places for the same order: synchronously from TicketScreen, and again via the DELIVERY_ORDER_COUNT bus notification the accept flow itself broadcasts. Under load, both requests race for the row lock; Odoo's retrying() replays the loser on lock contention, and by the time it replays the winner has already committed, so the loser hits the already-printed branch and raises. The raise is an unhandled ValueError, so it surfaces as a 500 and fails any tour that accepts an order (test_frontend.py, test_order_receipt.py), intermittently and CI-timing-dependent only. The only caller (pos_store.js: _sendDeliveryOrderForPreparation) already wraps the RPC in try/catch and treats a caught exception exactly like a falsy return value: either way it just skips sending the ticket to preparation. No other code reads or writes urbanpiper_printed, and no webhook path calls this method, so returning False is behaviorally identical for every real caller and safe to make the default. This also removes the mark_urbanpiper_prep_order_as_printed_patch monkeypatch added alongside the original raise in test_01_order_flow: it existed solely to swallow this exact ValueError for that one tour, which is no longer needed now that the method itself is idempotent. runbot error: 941514 Forward-Port-Of: odoo/enterprise#125840
This fix prevents errors when creating operation steps in a multi-company setup where only the basic quality module is installed. It ensures quality team mail aliases have the right company context, so users are not blocked by a configuration issue they cannot resolve from the available screens.
Original PR description
This commit actually reverts [1] and manually forwards [2]. Suppose `mrp_workorder` installed and `quality_control` uninstalled. Because of the default value provided by [1], the only existing quality team is linked to the first company. As a result, when using another company, if the user tries to create an operation step (i.e., a QCP), it will raise an error when the onchange tries to load the default team in charge: https://github.com/odoo/enterprise/blob/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e/quality/models/quality.py#L141-L142 However, the `quality` module doesn't provide any view to create such a team. tldr The module raises an error that is actually impossible to solve... Let's avoid it in the above situation. [1] https://github.com/odoo/enterprise/commit/f9c99f937bd64e5a0acb4bc88b1fc08249250c4e [2] https://github.com/odoo/enterprise/commit/8cd5c9322bef7db49a90d4aef844dd0ba267058e Forward-Port-Of: odoo/enterprise#127314 Forward-Port-Of: odoo/enterprise#126364
VoIP contact search and keypad suggestions now recognize phone numbers even when country codes or formatting differ. This helps users find the right contact more consistently when placing calls, reducing failed or missing suggestions.
Original PR description
Before this fix, the keypad's callee suggestions only matched the search term against the raw `phone` field of contacts. When the user input was automatically prefixed with a country code (e.g. +86), the match could fail if the stored phone number lacked the international prefix. Now `phone_sanitized` is also sent to the frontend via the Store, and the callee suggestion matching falls back to the E164 sanitized number when the raw phone field does not match. Task-6290760 compr https://github.com/odoo/odoo/pull/278018 Forward-Port-Of: odoo/enterprise#127437 Forward-Port-Of: odoo/enterprise#124797
Tasks created from project templates will now ignore archived users when assigning people through project roles. This prevents inactive employees or former users from being added to new customer project work, keeping task ownership accurate.
Original PR description
Steps to reproduce: ------------------------------------------------- 1. Install the `sale_project` module 2. Create a test user with Project User rights 3. Create a Project Role with the Created…
Steps to reproduce:
-------------------------------------------------
1. Install the `sale_project` module
2. Create a test user with Project User rights
3. Create a Project Role with the Created User as a Team Member
4. Create a Template Project as follows:
* Add one task to the template project
* Add the created Project role to the Task
5. Create a Service Type Product with:
* Create on order: Project
* Project Template: Created Template
6. Archive the Created User
7. Create and Confirm the Sale Order with the Created Product
Observation:
-------------------------------------------------
The generated task is assigned to the archived user, although the archived user is no longer part of the Project Role.
Issue:
-------------------------------------------------
While creating Project and Tasks from template, the context disable active record filtering (e.g., `active_test=False`), causing the assignment logic to fetch both active and inactive/archived users linked to the role. https://github.com/odoo/odoo/blob/8ec646e51497b38d34ea59296e0fc8644a50ee3a/odoo/orm/models.py#L4868
After that, during the `copy_data` method, It takes all the users from the roles without checking weather user is active or not
https://github.com/odoo/odoo/blob/8ec646e51497b38d34ea59296e0fc8644a50ee3a/addons/project/models/project_task.py#L890-L904
And even if we pass only Active users from this method, on moving further, it reassigns the users from roles without checking the Active field of the user
https://github.com/odoo/enterprise/blob/5abb147f9bf725daafc202d8259a5bb8a9b78d94/project_enterprise/models/project_task.py#L501-L503
https://github.com/odoo/enterprise/blob/5abb147f9bf725daafc202d8259a5bb8a9b78d94/project_enterprise/models/project_task.py#L544-L553
Due to this, the Archived User is also assigned to the tasks from the project roles
Solution:
-------------------------------------------------
Apply a `filtered('active')` check directly on the project role's users `(role.user_ids)` within the core task-copying logic in both `project` and `project_enterprise` modules. This ensures archived users are universally excluded from task assignments during template copying, regardless of what triggers the template instantiation.
Related Community PR: https://github.com/odoo/odoo/pull/274426
opw-6350841
Forward-Port-Of: odoo/enterprise#125637HR Gantt views now apply search filters consistently when deciding which employees to show, including employees without leave or attendance records. The views also standardize employee-based grouping to avoid confusing or unsupported results from other grouping options.
Original PR description
`user_domain` (in the context) is supposed to contain the domain defined by the user (in the search bar). It was not the case for all gantt views, and produced inconsistent results, as that domain is…
`user_domain` (in the context) is supposed to contain the domain defined by the user (in the search bar). It was not the case for all gantt views, and produced inconsistent results, as that domain is used to know when to display the employees without leaves/attendances. The PR fixes that issue by creating the `HrGanttModel` class, that takes care of defining the `user_domain` correctly. This class also disables the *Group By* menu, and defaults to grouping by employees. This was decided for the following reasons: - All gantt views inheriting this class would group by employee - Grouping by other fields would already not work in some cases - It's very difficult to add employees without records if the gantt is grouped by multiple fields at once Affected `_get_gantt_data()` functions have been adapted accordingly The access models (the ones defined in the `access.csv` files) are implied, and thus never passed as a parameter to `get_gantt_data()`, so we need to also manually add them when converting the model to the related field used in `groupby` task-5502544
This fix prevents access errors when HR users view salary offers by ensuring required payroll-related values are calculated with the right permissions. It also updates the salary configurator test flow to reflect realistic HR user access instead of relying on administrator rights.
Original PR description
A field displayed inside the offer should have been computed with sudo as it accesses some payroll field to compute. Also, the salary configurator tour has been adapted to use a HR user instead of an admin
Fixed an error that could occur when users turned the No Follow-Up option on or off for invoices with multiple payment installments. This prevents Follow-Up Reports from crashing when some installments have already been paid and reconciled.
Original PR description
Steps to Reproduce: 1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments. 2. Create a Customer Invoice with this Payment Term. 3. Post the invoice.…
Steps to Reproduce:
1. Configure Payment Term (Accounting > Configuration > Payment Terms) containing multiple installments.
2. Create a Customer Invoice with this Payment Term.
3. Post the invoice.
4. Register a payment and fully reconcile one of the installments.
5. Navigate to the customer's Follow-Up Report (Accounting > Reporting > Partner Ledger > Report: Follow-Up Report).
6. Navigate to remaining open installment/account move line for that invoice.
7. Turn On or Off the No Follow-Up toggle for the invoice.
An error occurs when enabling or disabling the No Follow-Up toggle.
Issue:
Enabling or disabling the No Follow-Up toggle on a remaining open installment in the Follow-Up Report raises a server error when the invoice contains multiple installments and one or more installments are already fully reconciled.
Root Cause:
The Follow-Up Report only loads and sends non-fully reconciled account move lines from the JavaScript side through all_line_ids. In action_toggle_no_followup(), when the selected line belongs to an invoice, the code retrieves all receivable/payable lines of the invoice, including fully reconciled installments:
```
move.line_ids.filtered(
lambda line: line.account_type in ('asset_receivable', 'liability_payable'),
)
```
The method then attempts to map every receivable/payable line to a report line ID using aml_id_to_line_id. Since fully reconciled installments are not present in all_line_ids, they are missing from the mapping dictionary, causing a KeyError when accessing:
`aml_id_to_line_id[line.id]
`
Fix:
Restricted the impacted lines to those present in the report by adding a check that the account move line exists in aml_id_to_line_id before performing the mapping:
```
lambda line: line.account_type in ('asset_receivable', 'liability_payable')
and line.id in aml_id_to_line_id
```
opw-6245448
Forward-Port-Of: odoo/enterprise#126785
Forward-Port-Of: odoo/enterprise#126156The Attendance Gantt view now includes employees without contracts when they have attendance records in the selected period. This prevents valid attendance entries from being hidden in the default grouped view, giving managers a more complete schedule overview.
Original PR description
Issue: The default Employee grouped Attendance Gantt hides an attendance when its employee has no contract. The record remains accessible from the list view or after removing the default grouping.…
Issue: The default Employee grouped Attendance Gantt hides an attendance when its employee has no contract. The record remains accessible from the list view or after removing the default grouping. Steps to reproduce: - Create an attendance-based employee without a contract. - Create an attendance for the employee. - Open Attendances with the default Date and Employee groupings. Cause: In https://github.com/odoo/enterprise/blob/c8c91758b148cced1b7b5c59479fba7aaea033c1/hr_attendance_gantt/models/hr_attendance.py#L151-L164 `_get_gantt_data_group_by_employee()` builds `employees_on_page` using only employees whose versions overlap the displayed period. It then adds those employee IDs to `attendances_domain`. A contractless employee is therefore excluded before its otherwise valid attendance is fetched. Solution: Build the employee visibility domain from employees having either an overlapping contract or an attendance matching the effective Gantt domain. Apply the same domain to the employee query and group count so contractless employees with visible attendances are included while contractless employees without attendances remain hidden. opw-6416111
Shifts for employees with flexible schedules are now counted consistently in planning and timesheet reports, even when specific start and end hours are not set. This fixes a reporting gap so managers see the same planned work across schedule views and analysis reports.
Original PR description
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning…
## Behavior Before the PR When an employee did not have explicit `hours_from` and `hours_to` values defined, their shift was in the **Schedule by X** pivot view but was not included in the **Planning / Timesheets Analysis** report. ## Steps to Reproduce 1. Create an employee with a Flexible Working Schedule in the Employee form, or configure working hours where both `Hour from` and `Hour to` are left unset. 2. Add a shift for this employee linked to a project and a task. 3. Publish the shift. 4. Navigate to **Planning → Schedule → By Project**, switch to the pivot view, and observe that the shift created in step 2 appears and is counted. 5. Navigate to **Planning → Reporting → Planning / Timesheets Analysis**, switch to the pivot view, and observe that the same shift does not appear. ## Behavior After the PR When an employee does not have explicit `hours_from` and `hours_to` values, their shift is now considered valid in both the **Schedule by X** views and the **Planning / Timesheets Analysis** report. ## Additional Notes - In earlier versions of Odoo, the `Work From` and `Work To` fields were mandatory. With a change to flexible working schedules and the option to define only the total number of hours per day, these fields may now be left empty. This change exposed the underlying issue addressed by this fix. task-[5969788](https://www.odoo.com/odoo/project/4105/tasks/5969788) Forward-Port-Of: odoo/enterprise#110606