Monday, February 23, 2026
9 changes · saas-18.3
Enhancements to existing features
This update enhances the spreadsheet module by adding logging when exporting large amounts of data, particularly during spreadsheet downloads, copy-pasting, and printing. It also restricts access to downloaded frozen or XLSX spreadsheets to authorized users, improving data security and control.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#246828 Forward-Port-Of: odoo/odoo#210592
This update enhances the logging process when exporting large amounts of data from Odoo spreadsheets, specifically during XLSX downloads, clipboard copies, and printing. It also restricts access to frozen or XLSX spreadsheets to authorized users, improving data security and control.
Original PR description
Forward-Port-Of: odoo/enterprise#106210 Forward-Port-Of: odoo/enterprise#85888
Resolved issues and error corrections
This update resolves an issue where event registration records didn't display attendee information after order processing. The fix ensures that registration answers are correctly linked, allowing attendee details to be accurately recorded in the system. This improves the accuracy of event attendance tracking.
Original PR description
Currently when event only have questions of type text, the field `registration_answer_ids` is empty after the order is synced to the backend. Steps to reproduce: ------------------- * Create an event…
Currently when event only have questions of type text, the field `registration_answer_ids` is empty after the order is synced to the backend. Steps to reproduce: ------------------- * Create an event for which you anloy have questions of type text: name, phone, email * Open a shop * Sell a ticket for the event and answer all 3 questions * Validate order * Check event registrations > Observe that although attendee's corresponding fields have been populated, the question' list is empty. Why is the issue happening: --------------------------- In the model EventRegistration there are two fields `registration_answer_ids` and `registration_answer_choice_ids`. Those two fields share the same relation table. https://github.com/odoo/odoo/blob/7c93b869a97f30a5b75d603b5973bf37b91bd63e/addons/event/models/event_registration.py#L83-L84 The registration is created here: https://github.com/odoo/odoo/blob/ef92cf5b33a3e55e801feea71bdbdd212da59adb/addons/pos_event/static/src/app/screens/product_screen/product_screen.js#L246 For context, when calling the create method `registration_answer_ids` has 3 values and `registration_answer_choice_ids` is empty (since no selection question). During the creation process we call the `_update` function. We first see what's happening with `registration_answer_ids`. Since there are some values we'll use the `create` part. https://github.com/odoo/odoo/blob/ef92cf5b33a3e55e801feea71bdbdd212da59adb/addons/point_of_sale/static/src/app/models/related_models/index.js#L394-L398 The `_connect` method will call itself on the inverse of the field. https://github.com/odoo/odoo/blob/ef92cf5b33a3e55e801feea71bdbdd212da59adb/addons/point_of_sale/static/src/app/models/related_models/index.js#L684 We'll finally try to get the inverse of this inverse. The first problem can be seen here because the inverse of the inverse points toward `registration_answer_choice_ids`. When we look at the field `registration_answer_choice_ids` since it is empty it will use the `clear` method. This also end up calling the `_connect` method on its inverse. Since the inverse of the inverse of `registration_answer_choice_ids` also points out to `registration_answer_choice_ids` thus it deletes the records that were added with `registration_answer_ids` This all happens because in the inversMap bot fields point toward the same value but the inverse of this value can only point to one value. In this case, ´registration_answer_choice_ids`. Why the fix: ------------ Touching the inverseMap or the related model is too risky at this point. So instead we focus on avoiding to clear all records. This can be achieved by creating the record empty of the two fields and creating the records for `registration_answer_ids` and `registration_answer_choice_ids` separately to link them later. opw-58966712
This update resolves an issue where users within the 'Planning / User' group couldn't take open shifts. The fix ensures that the 'I Take It' button is consistently displayed for this group, allowing them to seamlessly accept shifts within the Planning app. This improves the usability of the Planning feature for all users.
Original PR description
## Issue In the Planning app, users within the *Planning / User* groups could not take open shifts. The button *"I Take It"* would not be displayed when clicking on the shift. ## Steps to reproduce…
## Issue
In the Planning app, users within the *Planning / User* groups could not take open shifts. The button *"I Take It"* would not be displayed when clicking on the shift.
## Steps to reproduce
1. Install the *Planning* (`planning`) app
2. In the Planning app, as an admin, create and publish an open shift
3. Log in as a *User*, open the *Planning* app, and click on the open shift
4. **The _"I Take It"_ button is not displayed**
When logging in as a user who is **not** part of the *Planning / User* group, the *I Take It* button is displayed.
## Cause
The condition for the *I Take It* button to be displayed is the following:
https://github.com/odoo/enterprise/blob/301e3c40c3272a6ae89a1eb4387d7d244e1dc52c/planning/views/planning_views.xml#L282-L283
The condition that makes the button invisible is `not context.get('my_planning_action')`. When the user is part of *Planning / User*, the schedule they see when opening the *Planning* app is the *"Schedule by Resource"*, which has the following context:
https://github.com/odoo/enterprise/blob/301e3c40c3272a6ae89a1eb4387d7d244e1dc52c/planning/views/planning_views.xml#L716
When a user who **is not** part of the *Plannig / User* group opens the *Planning* app, they see their own schedule, where the context is different than in the first case:
https://github.com/odoo/enterprise/blob/301e3c40c3272a6ae89a1eb4387d7d244e1dc52c/planning/views/planning_views.xml#L636
Because the `my_planning_action` property is in the context (and all the other conditions are respected), the button is displayed.
opw-5451282
Forward-Port-Of: odoo/enterprise#104919This update resolves an issue where accrual plans were incorrectly blocking the ability to take future leave, even with remaining balances below the cap. The fix prevents the system from over-calculating accrued leave, ensuring users can accurately plan and take future leave as intended. This improves the usability of the time-off management feature.
Original PR description
Accrual plan for leave days gets blocked, even when the remaining leave balance is below the cap. As a result, no additional leaves are accrued beyond a certain point, even though they should be. #…
Accrual plan for leave days gets blocked, even when the remaining leave balance is below the cap. As a result, no additional leaves are accrued beyond a certain point, even though they should be.
# Steps to reproduce:
Go to time off app
* Create a new leave type.
* Create a new accrual plan with:
- one milestone :
- 2 days accrued per month
- Cap: 10 days
- start accruing 1 days after
- No expiration
- Carry over: All
* Create and validate a leave allocation
- 1 year ago
- new leave type
- new accrual plan
* Take the maximum number of leaves available.
* Advance the computer calendar by 1 year.
* Again, take the maximum number of leaves.
* Advance the computer calendar by another year.
* Try to take a future leave.
-> Issue: It’s not possible to take a future leave, the number of accrued days has stopped increasing. The accrual plan appears blocked.
Objective : The accrual plan should continue to allocate leave days even if leaves have been consumed regularly, as long as the remaining leaves are under the cap.
## Issue
Before going further: the property `leaves_taken` of the `hr.leave.allocation` is supposed to contain the number of leaves this allocation cover until "today".
In the `_test_get_allocation_future_leaves1` added test, in the last line of the test :
`assert_virtual_leaves_equal(self, leave_type_day, 2, self.employee_emp, date='2023-02-01')`
When calling `get_allocation_data` with a `target_date` set in the future, the result is wrong. Here is how it works :
`get_allocation_data`
...
.....`_get_consumed_leaves` (1)
...........`_get_future_leaves_on` (2)
...............`_process_accrual_plans` (3)
....................`_compute_leaves` (4)
.........................`_get_consumed_leaves` (5)
..............................`get_future_leaves_on` (6)
...................................`process_accrual_plans` (7)
**A)** The method **(2)** try to calculate the added number of days each allocation will have on `target_date`. So it creates a copy of the allocation in memory using the 'new' method:
`fake_allocation = self.env['hr.leave.allocation'].with_context(default_date_from=accrual_date).new(origin=self)`
It will then update it to `target_date` using `_process_accrual_plans` and will return the difference of days between the
updated `fake_allocation` and the current allocation (`self`)
**B)** Before iterating over each accrual date, the `_process_accrual_plans` **(3)** will get the `leaves_taken` property which is a computed field. It will trigger `_compute_leaves`.
**C)** The method **(4)** will call `_get_consumed_leaves`, and so the nightmare begins.
**D)** The method **(6)** will create a second `fake_allocation` based on the origin of the first `fake_allocation` (see **A)**).
**E)** This time, `_process_accrual_plans` **(7)** will also look at the `leaves_taken`, but won't trigger the `_compute_leaves` probably because the current allocation is a `fake_allocation` of a `fake_allocation`, and one property of the `new` method is that `Two new records with the same origin record are considered equal.`. Therefore, the `leaves_taken` is considered to be already computed (but it's not).
So `_process_accrual_plans` read the `leaves_taken` which is 0 (probably the default value of `leaves_taken`), but it should be 20 !
**F)** As the value of `leaves_taken` is wrong, the fake_allocation n°2 is also wrong, and its `number_of_day` is 10 but the `number_of_days` of the origin allocation is 20. So `get_future_leaves_on` **(6)** will return -10 which makes no sense, and all the previous calls computations will be wrong. And the final `virtual_remaining_leaves` value will be 0 instead of 2.
## Source of the issue
In the `_process_accrual_plans` method, for each allocation, `leaves_taken` is only computed once at the start of the loop over the allocation "important" dates (see `nextcall` property of `hr.leave.allocation`). At this moment, the method calculates the `leaves_taken` the allocation will have on the `accrual_date` parameter. Yet, this property can change depending on the date the allocation is on (`nextcall` property) which leads to some issues in the computation of the `allocation.number_of_days`.
## Solution
For each allocation, compute the `leaves_taken` at every iteration trough the values of `nextcall`. BUT, this can trigger an infinite loop as computing `leaves_taken` calls `_get_consumed_leaves` which calls `_get_future_leaves_on`, which calls `_process_accrual_plans` ... To avoid this, this PR add the context variable `precomputed_allocations` (will be converted into a function parameter in master) which will prevent `_get_consumed_leaves` from calling `_get_future_leaves_on` for the allocations already up to date (contained by this very `precomputed_allocations` context variable).
opw-4934391
opw-5226806
Forward-Port-Of: odoo/odoo#239836
Forward-Port-Of: odoo/odoo#249190
Forward-Port-Of: odoo/odoo#243812This update ensures that all event registration answers, including free-text responses, are correctly synchronized during the payment process. Previously, only selection-based answers were sent, leading to data loss. This fix corrects a technical issue related to how event registration data is handled, improving the reliability of event ticket sales through POS.
Original PR description
## Steps to reproduce: - Configure event registration with only free-text fields (no selection field). - Open the POS, add an event ticket product, and fill in the registration form. - Click Payment…
## Steps to reproduce: - Configure event registration with only free-text fields (no selection field). - Open the POS, add an event ticket product, and fill in the registration form. - Click Payment and validate the order. ## Issue: - Registration answers were only sent to the backend when at least one selection-type question was filled. - When no selection field was present, free-text answers were not synced at all. ## Reason: - The `registration_answer_ids` and `registration_answer_choice_ids` One2many fields on EventRegistration both point to the same `registration_id` Many2one field on EventRegistrationAnswer. https://github.com/odoo/odoo/blob/c738d049fe09101bd14dce0710c2659a4a6eca39/addons/event/models/event_registration.py#L83-L85 - This caused data loss during the POS model synchronization, as entries were overwritten in the `inverseMap`. https://github.com/odoo/odoo/blob/c738d049fe09101bd14dce0710c2659a4a6eca39/addons/point_of_sale/static/src/app/models/related_models/model_defs.js#L59-L75 ## Fix: - Send all registration answers (free-text and selection-based) exclusively via `registration_answer_choice_ids`. task-5438565
This update corrects a bug where internal users were incorrectly assigned as suppliers when forwarding supplier invoices. This issue was causing disruptions in the OCR (Optical Character Recognition) process, which relies on automatic data population from invoices. The fix ensures invoices are correctly linked to existing suppliers, maintaining the integrity of the OCR workflow.
Original PR description
Currently we have an issue with OCR flow, where if internal users forward an email from an internal email address, the internal user is added as vendor Steps to reproduce: - Setup email alias for journal "Purchases" - From an internal user email, forward a supplier bill to the vendor bill alias Issue: If the supplier is not already a registered partner, the bill will be created with the internal user set as partner. This will break OCR flow where the missing document fields will be auto populated from the bill opw-5487368
This update corrects a previous issue where the system incorrectly predicted taxes on imported invoices. Now, the system accurately uses the tax information directly from the XML invoice file, ensuring correct tax calculations. This improves the reliability of our invoicing process.
Original PR description
Context: When importing an XML invoice or vendor bill, the tax prediction was based on the customer’s invoice history. Example: if the imported invoice contains an item found in the history with two taxes (6% and 21%), the prediction would return both taxes (6% and 21%), even though only one tax is present in the XML file. The actual tax data present in the imported XML was not taken into account. After this commit, the prediction is more rigorous and correctly relies on the tax information provided in the XML (restricted search domain) task-5503126 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#249307 Forward-Port-Of: odoo/odoo#246308
This update fixes an issue where invoices weren't correctly using product category accounts. Now, Odoo automatically checks the entire category hierarchy (parent to child) to find an available account, ensuring invoices accurately reflect the intended income or expense. This improves financial reporting accuracy.
Original PR description
**Steps to reproduce:** * Install the **Accounting** module. * Create a product category hierarchy (grandparent → parent → child). * Set an **income account** on the grandparent category. * Set an…
**Steps to reproduce:** * Install the **Accounting** module. * Create a product category hierarchy (grandparent → parent → child). * Set an **income account** on the grandparent category. * Set an **expense account** on the child category. * Leave the parent category without any accounts. * Create a product assigned to the child category. * Create a customer invoice using this product. **Observed behavior:** * The invoice line uses the **default income account** from settings instead of the grandparent category’s income account. * The category hierarchy is not checked to retrieve parent accounts. **Expected behavior:** * If no account is defined on a category, Odoo should traverse the **parent categories** to find one. * Only if no account exists in the hierarchy should it fall back to journal or default accounts. **Cause:** * Account lookup only checked the **immediate category**. * Parent categories were not considered. **Fix:** * Traverse the product category hierarchy when searching for accounts. * Use the first account found in the parent chain. opw-5869592 Forward-Port-Of: odoo/odoo#249909 Forward-Port-Of: odoo/odoo#248389