Wednesday, September 2, 2026
6 changes · saas-18.4
Enhancements to existing features
Appointment pages now calculate available booking capacities much more efficiently, especially when many linked resources are involved. This reduces slow page loads from minutes to seconds in affected cases and also corrects some capacity values shown during resource selection.
Original PR description
Before --- - When opening the appointment page, the possible capacities that a user can book needs to be calculated to show the capacity dropdown. - This is currently done by getting all the possible…
Before
---
- When opening the appointment page, the possible capacities that a user can book needs to be calculated to show the capacity dropdown.
- This is currently done by getting all the possible combinations that could arise from each resource along with its linked resources (Linked resources are resources that are used to combine with the main resource to allow a bigger capacity)
- The complexity of this approach blows up with the increase in linked resources. For each resource we end up with O(2^m) where m is the number of linked resources it has. If they have the same number of linked resources, and n is the number of resources, we end up with O(n*2^m)
Solution:
- Flipping the algorithm, we create a structure for possible capacities and only keep the best combination for a found capacity.
- We first store all available capacity for relevant resources.
Algorithm
---
For a resource:
- We initialize the solution S to {0: []}
- Greedy dynamic programming is then used to get all the possible combinations by building the result dict, adding one resource at a time. We add its capacity to all entries of S -> we use the combination if a new capacity is reached OR update the combination if the number of elements of combination is less than the existing one.
Then, for the global solution:
- We go through S of resources (in the order of self), and add entries to G (the general dict of solutions). Again, on collision, we only keep the lower-cardinal combination.
The complexity of this algorithm is O(n*u) where u is the dynamic programming complexity. u would be quadratic O(m^2) if the sub-sums of capacities overlap heavily, for instance when resources have the same capacity, like tables of a restaurant, but could reach (2^m) in the worst case scenario.
Related changes / side effects
---
1. Fixing an issue
2. Cleaning strange logic
3. Cleaning max capacity computation
Benchmark:
- opw-5177932 goes down from 6 mins to less than 2 seconds using the new algorithm.
Also include
---
Fix the max capacity on resources when skipping resource selection. It computed the max capa based on values that were not yet emptied in the website_appointment controller.
Task-6233563
Forward-Port-Of: odoo/enterprise#118981Ecuadorian electronic invoicing now lets companies enter their third-party software provider's RUC in settings. This value is automatically included in electronic documents, printed reports, and delivery guides to help meet SRI reporting requirements.
Original PR description
Purpose: SRI Resolution requires taxpayers using 3rd-party billing software in Ecuador to report the software provider's RUC on all electronic documents and printed representations (RIDE). A new system parameter is introduced and displayed in Invoicing > Setting > Ecuadorian Localization > Electronic Invoicing, so users can add their software provider's RUC. This value will be automatically sent to the EDI and displayed on the report. task-6432810 Forward-Port-Of: odoo/enterprise#129456
Resolved issues and error corrections
Planning now correctly shows an employee's working hours on every Gantt row when their shifts are split across multiple projects. This prevents valid working time from appearing greyed out, making schedules clearer and reducing confusion for planners.
Original PR description
Issue: ---------------------------------------- In the Planning Gantt view, when an employee's slots are split across several rows, all of them are greyed out except the last one, which correctly…
Issue: ---------------------------------------- In the Planning Gantt view, when an employee's slots are split across several rows, all of them are greyed out except the last one, which correctly displays the employee's working hours. Steps to reproduce: ---------------------------------------- - Have an employee with a running contract. - Create two shifts for that employee on two different projects. - Open Planning, switch to the Gantt view, and group by Resource, then by Project. - Only one of the two rows for that employee shows the working hours; the other is greyed out. Cause: ---------------------------------------- The grey background is applied by the Gantt view to any cell that has no matching working period, the same way it greys out days off. The working periods, based on the employee's work intervals are computed in `gantt_resource_employees_working_periods()` and indexed by employee id in a plain dict (`row_per_employee_id`). When the same employee appeared in several rows, each new row overwrote the previous one in the dict, so only the last row kept a reference and got its working periods filled in. Solution: ---------------------------------------- Group the rows per employee into a list instead of a single dict entry, and append the computed `working_periods` to every row belonging to that employee. opw-6449130 Forward-Port-Of: odoo/enterprise#128094
Managers can now open employee org charts and related expense records without being blocked by company-access errors from other parts of the hierarchy. Expense team approvers also gain smoother handling when creating expenses for subordinates across companies, reducing duplicate employee setup and administrative friction.
Original PR description
_(updates on July-17th and July-28th for points 2. and 3. in `hr_expense`)_ # Introduction **1\.** A fix to `hr_org_chart` was initially proposed. I think it is mergeable because it fixes the…
_(updates on July-17th and July-28th for points 2. and 3. in `hr_expense`)_ # Introduction **1\.** A fix to `hr_org_chart` was initially proposed. I think it is mergeable because it fixes the user-experience properly like expected in Odoo standard. Other commits are suggested to `hr_expense`. They were designed to allow more flexibility in the management of `hr_employee.company_id` (in multi-company context) and allow not to duplicate the `hr.employee` of each companies of the `res.users`. **2\.** The [FIX] silences a multi-company access error when searching for Expense validators => it seems safe **3\.** The [IMP] largely allow more flexibility for a "Expense: Team Approver" when creating expenses on behalf of its subordinates # 1. in hr_org_chart [FIX] Prevents multi-company error when recursively searching for ancestors. <img width="1035" height="789" alt="image" src="https://github.com/user-attachments/assets/d5517f79-7efc-4223-ae77-0af8b25a3d1e" /> ### Issue description In a multi-company environment, when one of the `hr_employee.company_id` of a hierarchy is not in the allowed companies of a Manager's `res_users.company_ids`, this Manager can view `hr.employee` in the list view but cannot open their forms. This happens when: - `hr_org_chart` module is installed - the Org Chart is displayed on the 1st page of the `hr.employee` form, like when the HR settings "Skills Management" is disabled (in `res.settings`) => thus the whole form becomes inaccessible from the manager When a `hr.employee` form is opened, a multi-company access error is thrown to him, even if the `company_id` of the opened `hr.employee` is in the user's `res_users.company_ids`, because of the hierarchy's `company_id`. It should be expected that the part of the Org Chart which is not allowed to be seen would just be hidden. ### Steps to reproduce Data setup: - Employee "A" in company A - Manager "M" in company A, manager of "Employee A" - Manager of manager "MM" in company A, manager of "Manager M" - And now, in company B (let's say a Holding), the "Director" is manager of "Manager MM" - "Manager M" is only given access access to Company A - the module "hr_org_chart" is installed Actions: - Login with Manager M - Browse to Employees list and try and open the form of "Employee A" (up to tab _"Professional information"_, if it is not the 1st of the notebook) ### Proposed fix This PR re-uses the already existing method `_check_employee` which contains all the logic to solve the issue. Maybe the call to this method was forgotten? This PR simply call this method when finding an ancestor, in the controller of `hr_org_chart`. This fix is thus very limited to the call to the public method `hr_org_chart.get_org_chart()` made by the Org Chart widget. ### Desired behavior after PR is merged The part of the Org Chart not allowed to be seen by "Manager M" is hidden. # 2. hr_expense [FIX] <img width="541" height="415" alt="image" src="https://github.com/user-attachments/assets/d594815b-2b77-4088-878b-9b192b64d6a3" /> ### Issue description As an employee, I click on the button "View Report" on my expense. I get a multi-company access error, preventing me to view and edit my expense report. This is because the manager of the department I belong is in a company I'm not allowed to see. This can also happen just when opening my Expense (instead of Expense Report). ### Current behavior before this PR The employee is blocked to continue editing its Expense or to submit it to a Report. ### Current behavior after this PR The employee can edit and submit its Expense no matter the `company_id` of its hierarchy. Technically: the `can_approve` field on the expense sheet uses a localized `.sudo()` method to bypass multi-company limits when searching if the current user is a validator. # 3. hr_expense [IMP] <img width="1028" height="549" alt="image" src="https://github.com/user-attachments/assets/1096c0d4-d025-46a9-8559-6bab5de71577" /> ### Improvement summary In multi-company environment, allow a "Expense: Team Approver" to create Expenses for its subordinates (`hr_expense.employee_id`) **no matter the `hr_employee.company_id` of its subordinates**. The domain of `hr_expense.employee_id` keeps the security of `check_company=True` => thus the Manager only sees `hr.employee` having their `company_id` in the manager's allowed companies (`res_users.company_ids`). ### Current behavior before this PR Context: a 8-companies environment where the `hr.employee` of each hierarchy chains are splitted in many different companies, like: - top-level (admin board): 1 company - middle management: approx. 2 companies - down level: the other companies The "down level" have `hr.employee` but no `res.users`. The "middle management" have `res.users` and must create the Expenses of their "down level" subordinates on their behalf. Issue: as a manager, as per Odoo proposal, I need to have a `hr.employee` in the same company of my subordinates to be able to create Expense of their behalf. However, this is very inconvenient because as a Manager, I can have employees in various companies. And my own manager it not in the same company than me, so the same issue applies recursively. ### Behavior after this PR is merged The domain of the field `expense_id.employee_id` is more permissive. As a Manager, it allows me to select the Employee I manage in my active company, no matter if I have or not myself a `hr.employee` in this company. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279530 Forward-Port-Of: odoo/odoo#266261
The fix prevents Odoo from creating untrackable e-invoicing response records when the French PDP service does not return a message identifier. This avoids scheduled status update failures and keeps invoice and vendor bill e-invoicing processing running reliably.
Original PR description
Steps to reproduce: - Install `l10n_fr_pdp` and `l10n_be` module - Activate `French e-invoicing` (you need to put your DB in test mode) (i.e: `account_peppol.edi.mode = 'test'` in system parameter)…
Steps to reproduce:
- Install `l10n_fr_pdp` and `l10n_be` module
- Activate `French e-invoicing` (you need to put your DB in test mode)
(i.e: `account_peppol.edi.mode = 'test'` in system parameter) Refer this Documentation https://www.odoo.com/documentation/19.0/applications/finance/fiscal_localizations/france.html?highlight=e%20invoicing#localizations-france-e-invoicing-fac-elec-config
- Create a Invoice and Sent with `E-invoicing`
- Run `PEPPOL: retrieve new documents` Cron
- In Invoice Other Info > `E-Invoicing Status` should be in `Done` state
- Now Vendor Bill is created for that invoice > Cancel that bill with reason > Check `E-Invoicing Status` response for bill(one response will be without UUID)
- Go to schedule actions > `PEPPOL: update message status`
- Run it and get the traceback: `KeyError: 'false'`
Issue:
When sending a lifecycle response to the French PDP, `_pdp_send_response` calls the `/api/pdp/1/send_response` endpoint and expects IAP to return a `message_uuid` for each response.
However, IAP return a response without a message UUID, for example: ` {'messages': [{'message_uuid': False}]}` `_pdp_send_response` currently assumes that every returned message has a valid UUID and creates an `account.peppol.response` with:
```
'peppol_message_uuid': False
'peppol_state': 'processing'
```
This leaves an `account.peppol.response` in `processing` state without a UUID that can be used to track it.
During the next execution of `_cron_peppol_get_message_status`, `_peppol_get_message_status` retrieves this response through `_peppol_get_documents_for_status` and builds `uuid_to_record` using `peppol_message_uuid` as the key. The resulting mapping contains the Python value `False` as a key.
The cron then calls the PDP `1/get_document` endpoint with this invalid UUID. IAP returns it as the string `'false'`, which is passed to `_peppol_process_messages_status`. The French PDP implementation tries to retrieve the corresponding record with: `uuid_to_record[uid]`
Since the mapping contains `False` while `uid` is `'false'`, this raises: `KeyError: 'false'`
This failure prevents the status cron from completing the processing of the messages.
Solution:
Avoid creating the `account.peppol.response` when IAP does not return a `message_uuid`. A response without a UUID cannot be tracked through the PDP status flow, so creating it in `processing` state only leaves an
invalid record that will later cause the status processing to fail.
opw-6453133
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#281691Fixes an issue where manually increasing the quantity on a timesheet invoice could prevent later timesheets from being billed. Businesses can now continue invoicing future timesheet periods correctly while preserving refund-related behavior.
Original PR description
## Issue When a use rmanually increases the invoiced quantity on a timesheet invoice, subsequent timesheets logged in future periods can no longer be invoiced. The system assumes that the most recent…
## Issue
When a use rmanually increases the invoiced quantity on a timesheet invoice, subsequent timesheets logged in future periods can no longer be invoiced. The system assumes that the most recent logged hours are already covered by the previously over-invoiced amount, blocking the billing of the most recent timesheet entries.
## Steps to reproduce
1. Install *Sales Timesheet* (`sale_timesheet`)
2. Create a Product P
- Product Type: Service
- Invoicing Policy: Based on Timesheets
- Create on Order: (Project &) Task
3. Create an SO:
- Customer: Any
- Product: P (any quantity)
- Confirm
4. Record hours on the task created:
- 06/01/2026 (June 1st): 1 hour
- 07/01/2026 (July 1st): 1 hour
5. Create the invoice for the June timesheet entry (by setting a timesheets period when creating the invoice), then **change the quantity to any value strictly greater than 2** and confirm.
6. Create the invoice for July
7. **An "Invalid Operation" error appears, stating that there's nothing to invoice, even though the timesheet entry from July was never invoiced.**
## Cause
Since https://github.com/odoo/odoo/commit/3b86ac3d180993239b63fd305ee0ba1f17f7eb20, the computation of the quantity to invoice changed to include the difference between the quantity delivered and the quantity already invoiced. In the flow described by the steps to reproduce above, the quantity to invoice is larger than the quantity delivered, making the `qty_to_invoice` equal to `0.0`.
https://github.com/odoo/odoo/blob/626d31fa0191bfe7ca8e1fcf177357eeeb60f2c7/addons/sale_timesheet/models/sale_order.py#L331-L334
The reason for the fix above being the partial refunding of timesheet-related invoices, we can keep that solution when working with refunded invoices, and keep the previous behavior for other cases.
This solution solves the issue in the steps to reproduce above, but was also manually tested on the issues from https://github.com/odoo/odoo/commit/3b86ac3d180993239b63fd305ee0ba1f17f7eb20 and https://github.com/odoo/odoo/commit/64cf4afabd0c5ef040cf87fc6f3125fe0bb81bbb.
opw-6485674
Forward-Port-Of: odoo/odoo#286043
Forward-Port-Of: odoo/odoo#284470