Friday, April 17, 2026
10 changes · saas-18.3
Enhancements to existing features
This update replaces the unreliable VIES check with a more robust system using the IAP server. This improves the accuracy of EU VAT ID validation, particularly for French partners, and reduces potential errors during Tax ID changes.
Original PR description
Currently, when changing the Tax ID of a partner that is another EU country, we perform a VIES check to know whether it is valid (i.e. can do intra-com). However, it is often the case that the VIES check fails because of an internal error on their side (timeout, max concurrent update, ...), especially for France. Instead, we will now use the IAP server which stores the validity of a Tax ID for some time. If the IAP server does not have the info (because VIES is down), we will not actively wait. Instead, IAP will perform a push to a webhook on the client database once it has the information. For security purposes, an HMAC is generated and sent to IAP so that only IAP can contact the db with the up-to-date info, and not anyone on the internet that calls this new webhook. There is also a cron for polling for OnPrem instances that cannot be contacted via the webhook. task-5977584 Forward-Port-Of: odoo/odoo#259565 Forward-Port-Of: odoo/odoo#258155
Resolved issues and error corrections
This update fixes an issue where Mercado Pago webhooks with invoice references containing slashes (like INV/2026/00001) were not being correctly processed, resulting in a 404 error. The change allows the webhook to handle these references, ensuring accurate processing of Mercado Pago payments. This improves the reliability of payment processing.
Original PR description
Currently, the mercado_pago_webhook http route only takes into consideration 1 url segment. This means that invoices with references like INV/2026/00001 don't match any defined route and the server returns a 404. /payment/mercado_pago/webhook/S00001 => OK /payment/mercado_pago/webhook/INV/2026/00001 => KO This commit allows references with slashes to be matched by the route by capturing the entire remaining url path including the slashes. /payment/mercado_pago/webhook/S00001 => OK /payment/mercado_pago/webhook/INV/2026/00001 => OK opw-6035161 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#259378
This update resolves an issue where the Point of Sale app on iOS/Safari would unexpectedly crash due to a lost connection to its local database. The fix prevents crashes when the app goes to the background or when the operating system temporarily closes the database connection. This ensures a more reliable and stable Point of Sale experience for our iOS users.
Original PR description
On iOS/Safari, the WebKit IDB server process can be killed by the OS (e.g. due to memory pressure when the app is backgrounded), resulting in an UnknownError: "Connection to Indexed Database server lost". Additionally, returning from background can leave the connection in an InvalidStateError "closing" state while this.db remains non-null. opw-5121896 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256401 Forward-Port-Of: odoo/odoo#253943
This update corrects a rounding issue in the Peppol invoice XML generation, preventing validation errors related to line amount calculations. The fix ensures accurate invoice formatting for Peppol compliance, avoiding potential delays in electronic invoice processing. This improves the reliability of our Peppol integration.
Original PR description
**PROBLEM** Previously, we rounded the unit price up to 6 digits in the generated xml for peppol. However, odoo compute the lineExtensionAmount with the raw unit price. The generated xml is invalid because `priceAmount*InvoicedQuantity != LineExtensionAmount`. **STEP TO REPRODUCE** 1. Create an invoice with unit price of 0.01110515964, and quantity of 278362.5. 2. Generate an XML with peppol, and try validating the invoice. You should have the following error: `[PEPPOL-EN16931-R120]-Invoice line net amount MUST equal (Invoiced quantity * (Item net price/item price base quantity) + Sum of invoice line charge amount - sum of invoice line allowance amount` opw-6009771 Forward-Port-Of: odoo/odoo#255358
This update resolves an issue where invoices weren't automatically marked as paid when using a specific payment setup (default bank account as outstanding account). This prevented invoices from correctly reflecting completed subscription payments. The fix ensures invoices are properly reconciled, streamlining the billing process.
Original PR description
Currently, when a subscription payment is processed through a provider configured to use as outstanding account the bank default account, the resulting invoice may remain in an 'open' state even…
Currently, when a subscription payment is processed through a provider configured to use as outstanding account the bank default account, the resulting invoice may remain in an 'open' state even though the payment transaction is 'done'. This type of configuration is usually done when users want to skip the bank reconciliation process, and just create payments without the need to reconcile the payments with transactions. Steps to reproduce: - In Settings, under Sales > Invoicing, disable Automatic Invoice - Activate demo payment method - In the main Bank account add the default account as outstanding account for demo payment method. - Create a sales order with a subscription product - Open Preview - Pay - Go back to the sales order and open the created invoice Issue: The invoice is created and the payment is registered, but the invoice remains 'Not Paid'. Analysis: The invoice and the payment move lines are not automatically reconciled during the post-processing of the transaction, leaving the invoice unbalanced. opw-5869303
This update fixes a limitation in Odoo Studio where activity filters (past, today, future) weren't consistently working when the 'use_mail' Chatter feature was enabled. The team added necessary filters to the search view, ensuring users can effectively filter activities based on their timeliness within Studio. This enhances Studio's usability for managing and tracking activities related to records.
Original PR description
Steps to reproduce
==================
- Install studio
- Create a new app
- Create a new model
- Keep the Chatter toggled (use_mail)
- Exit studio
- Create three records, one with an activity in the past, one today and one in the future
- Click on the clock status icon in the top right
- There should be a section with the new model
- Click on 1 Late => every records is displayed
- Same for Today and Future
Cause of the issue
==================
https://github.com/odoo/odoo/blob/b6434b91a7f94075e1372ec827787504ef7aa4f0/addons/mail/static/src/core/web/activity_menu.js#L39-L77
For this feature to work, the activities_{overdue,today,upcoming_all} filter should be present
Solution
========
We add them to the search view. They are all pretty much implemented the same way in every model.
opw-6069150
Forward-Port-Of: odoo/enterprise#113011This update corrects a discrepancy in how purchase order move lines handle sub-location destinations. Previously, even with a sub-location configured, move lines defaulted to the main warehouse, preventing accurate forecasted quantity updates. This change prioritizes the sub-location, ensuring that quantities are correctly tracked for specific receiving areas.
Original PR description
### **Description of the issue/feature this PR addresses:** **Issue:** In Odoo 18/19, purchase move lines default to the WH's main stock location (`lot_stock_id`) as the `location_final_id`. However,…
### **Description of the issue/feature this PR addresses:** **Issue:** In Odoo 18/19, purchase move lines default to the WH's main stock location (`lot_stock_id`) as the `location_final_id`. However, when a user configures a sub-location on the Receipt Operation Type, the picking destination is correct, but the move lines are defaulted to the main warehouse. This mismatch causes the Forecasted Quantity to not increment for the intended sub-location **Solution:** Prioritize the `default_location_dest_id` before falling back to the default stock location opw-6032018 ### **Current behavior before PR:** When confirming a PO, the `location_final_id` on stock moves defaults to the `lot_stock_id`, regardless of the specific destination set on the Operation Type. This causes a mismatch in 1-step receiving flows where a sub-location (e.g., WH/Stock/Test) is intended, since the move lines revert to the root warehouse location (WH/Stock). Thus, the forecasted quantity for the specific sub-location doesn't increment as expected. ### **Desired behavior after PR is merged:** The `_get_final_location_record` method will now evaluate if the Operation Type's `default_location_dest_id` is a child of the warehouse's main stock. If it is, the sub-location is used as the `location_final_id` for the moves and move lines. This ensures that the forecasted quantity reflects the intended destination upon PO confirmation while still maintaining the fallback to the warehouse root for standard multi-step routes. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#256725 Forward-Port-Of: odoo/odoo#254527
This update fixes an issue where the Italian tax module was incorrectly removing parts of VAT numbers when generating invoices for Spain. The fix ensures that VAT numbers, like A95758389, are accurately exported to the tax agency's XML files, preventing data discrepancies. This ensures compliance with Italian tax regulations.
Original PR description
**Steps to reproduce:** * Install `l10n_it_edi` module. * Create a partner with country **Spain** and VAT `A95758389`. * Create an invoice and send it to the Tax Agency, and download XML. **Observed behavior:** * The exported XML contains `5758389` in <IdCodice> instead of `A95758389` — the first two characters of the VAT are silently dropped. **Cause:** * In `_l10n_it_edi_get_values`, the EU branch that strips the country-code prefix used a bare `else` after the `isdecimal()` check, unconditionally removing the first two characters of any VAT that does not start with two digits. Spanish NIFs like `A95758389` start with `A9` (letter + digit), which is not a country-code prefix but was treated as one, corrupting the value. **Fix:** * Remove country prefix from normalized VAT by `removeprefix(normalized_country)` Instead of removing the first two characters. It will ensure that only the country prefix will be removed. opw-6089198 Forward-Port-Of: odoo/odoo#258626
This update resolves issues causing crashes when deleting sign templates and breaks in the tracking of document signatures. The fix ensures documents remain stable during template creation and deletion, and accurately maintains the link between signature requests and their original documents, regardless of the number of templates created.
Original PR description
Steps to reproduce: Bug 1 (The Crash): 1. Open Documents app, select a PDF, and click Action > Sign. 2. In the Sign app, delete the newly created Sign Template. 3. Return to the Documents app. 4. A…
Steps to reproduce:
Bug 1 (The Crash):
1. Open Documents app, select a PDF, and click Action > Sign.
2. In the Sign app, delete the newly created Sign Template.
3. Return to the Documents app.
4. A traceback occurs (`KeyError: <document_id>`) in `web_read`.
Bug 2 (The Broken Lineage):
1. Create two separate Sign Templates from the exact same Document.
2. Send a signature request from the second template.
3. The `reference_doc` on the signature request fails to link back to the original Document.
Current behavior:
When creating a sign template from a document, `documents_sign` intentionally unlinks the original `ir.attachment` (`res_model = False`) to pass custody to `sign.document`. If the template is deleted, the attachment is orphaned, permanently corrupting the original `documents.document` and crashing the UI.
Furthermore, the lineage tracking (`reference_doc`) relies strictly on a 1:1 shared `attachment_id`. If a user creates multiple templates from one document, the system is forced to make a copy for the second template, natively breaking the lineage tracking because the IDs no longer match.
Expected behavior:
Documents should not be corrupted when generating or deleting sign templates. Furthermore, lineage tracking (`reference_doc`) should successfully link back to the original document regardless of how many templates have been generated from it.
Fix:
1. Replaced the `res_model = False` custody-handoff hack in `documents_sign` with a safe `.copy({'original_id': attachment.id})`. This sandboxes the Sign app's files, completely preventing the deletion crash and the multi-template conflicts.
2. Updated the `reference_doc` computation in `sign.request` to dynamically search for both the current `attachment_id` AND its `original_id` (utilizing a minimal-diff recordset union `|`). This perfectly preserves the lineage tracking for all templates without requiring database schema changes.
Task: 5432116This update fixes an error in the manufacturing report that incorrectly converted quantities between different units of measure (e.g., Kg and Ton). The fix ensures accurate calculations for produced quantities and average unit costs, resolving discrepancies in reporting. This improves the reliability of production cost analysis.
Original PR description
Steps to reproduce:
- Create a product W1 with UoM = Kg with the following BoM:
- Component C1: 1 unit, cost = $1
- Create and confirm MO1:
- Produce 1 Kg of W1 → total cost = $1
- Create and confirm MO2:
- Produce 1 Ton of W1 → total cost = $1000
- Open the Manufacturing Report and group results
Problem:
- qty_produced ≈ 1.001 instead of 1001
- unit_cost average ≈ 1000 instead of 1
Expected behavior:
- qty_produced = 1001
- unit_cost average = 1 (consistent across MOs)
The manufacturing report (`mrp.report`) incorrectly converts quantities from move UoM to product UoM, leading to wrong `qty_produced` and `qty_demanded` values when different units of measure are used
The current implementation uses:
sm.quantity / uom.factor * uom_prod.factor
This inverts the conversion ratio. As a result:
- 1 Ton is converted to 0.001 Kg instead of 1000 Kg
opw-6097098