Wednesday, July 15, 2026
5 changes · saas-18.3
Enhancements to existing features
This update ensures Peruvian e-invoices can include the product codes required by SUNAT's new 2026 validation rules. It adds missing shared product classification codes and activates Peru-specific codes only for databases using the Peru localization, reducing the risk of invoice rejection for affected goods.
Original PR description
SUNAT is updating its validation rules 2026-08-01, adding three mandatory annexes (25.1, 25.2, 25.3) to Product Catalog N25. E-invoices for these goods are rejected when the required UNSPSC code is not available in the database. Most of the required codes already exist and are active. The rest are handled here: two missing UNSPSC codes were added to the shared catalog, while the fourteen codes that exist but are inactive and the SUNAT-only code 11111111 (which is not part of the UNSPSC standard) are activated from the Peru localization instead. Doing the Peru-specific part in the l10n_pe_edi install hook and upgrade script, the way l10n_mx_edi and l10n_ke_edi_oscu do for their own codes, keeps these activations out of databases that do not use the Peruvian localization. Task-6366907 Forward-Port-Of: odoo/enterprise#124001 Forward-Port-Of: odoo/enterprise#123577
Resolved issues and error corrections
This fix prevents an error when users leave Studio with the browser Back button after editing a project task view. Users can now return smoothly to the task list without crashes or repeated navigation loops.
Original PR description
### Steps to reproduce: 1. Open any Project > open its Tasks view 2. Open Studio 3. Press the browser Back button ### Current behavior: Crash "active_id is not defined". The Tasks view needs…
### Steps to reproduce: 1. Open any Project > open its Tasks view 2. Open Studio 3. Press the browser Back button ### Current behavior: Crash "active_id is not defined". The Tasks view needs active_id (the ID of the open project, e.g. 5) to pre-filter tasks by project, but it is missing when Studio restores the view from the URL. ### Expected behavior: Back exits Studio and returns to the Tasks view with no error. ### Issue: The browser URL tracks navigation as a stack of visited actions. When Studio is open, the stack has two entries: the view being edited (position -2) and Studio itself (position -1). Studio loads the action from position -2 but was reading active_id from position -1 Studio's own slot, which carries no record ID. Reading context from the wrong slot left active_id undefined, crashing the view render. A second problem: Studio was writing active_id into the shared URL state. The router automatically copies this into the Studio URL path, changing ".../tasks/studio" to ".../tasks/5/studio". Every Back press produced a different URL, so the router treated it as a new visit instead of a Back navigation creating an infinite history loop. ### Fix: active_id now comes from the same URL slot as the action identity (position -2), which is where the project ID actually lives. The shared URL write that caused the history loop is removed. task-6097949
Email buttons for appointments now use the website tied to the appointment setup instead of falling back to the last logged-in website or default site. This prevents customers in multi-website environments from being sent to the wrong website when managing their appointment.
Original PR description
In Multiwebsite settings, when the public user interactions needs email generation (appointment or event flow), the email links are generated with a base url that does not corresponds to the one from…
In Multiwebsite settings, when the public user interactions needs email generation (appointment or event flow), the email links are generated with a base url that does not corresponds to the one from which the request started. Case 1: - Have website A and website B - Create an appointment page website A - Log in via website B - As public user, make an appointment in Website A - Check the generated email Issue: button links in the email will redirect to the wrong website, so users will encounter an issue when managing the appointment. This occurs because when an user log in, the system parameter 'web.base_url' is updated with the current url. This parameter is then used as fallback when we need to retrieve the base url without an active record Case 2: - Have website A and website B - Create an event and assign it to website B - As public user, access the event and register to it - Check the generated email Issue: button links in the email will redirect to the wrong website, so users will encounter an issue when managing the event. This occurs because the record `event.registration` has no website_id field and the base url is taken from the company default website (website A) Backport with improvements of 15bae202d8f1b5bf70bbc63b2d89025e9237e6cf opw-4146760 opw-4336369 Forward-Port-Of: odoo/enterprise#123851 Forward-Port-Of: odoo/enterprise#122669
This fix updates POS IoT device matching so newer IoT Boxes are found even when they no longer provide subtype or manufacturer details. This helps printers and payment terminals connect more reliably without requiring missing device information.
Original PR description
Newer IoT Boxes don't share device subtype or manufacturer. We then adapt the domains to avoid searching on fields that aren't filled. task-6388669 task-6388733 Forward-Port-Of: odoo/enterprise#124306
Colombian electronic invoice imports now treat the XML price value as the actual unit price, matching DIAN rules. This prevents incorrect negative discounts on vendor bills when imported invoice lines use quantities greater than one.
Original PR description
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price…
Problem: When importing XML files to generate vendor bills, the system uses UBL parser and assumes that the PriceAmount node needs to be divided by the BaseQuantity node to obtain the exact price unit. However, in Colombia, the DIAN treats the PriceAmount node as the exact price unit. This was not flagged in the system so the parser incorrectly divides the PriceAmount by BaseQuantity, resulting in negative discounts to be added to match the subtotal. Solution: Extract the basis_qty logic into a helper method so other localizations can override when needed. Current behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets incorrectly divided, resulting in negative discounts on the vendor bill. Expected Behavior: When importing a Colombian XML with a product that has a BaseQuantity greater than 1, the PriceAmount gets parses as the exact unit price with no negative discounts applied. task-6215466 Forward-Port-Of: odoo/enterprise#122313