Friday, September 4, 2026
14 changes · saas-19.1
Enhancements to existing features
This update aligns French accounting localization with European VAT handling by adding Monaco to the relevant EU VAT group and creating a separate EU group that excludes Monaco where needed. It also adds localization packages for several French overseas departments and regions, helping businesses apply the correct accounting setup in those territories.
Original PR description
[[IMP] l10n_fr,l10n_fr_account: European union vat](https://github.com/odoo/odoo/commit/9870a204bb64bdf8a11e433eef3a9e9f8f3fad86)
This commit will add Monaco to the European union vat country group and
create a new country group that is the same than the european union but
without monaco
task-6321652
[[ADD] l10n_{gf,gp,mq,re,yt}: new localization](https://github.com/odoo/odoo/commit/58518e1d7df773a70c595bd3842a33ac6be611bb)
Like Monaco, we need to add new localization package for
the drom countries
task-6321652
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#284404Resolved issues and error corrections
Fixes Italian electronic invoices so credit notes and final invoices only reference the relevant down payment document instead of listing unrelated historical documents. This reduces confusing or incorrect FatturaPA XML output for businesses managing orders with multiple down payments and credit notes.
Original PR description
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without…
Steps to reproduce: - Create a sale order with a down payment term. - Invoice the down payment (down payment invoice A). - Credit that down payment invoice directly (credit note A), without reconciling it with anything else. - Generate the FatturaPA XML for credit note A. - Observe that DatiFattureCollegate lists both down payment invoice A and credit note A itself, instead of only down payment invoice A. On an order with several down payment invoices/credit notes over time, every one of them is listed instead of just the document the current credit note actually reverses. Cause of the issue: _l10n_it_edi_export_data built downpayment_moves from self.invoice_line_ids._get_downpayment_lines().move_id with no filtering. _get_downpayment_lines() (sale override) returns every invoice line ever created against the same down payment sale order line, across the whole life of the order, not just the invoice the current document actually relates to. The template renders DatiFattureCollegate for every one of those moves unconditionally, on top of the already-correct reversed_entry_id/reconciled_moves fallback, including the current document itself. Solution: Restrict downpayment_moves to lines with a negative price_subtotal, mirroring the existing down payment deduction-line detection a few lines above, and explicitly exclude the current document. This limits DatiFattureCollegate's down payment entries to the case they are meant for: a final invoice actually deducting a previously invoiced down payment. opw-6429716 Forward-Port-Of: odoo/odoo#285555 Forward-Port-Of: odoo/odoo#279938
DHL shipping in Odoo no longer automatically requests a carrier pickup when creating shipments. This aligns DHL with other shipping carriers and prevents users from needing to configure pickup times or encountering related DHL errors.
Original PR description
Before this commit, the DHL REST module had the pick-up request set, which led to an unnatural use flow, needing to set up an scheduled date for pick-up, and several errors from DHL when these were not properly configured. This feature was unique to this module among the shipping carriers as we don't normally request pick-up for packages and leave it to user to decide how to do it. This commit removes the request for pick up and the logic that was needed to make it work. This will make the behavior consistent with other carriers in Odoo, and avoids the need to set scheduled pick-up windows and having to deal with unnecessary errors. opw-6148927 Forward-Port-Of: odoo/enterprise#123773
This update refreshes Odoo's spreadsheet component with several fixes that improve stability and visual consistency. Users should see fewer crashes, more reliable chart and pivot behavior, and corrected dashboard and font display issues.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/b709c26b4a [REL] 19.1.34 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0)…
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/b709c26b4a [REL] 19.1.34 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/65c8ac8baa [FIX] carousel: cannot add non-existing chart to carousel [Task: 6455790](https://www.odoo.com/odoo/2328/tasks/6455790) https://github.com/odoo/o-spreadsheet/commit/e86d15b996 [FIX] Fonts: fix linux font on css variable [Task: 6452045](https://www.odoo.com/odoo/2328/tasks/6452045) https://github.com/odoo/o-spreadsheet/commit/2c9e7b34e4 [FIX] svg: remove unused commented SVGs [Task: 6317134](https://www.odoo.com/odoo/2328/tasks/6317134) https://github.com/odoo/o-spreadsheet/commit/016f950205 [FIX] chart: fix funnel chart show value [Task: 6475094](https://www.odoo.com/odoo/2328/tasks/6475094) https://github.com/odoo/o-spreadsheet/commit/daef0f7208 [FIX] pivot: unbounded ranges in the pivot side panel [Task: 6478220](https://www.odoo.com/odoo/2328/tasks/6478220) https://github.com/odoo/o-spreadsheet/commit/47b1bd899d [FIX] composer: crash when hovering a composer token [Task: 5153319](https://www.odoo.com/odoo/2328/tasks/5153319) https://github.com/odoo/o-spreadsheet/commit/4209d23f60 [FIX] evaluation: wrong dependencies invalidation with spread formulas [Task: 5103328](https://www.odoo.com/odoo/2328/tasks/5103328) https://github.com/odoo/o-spreadsheet/commit/505eabd39d [FIX] dashboard: fix background color [Task: 6497278](https://www.odoo.com/odoo/2328/tasks/6497278) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
This fix prevents electronic invoice exports from failing in Community Edition when enterprise-only deferred billing fields are unavailable. It checks whether those fields exist before using them, improving reliability for businesses that do not have the enterprise accounting add-on installed.
Original PR description
The fields `deferred_start_date` and `deferred_end_date` are created by the enterprise addon `account_accountant`, so not having it installed, this crashes with:
```
File "/opt/odoo/auto/addons/account_edi_ubl_cii/models/account_edi_cii.py", line 684, in <listcomp>
billing_start_dates += [move_line.deferred_start_date for move_line in invoice.invoice_line_ids if move_line.deferred_start_date]
AttributeError: 'account.move.line' object has no attribute 'deferred_start_date'
```
This PR protects the access to these fields checking first if they exist.
Bug introduced in the refactoring in #261572.
@Tecnativa TT64359
Forward-Port-Of: odoo/odoo#286245The event booth registration page now shows booths for the category a visitor actually selected, even when earlier page requests finish later. This prevents outdated booth options from appearing and helps exhibitors complete booth registration without needing to reload the page.
Original PR description
Before this commit, the "Get A Booth" page of an event could keep listing the booths of the category checked on load after the visitor picks another one. Only a page reload brought the right ones…
Before this commit, the "Get A Booth" page of an event could keep listing the booths of the category checked on load after the visitor picks another one. Only a page reload brought the right ones back. The tour webooth_exhibitor_register timed out on that list: FAILED: [5/13] Tour webooth_exhibitor_register -> Step Choose Booth (trigger: .o_wbooth_booths div:contains(OpenWood Demonstrator 2) input:not(:visible)). TIMEOUT step failed to complete within 10000 ms. This happens because the page fetches the booths of the category checked on load, and clicking another category fetches its booths while that first answer is still on its way. The answer coming back last fills the list, and the widget caches it under the category active on arrival, so the booths of the first category land in the cache of the clicked one. This commit fixes the issue by caching an answer under the category it was asked for, and by filling the list only while that category is still the chosen one. https://runbot.odoo.com/odoo/error/110527 https://runbot.odoo.com/odoo/error/161834 Forward-Port-Of: odoo/odoo#286552 Forward-Port-Of: odoo/odoo#286266
This fix ensures Odoo correctly flags a numbering gap when the latest posted journal entry is reset to draft and a new entry is posted afterward. This helps businesses maintain accurate, compliant accounting sequences without changing already assigned document numbers.
Original PR description
To reproduce: * create and post 2 journal entries in the same journal * reset to draft the entry with the highest number * create and post a third entry in the same journal The draft entry is not flagged as having made a gap, because at the time of resetting it to draft, it was the last entry and therefore didn't really make a gap. 3 options to solve were considered: * flag all moves when reseting to draft even if they were the last of the chain * if we reset the last move of the sequence to draft, also remove its sequence and put it back to `/` * when posting, check if the previous number was draft. If it was the case, flag it. The last options was taken in this fix to avoid changing the previous behavior while fixing the current issue. Forward-Port-Of: odoo/odoo#286204
Turkish Nilvera e-invoices now warn users before sending if invoice lines are missing required taxes, helping avoid rejected submissions. The validation also avoids unnecessary warnings for note or section lines that do not require product or CTSP details.
Original PR description
Nilvera does not accept invoices with lines that do not have taxes, so we added a valiation check for sending the invoice to warn the user. Additionally, we raise a warning when a line does not have a product and has an empty CTSP. However, if the line is a note or section, this warning should not be triggered. task-6404409 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#284377
Fixed an issue where selling a shared product in Point of Sale with an invoice could fail in multi-company setups. The change ensures only vendor information relevant to the active company is used, preventing access errors and keeping checkout workflows reliable.
Original PR description
**Steps to reproduce:** - Install PoS and Purchase - Make 2 companies, A and B - On product "Office Lamp" that is accessed by both companies, put Partner A in the vendor tab for company B - Company A…
**Steps to reproduce:**
- Install PoS and Purchase
- Make 2 companies, A and B
- On product "Office Lamp" that is accessed by both companies, put Partner A in the vendor tab for company B
- Company A should have a Partner A and Partner B in this tab
- In the stock, set a replenishment for company A, with Partner A on Office Lamp
- Go to company B and open the PoS
- Try to buy Office Lamp while requesting an invoice
- An access error appears
**Why the fix:**
When requesting an invoice in the PoS, we try to create the stock picking. Doing so will trigger the replenishment rules linked to the product to be recomputed.
Those are executed when we **flush_all()**, processing Company A's replenishments as sudo(), meaning all of Company A's **seller_id** are fetched and cached. This means the product's **seller_ids** now contains Company A's **seller_id**, even though we are currently in Company B.
While trying to get the product's code, we iterate over **product.seller_ids**, but we do not have access to every record in that product.
https://github.com/odoo/odoo/blob/b8e5291d103d9f43bd8db6d2dfe708076a57ea37/addons/product/models/product_product.py#L337-L343
As we don't have access to those, we get an access error when we stumble upon it.
To avoid those errors, we now filter the sellers to only have the ones compatible with our current Company in the given product we are currently buying.
Another solution would be to do **product.invalidate_recordset(['seller_ids'])** before looping over it, but feels more like a band-aid than the current fix IMO.
We could also write **self.lines.product_id.mapped('code')** in **_create_order_picking(self)** to have the solution be in PoS directly, but the error might arise from somewhere else at some point, and this just hides the issue by adding the code to the cache so that we don't have to fetch it again later.
opw-6308182
Forward-Port-Of: odoo/odoo#275354This fixes an issue where Mexican CFDI payment complements could be rejected by the tax authority provider when invoices in USD were partially paid in MXN. The calculation now matches the rounded values shown in the XML, helping users successfully submit affected payment complements.
Original PR description
**Steps to reproduce:** * Install the **l10n_mx** localization. * Enable **USD** and fetch the latest currency exchange rate from the settings. * Configure **Quadrum** as the **PAC** in the settings.…
**Steps to reproduce:**
* Install the **l10n_mx** localization.
* Enable **USD** and fetch the latest currency exchange rate from the settings.
* Configure **Quadrum** as the **PAC** in the settings.
* Create and sign a USD invoice with **16% IVA** (e.g. 4,500 USD + 720 USD tax = 5,220 USD total).
* Send the invoice to **CFDI**.
* Register a **partial MXN payment** that does not convert to a round USD amount (e.g. 30,000 MXN = 1,762.45 USD).
* Send the payment complement to the PAC by clicking **Update Payments** on the invoice.
**Observed behavior:**
The PAC rejects the CFDI with error **CRP20268**:
> El campo BaseP que corresponde a Traslado, no es igual a la suma de
> los importes de las bases registrados en los documentos relacionados
> donde el impuesto del documento relacionado sea igual al campo
> ImpuestoP de este elemento y la TasaOCuotaDR del documento
> relacionado sea igual al campo TasaOCuotaP de este elemento.
**Cause:**
* The SAT validator enforces a strict arithmetic relationship between three fields that are printed in the payment complement XML:
```
BaseP == round(BaseDR / EquivalenciaDR, 6)
```
* When `percentage_paid` is a non-terminating decimal (which happens for any partial MXN payment against a USD invoice), the internal `raw_base` float carries more precision than the 6-decimal-place `BaseDR` that is actually written to the XML:
```
percentage_paid = 1762.45 / 5220 = 0.33763409961685825...
raw_base = 4500 × 0.33763... = 1519.353448275862...
BaseDR (XML) = float_round(raw_base, 6) = 1519.353448
```
* The old code then used `raw_base` (the unrounded internal value) as the dividend when computing BaseP:
```
BaseP (old) = float_round(1519.353448275862... / 0.0587483333, 6)
= 25862.068980 ← written to <TrasladoP BaseP="...">
```
* The PAC performs the same division using only what it can read from the XML. The already-rounded BaseDR:
```
BaseP (PAC) = round(1519.353448 / 0.0587483333, 6)
= 25862.068975
```
* The 6th-decimal mismatch (25862.068980 ≠ 25862.068975) triggers CRP20268 and the document is rejected.
* The same issue occurs with a full MXN payment on a different date than the invoice when multiple invoice lines cause the rounded aggregate to diverge from the sum of per-line raw values.
**Fix:**
In `_l10n_mx_edi_add_payment_cfdi_values` (`account_move.py`), the block that builds the BaseP aggregation list was changed from iterating over raw per-`base_line` amounts to building a single synthetic entry per invoice using the already-rounded document-level `BaseDR` values (`tax_details['base']`) as the dividend:
- Before : wrong: raw_base ≠ BaseDR printed in the XML
'raw_base': tax_details['raw_base'] / inv_rate
- After : correct: 'base' == BaseDR, the exact value in the XML
'raw_base': tax_details['base'] / inv_rate
This guarantees that Odoo and the PAC divide the identical value, producing the identical 6-decimal result.
The `base_line_cfdi_values_mx_curr_list` path (used only for the `Totales` summary fields rounded to 2 dp) is left unchanged because the CRP20268 rule does not apply to that block.
opw-6468077
Forward-Port-Of: odoo/enterprise#128357Stripe SEPA payments could fail when an order reference contained only numbers, because Stripe requires a statement descriptor with at least one letter. The payment description now uses the company name again, helping customers complete SEPA payments reliably.
Original PR description
**Steps to reproduce:** 1. Install Sales, l10n_be, payment_stripe and switch to belgian company 2. Switch the admin user's company to the belgian one, and update their contact address to be in Belgium 3. Enable stripe payment provider 4. Add SEPA payment method in stripe configuration 5. Create a sale order with a name that doesn't include any characters (numbers only), confirm, click preview and attempt to make a payment using SEPA **Issue:** `The statement descriptor must contain at least one Latin character.` **Cause:** The previous fix (4fde0232b821c9a1d46d589ff14495f4e19029f4) passed the order reference directly, assuming it will contain characters. The intended behavior is to actually have the company name used in the statement descriptor field: https://support.stripe.com/questions/what-is-a-statement-descriptor-and-how-do-i-update-it This was the existing behavior before the fix, so will revert back to it. opw-6497127 Forward-Port-Of: odoo/odoo#286287
Portal users who follow a project can now open tasks they are allowed to view without incorrectly seeing a not found error. This improves the customer portal experience by making task access consistent across task lists and direct task pages.
Original PR description
Before this commit, the portal user could have a request not found when he wants to access to a task from a project he follows even if he can access to the task in /my/tasks route. This commit makes sure the read access are checked instead of checking if the user can access to the task thanks to the token since the token if the one of the project. Forward-Port-Of: odoo/odoo#285878
This fix ensures product costs on invoices reflect the actual value of delivered stock movements instead of recalculating everything from the latest average price. It prevents incorrect negative cost entries when additional quantities are delivered and invoiced later, improving accuracy of margins and accounting reports.
Original PR description
## HOW TO REPRODUCE: - Create a product AVCO Perpetual - Receive 10 units with a unit price of $10 => Product average price is now $10 - Create a Sale Order for 10 units, confirm, deliver and invoice…
## HOW TO REPRODUCE:
- Create a product AVCO Perpetual
- Receive 10 units with a unit price of $10 => Product average price is now $10
- Create a Sale Order for 10 units, confirm, deliver and invoice => COGS are at $100
- Receive 1 unit with a price of $5 => Product average price is now $5
- Update SO and add 1 unit, deliver and invoice this new unit => New invoice COGS is $-45
This is because Odoo computes the COGS for the whole SO, and decided that the value of the deliveries is the `quantity * standard_price`, which would means `11 units * $5 = $55`. Because the 1st invoice has a COGS balance of $ 100, the 2nd invoice is set to $ -45.
This behavior is inconsistent with how it was done in previous versions. Furthermore, if we added the +1 unit in a new invoice, the COGS would have been $ 5, for a total products COGS of $ 105.
- - -
With this fix, the COGS are computed using the average of the move value. So if the first move value is $100, and the second is $5, then the global COGS for the sale order should be $105. Because the first invoice COGS are already at $100, the second invoice COGS must be $5.
OPW-6442049
---
## TEST RESULT WITHOUT FIX:
```
2026-08-21 08:00:11,722 28203 INFO oes_test_19.0 odoo.addons.sale_stock.tests.test_anglo_saxon_valuation: Starting TestAngloSaxonValuation.test_cogs_average_multiple_invoices_and_deliveries ...
2026-08-21 08:00:13,055 28203 INFO oes_test_19.0 odoo.addons.sale_stock.tests.test_anglo_saxon_valuation: ======================================================================
2026-08-21 08:00:13,055 28203 ERROR oes_test_19.0 odoo.addons.sale_stock.tests.test_anglo_saxon_valuation: FAIL: TestAngloSaxonValuation.test_cogs_average_multiple_invoices_and_deliveries
Traceback (most recent call last):
File "/home/odoo/Odoo/src/19.0/odoo/addons/sale_stock/tests/test_anglo_saxon_valuation.py", line 1935, in test_cogs_average_multiple_invoices_and_deliveries
self.assertRecordValues((cogs_line_1 | cogs_line_2), [
File "/home/odoo/Odoo/src/19.0/odoo/odoo/tests/common.py", line 727, in assertRecordValues
self.assertSequenceEqual(expected_reformatted, record_reformatted, seq_type=list)
AssertionError: Lists differ: [{'ac[142 chars]it': 5.0}, {'account_id': 3566, 'debit': 5.0, 'credit': 0.0}] != [{'ac[142 chars]it': 45.0}, {'account_id': 3566, 'debit': 45.0, 'credit': 0.0}]
First differing element 2:
{'account_id': 3536, 'debit': 0.0, 'credit': 5.0}
{'account_id': 3536, 'debit': 0.0, 'credit': 45.0}
[{'account_id': 3536, 'credit': 100.0, 'debit': 0.0},
{'account_id': 3566, 'credit': 0.0, 'debit': 100.0},
- {'account_id': 3536, 'credit': 5.0, 'debit': 0.0},
+ {'account_id': 3536, 'credit': 45.0, 'debit': 0.0},
? +
- {'account_id': 3566, 'credit': 0.0, 'debit': 5.0}]
+ {'account_id': 3566, 'credit': 0.0, 'debit': 45.0}]
? +
```
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#283778This fix stops duplicate French VAT XML declarations from being sent to Aspone when they would be rejected anyway. It helps avoid unnecessary provider charges and gives businesses a cleaner submission process.
Original PR description
When sending an xml to aspone, they will check if a duplicate declaration exist, and if it's the case, then they will refuse it. Since contacting aspone cost us money we will block the sending before that. task-6420220 Forward-Port-Of: odoo/enterprise#127953