Friday, April 3, 2026
19 changes · saas-19.1
New functionality added to Odoo
This update introduces a new module for Romania to comply with SAFT (Simplified Fiscal Transparency) reporting requirements. Specifically, it focuses on generating the 'On Demand' XML file needed to submit inventory valuation data to the Romanian tax authority (ANAF). This ensures accurate financial reporting for Romanian businesses using Odoo.
Original PR description
Baiscally the SAFT for Romania consists of 3 xml files to submit. This commit focuses on the "On Demand" xml to submit, consisting in the inventory valuation of of the company. File with fields and value to export can be found here: https://www.anaf.ro/anaf/internet/ANAF/despre_anaf/strategii_anaf/proiecte_digitalizare/saf_t/ task-3748978 Forward-Port-Of: odoo/enterprise#95540
This update adds support for Peppol Business Level Responses (BLR) for invoices and credit notes, ensuring compliance with Peppol standards. It allows Odoo to correctly respond to documents received through Peppol, improving integration with international trade partners. This change also incorporates responses for Nemhandel, a related system, with simplified response types.
Original PR description
Peppol offers a response system to the document received through it. These are called Business Level Responses, and their documentation can be found here:…
Peppol offers a response system to the document received through it. These are called Business Level Responses, and their documentation can be found here: https://docs.peppol.eu/poacc/upgrade-3/profiles/63-invoiceresponse/#introduction-to-openpeppol-and-bis The specific BLR implemented in this commit is targeted to invoices and credit notes. 3 types of responses are mandatory for a Peppol participant to correctly adhere to the BLR service: acknowledgement (different to the transport ack), confirmation and rejection. More response's types are available but were not implemented as they're not mandatory/needed (for now). For rejection, a list of at least one reason must be given, and actions can be suggested to the sender of the document for the eventual next invoice shipment. Same principle goes for Nemhandel, with some differences: only 2 responses, BusinessAccept and BusinessReject (no Acknowledgement, and no reasons are needed in case of rejection. Instead, the user can send a string message. iap PR: https://github.com/odoo/iap-apps/pull/1364 task-5237698 Forward-Port-Of: odoo/odoo#243191
Resolved issues and error corrections
A bug causing access errors when creating new stations or visitors within the Frontdesk module for users without employee access rights has been fixed. This update ensures Frontdesk functionality works correctly for all users, regardless of their employee permissions. The fix involved updating the Frontdesk module to align with a recent Odoo update.
Original PR description
Issue: ---------------------------------------- When a user with administrator rights on frontdesk but no rights on employees try to create a new station or visitor, they get an access error. Steps to reproduce: ---------------------------------------- - Have a user with administrator rights on Frontdesk but no rights on Employees - Switch to this user - Open Frontdesk and try to create a new station - Access Error Cause: ---------------------------------------- Since [this commit](https://github.com/odoo/odoo/commit/71f662b827b58c4f8ed1260728dc5194201ec323) models having a many2many field on `hr.employee` must inherit from `hr.mixin` to avoid an access error. The Frontdesk module was not changed. Solution: ---------------------------------------- Make `frontdesk.visitor` and `frontdesk.frontdesk` inherit `hr.mixin` opw-6000417 Forward-Port-Of: odoo/enterprise#110084
This update resolves an issue where the LPP (Labor Pension Plan) was incorrectly applied to employee salaries in the Swiss payroll module when employees were not covered by insurance. The fix ensures that LPP contributions are only applied to insured employees, aligning with Swiss tax regulations and improving payroll accuracy. This change enhances the reliability of the Swiss payroll reporting.
Original PR description
Forward-Port-Of: odoo/enterprise#112824
This update resolves an issue preventing proper product exports from the Web Studio interface. The change involved decoupling a key process, and this commit adds the necessary context to ensure product templates are created correctly. This improves the reliability of product export functionality.
Original PR description
This commit https://github.com/odoo/odoo/pull/254323 changed the way product( template)s are created, which now decouples the logic into two context attributes instead of one. This commit fixes this by adding the second one. Forward-Port-Of: odoo/enterprise#112753
This update corrects a bug where discounts were applied twice to service tasks, resulting in incorrect pricing. The fix ensures discounts are applied correctly based on sales order settings, preventing over-discounting and ensuring accurate pricing for service tasks. This improves the reliability of pricing calculations for field service operations.
Original PR description
Currently, when the user creates a task for a customer with a discount pricelist, the discount is applied twice for the service. <h2>Steps to produce:</h2> * Install `industry_fsm_sale` and enable…
Currently, when the user creates a task for a customer with a discount pricelist, the discount is applied twice for the service. <h2>Steps to produce:</h2> * Install `industry_fsm_sale` and enable `Discounts` and `Pricelists` in settings. * Create a pricelist with a price rule of type discount that applies 10 percent discount to every product. * Go to Customers > Acme Corporation > Sales & Purchase and set the pricelist. * Go to Field Service > Create a Task, and set `Customer` to Acme Corporation. * Add a timesheet with Time Spent 1 > Mark the task as Done > Sale Order <h2>Observed behavior:</h2> The discount is applied twice to the product on SO: **Product**: Service on Timesheets **Unit Price**: `$40` (excluding tax) **First discount:** The 10 percent discount on the unit price of the product. Product unit price is set from `$40 -> $36 ` **Second discount:** The 10 percent discount on the SO line itself. `$36 -> $32.4 ` The untaxed amount is: `$32.40` which should be `$36.00` <h2>Root cause:</h2> This happens because, at line [1], the unit price is already set to the final price from the pricelist when the sale order line is created. Since discounts are enabled, [2] applies an additional discount to that same price, causing the discount to be applied twice. <h2>Solution:</h2> When creating the sales order: * **Discount setting is on:** use list price so the discount is applied from the sales order. * **Discount setting is off:** set the product unit price to the discounted price. [1]- https://github.com/odoo/enterprise/blob/224d2453cc975a3e333825370beaf30d27d89f10/industry_fsm_sale/models/project_task.py#L658 [2]- https://github.com/odoo/odoo/blob/76717e588bfd012b42e859bfc829257d899c6165/addons/sale/models/sale_order_line.py#L788 opw-5432088 Forward-Port-Of: odoo/enterprise#112761 Forward-Port-Of: odoo/enterprise#103950
This update fixes an issue where invoices sent to the Colombian DIAN tax authority were incorrectly flagged as duplicates, leading to rejection. The fix prevents a rollback process from interfering with the correct invoice acceptance status, ensuring invoices are properly recorded by DIAN.
Original PR description
Steps to reproduce:
- Send a Colombian DIAN invoice (SendBillSync flow)
- Simulate a non-200 response from the DIAN GetStatus endpoint during the call of _get_attached_document (see ticket)
Issue:
The invoice is accepted by DIAN but the state is never written. When trying to send the invoice a second time DIAN rejects the invoice as a duplicate (already submitted).
Cause:
`_get_response_history` returns `("", error_msg)` on non-200 status_code and when calling `_get_attached_document`
-> error and rollback and `invoice_accepted` is not written correctly
opw-5919395
Forward-Port-Of: odoo/enterprise#111186This update corrects a visual glitch on mobile devices where the 'Products' snippet would jump unexpectedly when scrolling. The fix removes a setting that caused the snippet to repeatedly re-render, leading to inconsistent display heights. This ensures a smoother and more reliable user experience across different screen sizes.
Original PR description
Scenario: - add the dynamic Products snippet on top of page and save - go to the website with browser address bar that change height when going when going down in the page (hide or change size of it…
Scenario: - add the dynamic Products snippet on top of page and save - go to the website with browser address bar that change height when going when going down in the page (hide or change size of it that is changing the viewport size) - scroll all the way up and down in the page Result: there is some jump that happen when the browser interface change size when scrolling down or up. Cause: When going down the page, the viewport size changes (because the address bar gets bigger / smaller). This causes the dynamic snippet to be re-rendered. Since February 2026 commit 2e5bd409581ddaab28084c42ab51b50b494f4876 to optimize performance, product blocks are only rendered when the are in the viewport (may depends on browser) with "content-visibility: auto". The combination of those two things, causes that if you scroll down, the widget is re-rendeded in owl, but it is only rendered in the page once you scroll in the viewport so the scroll jump up or down with the products snippet being rendered (going from 0 to eg. 300px when scrolling into viewport) or not being rendered (going from eg 300px to 0 when scrolling and the widget not being in viewport). Fix: remove the "content-visibility: auto" when we are in the dynamic "Products" snippet, it was intended for the shop view and not for the case where product block can be re-rendered outside of viewport. opw-6005340 Note: this is mainly happening on mobile browser (eg. safari on iOS) because of the viewport resize when scrolling, but this can somehow be reproduced on chrome desktop: - scroll below a "Products" snippet, change browser window size manually => the should be a jump of the content up - scroll up to go back to the product snippet => the content of product snippet should appear all at once when the 0 pixel heigh get in the viewport Forward-Port-Of: odoo/odoo#256815
This update fixes an issue where Ctrl+A and Delete wouldn't remove all content from editable areas, specifically when the first element was non-editable. Now, text editing works correctly across all editable content, ensuring a smoother and more reliable editing experience. This improves the overall usability of the HTML editor.
Original PR description
Description of the issue this PR addresses: - When an element with `contenteditable="false"` is the first node in the editable, pressing Ctrl+A followed by Delete does not remove the entire selection and instead deletes only the last character. Desired behavior after PR is merged: - Ensure that the selection is anchored to the deepest editable position when performing a select-all operation so that the full editable content is correctly selected and removed. Steps to reproduce: - Insert a toggle list using `/togglelist` in a new todo - Add one or more paragraphs below it and enter some text - Select all content using Ctrl+A - Press Backspace to delete the selection - Observe that only the last character is removed Backport of: 67e6a617def3bf4f9eb6b63b0850f5cfc773bccc task-5363926 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#248601 Forward-Port-Of: odoo/odoo#241696
This update resolves an error that occurred when rearranging sections within sale order lines. The fix ensures that the system correctly handles changes to order lines, preventing errors during editing and maintaining data integrity. This improves the user experience when managing sales orders.
Original PR description
Moving a section around in sale order lines when there is a line that can be abandoned throws an error Steps to reproduce: 1. Install Sales app 2. Go to Sales and create a new quotation 3. Add any…
Moving a section around in sale order lines when there is a line that can be abandoned throws an error Steps to reproduce: 1. Install Sales app 2. Go to Sales and create a new quotation 3. Add any product, then add a section: enter any name for the section and then immediately press Enter (it should create an empty product line) 4. Without leaving edit mode, drag and drop the section at the top of the sale order lines (the empty product line should still be there) 5. An error is thrown The same issue can be reproduced by moving a section down: 3b. Add any product, then add a section: move it to the top of the order lines then enter any name for the section and immediately press Enter (it should create an empty product line) 4b. Without leaving edit mode, drag and drop the section just between the product line and the empty product line Issue: `sortDrop` calls `leaveEditMode` at https://github.com/odoo/odoo/blob/e906eb23d698061f146ba67aae420eb7bb5e8a68/addons/web/static/src/views/list/list_renderer.js#L2242 which removes order lines that can be abandoned https://github.com/odoo/odoo/blob/e906eb23d698061f146ba67aae420eb7bb5e8a68/addons/web/static/src/model/relational_model/static_list.js#L379-L381 This can remove records from the recordMap generated before calling `super.sortDrop` in https://github.com/odoo/odoo/blob/e906eb23d698061f146ba67aae420eb7bb5e8a68/addons/sale_management/static/src/fields/sale_order_line_field/sale_order_line_field.js#L175-L182 so we end up calling `_handleQuantityAdjustment` with a recordMap that contains record ids that have been deleted, throwing an error when we try to access the deleted record Solution: Call `leaveEditMode` before computing recordMap in order to remove the records that can be abandoned. This prevents `this.props.list.records` from being different when we generate recordMap and when we call `_handleQuantityAdjustment`. We also need to set the record being moved as dirty. This prevents the record from being abandoned when `leaveEditMode` is called. opw-6022538 Forward-Port-Of: odoo/odoo#256662
This update fixes an issue where the HTML editor toolbar wasn't opening correctly on macOS when using Cmd+Shift+Arrow to select text. The fix utilizes a secondary event listener to ensure the toolbar activates reliably, even when the Cmd key is held down. This improves the user experience for macOS users.
Original PR description
Problem: The toolbar does not open when using Cmd+Shift+Arrow to select text on macOS. Cause: On macOS, when the Cmd key is held down, the `keyup` event is never fired for other keys. The toolbar…
Problem:
The toolbar does not open when using Cmd+Shift+Arrow to select text on macOS.
Cause:
On macOS, when the Cmd key is held down, the `keyup` event is never fired for other keys. The toolbar relies on `keyup` for Arrow keys to re-enable `onSelectionChangeActive` and trigger the toolbar update, so it never opens.
See section ("Issue 3 - keyup event put on hold for other keys"): https://web.archive.org/web/20160304022453/http://bitspushedaround.com/on-a-few-things-you-may-not-know-about-the-hellish-command-key-and-javascript-events/
Solution:
Track when an Arrow key is pressed while Cmd is held (`pendingArrowKey`) and use a `selectionchange` listener as a fallback to re-enable the toolbar. The `selectionchange` event fires reliably on macOS even when `keyup` is suppressed. A `isMouseDown` guard ensures the listener does not interfere with the existing mousedown/mouseup flow.
Steps to reproduce:
1- Type some text
2- Use Cmd+Shift+Arrow (left or right) to select text 3- Observe the toolbar does not appear
task-6013408
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#257166
Forward-Port-Of: odoo/odoo#253293This fix resolves a crash that occurred when deleting certain fields used in website forms. The issue stemmed from the way website forms were parsing fields as XML, leading to parsing errors. This update corrects the parsing process to prevent these crashes.
Original PR description
Steps to reproduce ================== tl;dr: html fields are parsed as xml - Go to Helpdesk > Tickets > Warranty - Open studio - Add a new text field named "TEST" - Remove it from the view - Exit studio - Go to the website - Click on new - Add a new blogpost - Set a title and save - Click on "Contact & Forms" - Click on the first block - Click on the form - Change the form action to "Create a ticket" - Click on "+ Field" - Change the Type selection to "TEST" - Click on save - Enable debug mode - Go to "Settings / Technical / Database Structure / Fields" - Type x_ in the search bar and press enter - Delete the field => lxml.etree.XMLSyntaxError Cause of the issue ================== When deleting a field, `_check_if_used_in_website_form` is called to prevent the deletion if a field is used in an html field. The html fields were parsed with an xml parser.. opw-5946029 Forward-Port-Of: odoo/odoo#256066
This update fixes a validation error that prevented users from being linked to multiple employees within the same company. The issue stemmed from a miscalculation of employee IDs during user creation, particularly in a non-sudo environment. The fix ensures accurate employee assignment by always performing the necessary search operations within a sudo context.
Original PR description
**Steps to reproduce** - Install `pos_hr` with demo data - Open Settings > Manage Users - Validation Error: A user cannot be linked to multiple employees in the same company **Cause** `employee_id`…
**Steps to reproduce** - Install `pos_hr` with demo data - Open Settings > Manage Users - Validation Error: A user cannot be linked to multiple employees in the same company **Cause** `employee_id` for the current user was computed as False here: https://github.com/odoo/odoo/blob/361aa8505506f9686b1cdb244ba1723ee3f06f7b/addons/pos_hr/models/pos_config.py#L27 Despite an employee already existing, which led to the error here: https://github.com/odoo/odoo/blob/361aa8505506f9686b1cdb244ba1723ee3f06f7b/addons/pos_hr/models/pos_config.py#L30 This exposes an issue with `_compute_company_employee`: - the compute is called a first time on multiple users, including the current user, in a non-sudo environment - the `employee_id` field for the current user is accessed in a sudo environment The problem comes from the search in non-sudo, which uses an `ir.rule` that evaluates `user.employee_id` in sudo while we are computing `user.employee_id`. **Fix** We avoid the cache issue by always performing the search in sudo. opw-6046297
This update resolves an issue preventing customers from viewing product ratings on the website. Previously, a technical restriction limited access to rating data for public users. This change ensures all customers can see and interact with product ratings, improving the shopping experience. The fix addresses an AccessError related to product variant permissions.
Original PR description
**Steps to produce:** - Install the `Ecommerce` module. - Create a product. - In the Sales tab, set an alternative product and ensure both are published. - Open the product page on the website and enable `reviews` from the editor. - Open the same product page in incognito mode. **Issue:** ``` AccessError: You do not have enough rights to access the field "rating_avg" on Product Variant (product.product). ``` Root cause: --- - Currently, product records in dynamic snippets to be fetched without superuser privileges. Since the `rating_avg` field is restricted to internal users, public visitors encounter an `AccessError` when viewing snippets with ratings enabled. - Similar approach used [here]. [here]: https://github.com/odoo/odoo/commit/12bb994da4c3222e8c7fb2df95c202a6c45a28b0 opw-6065319 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#257400 Forward-Port-Of: odoo/odoo#256570
This update fixes a confusing issue where RFQs sent via the list view's 'Send by mail' action didn't automatically update their status. Now, RFQs are correctly marked as 'Sent' immediately after email delivery, matching the behavior of sending from the form view. This improves user experience and eliminates the need for manual re-sending.
Original PR description
Issue Before This Commit: ======================= When RFQs are sent using the `Send by mail` action from the list view, their state remains draft instead of being updated to sent, so users see them…
Issue Before This Commit: ======================= When RFQs are sent using the `Send by mail` action from the list view, their state remains draft instead of being updated to sent, so users see them as not sent even though they were already emailed and have to send them again using the `Send RFQ` button from the form view to mark rfq as sent, which is inconsistent with the form view behavior and confusing for users. Steps to Reproduce: ======================= - Install the `Purchase` app. - Go to Purchase and select multiple RFQs in the list view. - Click `Send by mail` from the actions menu. - Select the RFQ email template and send the email. - Observe that the RFQs remain in state RFQ instead of being set to RFQ Sent, unlike when using the `Send RFQ` button in the form view. Cause of the issue: ======================= The `Send by mail` action in the list view does not apply the same state update logic as the `Send RFQ` button from the form view, where the RFQ state is updated when the email is posted on the purchase order. As a result, when emails are sent from the list view (mass mailing flow), the RFQ state is not updated After This Commit: ======================= When emails are sent using Send by mail, purchase orders in state draft are updated to sent in `_message_mail_after_hook` after the email is sent. This keeps the list view flow consistent with the form view behavior and prevents users from having to resend RFQs just to update the state. TaskID-5443248
This update ensures that the price of a combo order is accurately applied to any additional items added as extras. Previously, when all sub-items were ordered without free quantities, the system incorrectly priced these extra items at the base price, leading to inaccurate order totals. This fix corrects this issue by distributing the parent combo's list price proportionally to the extra items.
Original PR description
When all sub-combos have qty_free=0, no child lines were classified as free, leaving remaining_total (= parent list price) undistributed. Extra lines were priced at base_price only, silently dropping the parent combo price. Fix by mirroring the JS computeComboItems logic: before processing extra lines, compute their proportional denominator and allocate remaining_total to each extra line as a share of parent_lst_price, with a per-unit rounding correction on the last line. opw-6045562 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#254665
This change fixes an issue where new projects created from CRM leads using templates incorrectly inherited the template's company. Now, the project automatically uses the lead's company, ensuring accurate company information and preventing user errors. This improves data consistency and the user experience.
Original PR description
When creating a project from a CRM lead using a template, the `default_company_id` set by `_get_project_create_from_lead_context` is ignored because `company_id` is not in the template default context whitelist. This causes the new project to inherit the template's company instead of the lead's company, resulting in a company mismatch with the partner and a UserError. https://github.com/odoo/odoo/blob/2b89db4af2265e5cec8feb11853364e293de203e/addons/crm_sale_project/models/crm_lead.py#L37-L45 https://github.com/odoo/odoo/blob/2b89db4af2265e5cec8feb11853364e293de203e/addons/project/models/project_project.py#L1413-L1419 Steps To Reproduce: 1. Go to CRM, create or open a lead. 2. Clear the contact field and save. 3. Click the gear icon → Create Project. 4. Select any project template (not empty) and submit. Ticket [link](https://www.odoo.com/odoo/project.task/5933253) opw-5933253 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update resolves an issue where deleting a public holiday incorrectly created timesheets for all related leave requests, even those that were refused. Now, deleting or modifying a holiday only generates timesheets for valid, approved leaves, streamlining the timesheet process and preventing unnecessary entries.
Original PR description
…d leaves Description of the issue/feature this PR addresses: When a public holiday is edited or deleted, the timesheet re-creation is erroneously done for *all* leaves, even those which are canceled or still in draft. Steps to Reproduce: 1. Create a Time Off request for a timesheet-creating leave type (i.e. `timesheet_generate = True`) that overlaps with a public holiday. 2. Refuse the Time Off request. 3. Delete the public holiday the request overlaps with. Current behavior before PR: The deletion of the holiday causes timesheet entries to be created, even though it's a refused request. Desired behavior after PR is merged: The deletion or editing of the public holiday only re-creates the timesheets for the leaves that are actually valid and thus need timesheet entries. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#255155 Forward-Port-Of: odoo/odoo#250372
This update resolves an issue where multi-day 'Half Day' time off requests were incorrectly generating a single timesheet entry. The fix ensures that requests for multiple days now create individual timesheet entries for each day of the leave, accurately reflecting employee hours. This improves the accuracy of timesheet reporting.
Original PR description
Steps to reproduce: ------------------- 1. Install Timesheets and Time Off. 2. Create a new Time Off type with duration type "Half Day" (disable "Require allocation" to skip allocations). 3. Enable…
Steps to reproduce: ------------------- 1. Install Timesheets and Time Off. 2. Create a new Time Off type with duration type "Half Day" (disable "Require allocation" to skip allocations). 3. Enable "Timesheets" in Timesheets > Configuration. 4. Ensure the employee is configured with a "Flexible" working schedule. 5. Create a multi-day Time Off request (2 days) using this type and approve it. 6. Check the timesheet entries for the employee. Issue: ------ When a multi-day time off request is submitted for a "Half Day" time off type, the system fails to generate individual daily timesheet entries. Instead, it generates a single timesheet entry with a duration calculated as calendar.hours_per_day / 2, regardless of the actual number of days requested. (e.g., if hours_per_day is 8, it generates one entry of 4 hours for a 2-day leave), Cause: ------ After this commit 9512832, multi-day requests were allowed for time off types where the **request_unit** is "half day" or "hours". However, the logic in `_generate_timesheets` for flexible calendars was not updated to account for this. Previously, `request_unit_half` was computed as: https://github.com/odoo/odoo/blob/feb3cd520d7a736996621f7765184edca6ac390c/addons/hr_holidays/models/hr_leave.py#L319-L323 But now, the condition is: https://github.com/odoo/odoo/blob/14ccd1f54e21c07ac68593c8dec7682371f8663c/addons/hr_holidays/models/hr_leave.py#L467-L470 Since `request_unit_half` now stays **True** regardless of the request duration (as long as the Type is 'half_day'), the following condition in `_generate_timesheets`: https://github.com/odoo/odoo/blob/14ccd1f54e21c07ac68593c8dec7682371f8663c/addons/project_timesheet_holidays/models/hr_leave.py#L39 always evaluates to **True**. This traps multi-day requests in the "single-day" logic, preventing the code from reaching `_list_work_time_per_day`. Solution: --------- Update the condition to strictly check if the request is a single day before applying the simplified single-entry logic. If the request spans multiple days, it will now correctly fall into the else block, utilizing `_list_work_time_per_day` to generate one timesheet entry per day of the leave. opw-5715197 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#248605