Thursday, April 2, 2026
10 changes · saas-18.3
Enhancements to existing features
This update incorporates the latest withholding tax percentages required by Ecuadorian regulations (Resolución N.º NAC-DGERCGC26-00000009). The changes ensure Odoo accurately calculates and reports withholding taxes for Ecuadorian businesses, maintaining historical data and aligning with internal TRESCLOUD guidelines. This update corrects existing naming inconsistencies and improves data accuracy.
Original PR description
Implement the new withholding tax percentages according to "Resolución N.º NAC-DGERCGC26-00000009" for Ecuador, following internal implementation guidelines by TRESCLOUD. SPECIFICATION: - Created the new withholding percentages as new tax records. - Set the previous withholding percentages as inactive to preserve historical data. - Ensured compatibility with existing tax configurations and fiscal mappings. Table with the changes established in "Resolución N.º NAC-DGERCGC26-00000009". <img width="1676" height="303" alt="image" src="https://github.com/user-attachments/assets/79ae91b2-6d31-442f-af3c-74304742c8b6" /> BP: #252917 Forward-Port-Of: odoo/odoo#254240 Forward-Port-Of: odoo/odoo#254018
Resolved issues and error corrections
This update ensures that the total value displayed for stock items remains consistent between versions 18 and 19. The change corrects a calculation error where only internal locations were considered, leading to discrepancies when migrating to the newer version. This update ensures accurate stock valuation reporting.
Original PR description
**Steps to reproduce** - Create a database in v18.0. - Install the stock and sale modules. - Create a storable product. - Add 100 units as on-hand quantity using an inventory adjustment in the…
**Steps to reproduce** - Create a database in v18.0. - Install the stock and sale modules. - Create a storable product. - Add 100 units as on-hand quantity using an inventory adjustment in the internal location (e.g., WH/Stock). - Create a transit location. - Create an internal transfer and move 20 units from the internal location (WH/Stock) to the Transit location. - WH/Stock now contains 80 quantities. - Transit location contains 20 quantities. - See stock valuation of product. **Issue** - In v18, `value_svl` and `quantity_svl` are computed from [Stock Valuation Layers](https://github.com/odoo/odoo/blob/18.0/addons/stock_account/models/product.py#L213) After that, `total_value` is calculated using the formula [`avg_cost * qty_available`](https://github.com/odoo/odoo/blob/18.0/addons/stock_account/models/product.py#L205). However, when calling `qty_available`, no location context is passed to specify which locations should be considered. Because of this, `qty_available` only considers quantities from internal locations. As a result, the final `total_value` is computed based only on the internal location quantity (80). - When the customer migrates the database to v19, the displayed `total_value` changes. In v19, both (100 qty) internal and transit locations are considered when computing the quantity used for valuation. This happens because a valuation [context](https://github.com/odoo/odoo/blob/19.0/addons/stock_account/models/product.py#L203) is passed when selecting the locations for the quantity computation. Due to this change, the quantity used in the calculation includes both internal and transit locations, which can lead to a different `total_value` being displayed compared to v18. **Solution** - To resolve this issue, the valuation context has been passed when computing the value in the function. This context includes both internal and transit locations. As a result, when `qty_available` is computed, it considers these locations, and the resulting `total_value` remains consistent with the valuation logic. opw-5924110 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#254120
This update fixes an issue where the customer stock report incorrectly showed lots as unreturned when multiple partial returns were made. The change ensures that returned lots are accurately identified and displayed, preventing duplicate records and improving the reliability of the report. This ensures accurate stock reporting for customers.
Original PR description
### Issues: The customer stock.lot.report is not well behaved with respect to multiple partial returns which can lead to returned lots that are not correctly flagged as returned and to duplicate…
### Issues: The customer stock.lot.report is not well behaved with respect to multiple partial returns which can lead to returned lots that are not correctly flagged as returned and to duplicate records. ### Steps to reproduce: - Create a product tracked by SN and put 3 units in stock SN1, SN2, SN3 - Create, confirm and validate a delivery for these two units for Bob. - Click Return, return 1 unit and validate the return for the SN1 - Click Return, return 1 unit and validate the return for the SN2 - Open the contact form of Bob > Lots serial numbers smart button #### > There are two lines referring to SN2 both are flagged as un-returned ### Cause of the issue: In order to determine if a lot has been returned the `stock.lot.report` joins the stock_move_line table with it self based on picking and returns of these: https://github.com/odoo/odoo/blob/109f829c2b461b14167e9227e42d096d4410a3b3/addons/stock/report/stock_lot_customer.py#L48-L71 Records are then grouped to represent single move lines and a move line is expected to be returned to be returned if it is related to at least one move line on a return of its picking sharing the same lot related data (see the definition of `has_return`): https://github.com/odoo/odoo/blob/109f829c2b461b14167e9227e42d096d4410a3b3/addons/stock/report/stock_lot_customer.py#L22-L34 Now the issue is that the group by close actually group records based on the `sml_return.id`: https://github.com/odoo/odoo/blob/109f829c2b461b14167e9227e42d096d4410a3b3/addons/stock/report/stock_lot_customer.py#L73-L89 which does not make sense since we expect to aggregate `sml_return.id`'s to compute the `has_return` field and since we do not want a move line of the original delivery to appear twice simply because it is linked to two returns one with and one without returned move line. opw-5974155 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256396
This update fixes a flaw in the website's leaderboard that incorrectly ranked users based on their recent activity. The system now accurately calculates and displays user rankings by weekly and monthly karma gains, ensuring a fairer and more relevant leaderboard experience. This improvement ensures users are recognized for their current performance.
Original PR description
[FIX] website_profile, gamification: fix weekly/monthly leaderboards Prior to this commit, the leaderboard pagination logic was flawed when filtering by specific time periods (e.g., "This Week" or…
[FIX] website_profile, gamification: fix weekly/monthly leaderboards Prior to this commit, the leaderboard pagination logic was flawed when filtering by specific time periods (e.g., "This Week" or "This Month"). The system would first retrieve users sorted by their *all-time* global karma, apply pagination (taking the top X users), and only then calculate the karma gain for the specific period for those few users. This caused users with high recent activity but low all-time karma to only be displayed much later in the page order than they should. This commit fixes the issue by introducing a pre-search step that calculates the karma gain for the requested period at the database level. Pagination is now applied to this specific result set, ensuring users are correctly ranked by their actual performance during that week or month. Note: A new method `_get_users_by_tracking_karma_gain` was added to `res.users` to handle this logic. This approach was chosen to strictly preserve the signature of existing methods for the stable version. A distinct refactor to unify these calculation methods is planned for the master branch. Steps to reproduce: - Install the eLearning module. - Create a few users with different karma_points (more than 25 to have 2 pages). - Go to /profile/users. - Group by week. - Paginate, and you will notice that the order is wrong; the first user on the second page might have more points than users on the first page. Also, when the logged-in user is not on that page, they do not appear at the bottom. task-5344657 opw-3979785 Forward-Port-Of: odoo/odoo#256908 Forward-Port-Of: odoo/odoo#176626
This update resolves an issue where deleting certain fields within the website form functionality caused a crash. The problem stemmed from how website fields were incorrectly parsed as XML. The fix ensures that field deletions are handled correctly, preventing unexpected errors and improving website stability.
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 an issue where email notifications were incorrectly routing external emails as internal aliases. The change enhances the system's ability to accurately filter internal system emails based on allowed domains, preventing potential notification errors and ensuring correct recipient targeting. This improves the reliability of our email communication.
Original PR description
The fix introduced in https://github.com/odoo/odoo/pull/216737 can lead to "over-eager" filtering when an external email address matches a localpart (left part) alias in a input email list contains…
The fix introduced in https://github.com/odoo/odoo/pull/216737 can lead to "over-eager" filtering when an external email address matches a localpart (left part) alias in a input email list contains internal emails (aliases to filter) AND external email addresses (should not be filtered). The `_find_aliases` method is used to identify internal system emails (aliases, bounces, catchalls) to prevent mail loops and ensure correct recipient filtering during notification grouping. Before this fix, when the `mail.catchall.domain.allowed` system parameter was set, the logic for local-part aliases (where `alias_incoming_local` is True) failed to correctly associate the local part with the allowed domains. This resulted in external email addressed being returned by the system, potentially leading to incorrect notification routing. We now use a more robust approach: - Pre-filter local parts based on the allowed domains to reduce DB load. - Utilize Python Sets for O(1) lookups of static and local aliases - Explicitly validate the (local_part, domain) combo during the final filtering. Example Scenario: - Config: mail.catchall.domain.allowed = "test1.com,test2.com" - Alias: "info" (alias_incoming_local=True) - Input: ["info@test1.com", "info@test3.com"] ### Output Before Fix: ["info@test1.com", "info@test3.com"] (The function failed to recognize info@test3.com as an external alias to be ignored based on the `mail.catchall.domain.allowed` config) ### Output After Fix: ["info@test1.com"] (Correctly identifies the internal alias tob filtered while ignoring the external one) OPW-5469264 OPW-5504201 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#244272
This update fixes an issue where PEPPOL self-billing invoices weren't correctly including the GLN number or delivery address. The change ensures that the delivery address from the company partner is used in the generated XML, improving compliance with PEPPOL standards and accurate invoice data transmission.
Original PR description
**PROBLEM** When selfbilling with peppol, we have no way of providing a GLN number, or modifying the delivery address. Even if we create a delivery address partner on the current company partner, it's not taken into account. **STEP TO REPRODUCE** 1. Create a delivery address on the current company, set up a GLN number. 2. Configure the purchase journal to do selfbilling. 3. Create a vendor bill with this journal and send it using peppol. 4. Download the xml, and look for the Delivery tag, and notice it doesn't have the GLN number. **FIX** We search for a delivery address on the current company. If there is one, we use it for the Delivery tag. opw-6014374 Forward-Port-Of: odoo/odoo#252970
This update fixes an issue where shipping costs were incorrectly calculated when using combo products with delivery methods based on quantity. The fix ensures that shipping costs accurately reflect the quantity of individual components within the combo, preventing inflated shipping charges. This improves the accuracy of shipping calculations for customers using combo products.
Original PR description
**Issue:**
When using a delivery method that has a shipping cost based on the quantity of the product, the shipping cost is incorrect if there is a combo product. The quantity of the combo product was added to the total quantity of its components.
**How to reproduce:**
1. Create a delivery method based on rules.
2. Create a rule that uses the quantity (ex: 0$ + 5$ times the quantity)
3. Create a combo product
4. Create a sale order and add the combo product to it
5. Add the shipping
=> The shipping cost is incorrect
ex: With 1 combo choice, the shipping cost is doubled
**Fix:**
When calculating shipping cost, skip the sale order line of the combo product and only use the sale order lines of the components.
opw-6016209
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#256978
Forward-Port-Of: odoo/odoo#256055This update resolves an issue preventing website users from seeing product ratings due to access restrictions on product data. The change allows dynamic snippets to be accessed without superuser privileges, ensuring public visitors can view and interact with product ratings. This improves the user experience for customers browsing products.
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
This update fixes an error in the Mod 347 BOE export for Spanish companies, ensuring the correct indicators ('C' and 'S') are used for substitutive and complementary declarations. This prevents the AEAT from misinterpreting the file, ensuring accurate tax reporting and compliance.
Original PR description
Currently, the BOE export for `Mod 347` uses incorrect indicators for `Substitutive` and `Complementary declarations`. **Steps to reproduce:** - Install the `l10n_es_reports` module and switch to the…
Currently, the BOE export for `Mod 347` uses incorrect indicators for `Substitutive` and `Complementary declarations`. **Steps to reproduce:** - Install the `l10n_es_reports` module and switch to the `ES company` - Navigate to Accounting > Reporting > Tax Report - From the smart button, select `Report: Tax Report (Mod 347) (ES)` - Download the BOE file using the dropdown. - In the wizard: - Enable `Substitutive Declaration` or `Complementary Declaration` - Set `Previous Report Number` (e.g., 123456789) - Click `Generate BOE` - Upload the generated .txt file to the `AEAT portal`. (AEAT credentials are required) **Observation:** AEAT does not recognize 'X' as a valid indicator for substitutive or complementary declarations and interprets the file as a standard return. **Root Cause:** At [1], the BOE Mod 347 generation writes 'X' for both substitute and complementary declarations. **Fix:** This commit ensures the file contains correct indicators: - 'C' for `complementary declarations` - 'S' for `substitute declarations` This aligns Modelo 347 with AEAT specifications and ensures consistency with the implementation of Modelo 349 at [2]. Ref: https://sede.agenciatributaria.gob.es/Sede/en_gb/ayuda/consultas-informaticas/declaraciones-informativas-ayuda-tecnica/modificar-declaracion-informativa-mediante-fichero.html [1]: https://github.com/odoo/enterprise/blob/c5332bef593cc3fa1b5013a0dac56ccd67e4da14/l10n_es_reports/models/aeat_tax_reports.py#L1061-L1062 [2]: https://github.com/odoo/enterprise/blob/c5332bef593cc3fa1b5013a0dac56ccd67e4da14/l10n_es_reports/models/aeat_tax_reports.py#L1490-L1491 opw-6048711 Forward-Port-Of: odoo/enterprise#112566