Friday, August 14, 2026
14 changes · 18.0
Enhancements to existing features
The Dutch payroll module now includes the 2026 resident income tax rate values. This helps payroll calculations stay aligned with upcoming tax requirements for employees in the Netherlands.
Original PR description
Added 2026 values for the residents' income tax rates rule parameter. task-6462877 Forward-Port-Of: odoo/enterprise#127556
Resolved issues and error corrections
This fix ensures successful report prints properly clean up their connection callback. It prevents later false IoT connection errors after a print has already completed successfully.
Original PR description
The report printing logic was missing a call to `removeListener` on print success, resulting in an IoT connection error later, even on print success. opw-6469551
Documentation and clarification updates
This pull request submits my Odoo Individual Contributor License Agreement signature.
Original PR description
This pull request submits my Odoo Individual Contributor License Agreement signature.
Miscellaneous changes
The Sale Details report (report.point_of_sale.report_saledetails, get_sale_details) repeatedly searches account.payment filtered by pos_session_id while building the payments breakdown: it runs one such search per (session x payment method) pair. Since pos_session_id had no index, every one of those searches was a sequential scan over the whole account_payment table, which degrades badly on databases with a large payment history. Adding index=True lets PostgreSQL resolve each lookup with an i
Original PR description
The Sale Details report (report.point_of_sale.report_saledetails, get_sale_details) repeatedly searches account.payment filtered by pos_session_id while building the payments breakdown: it runs one…
The Sale Details report (report.point_of_sale.report_saledetails, get_sale_details) repeatedly searches account.payment filtered by pos_session_id while building the payments breakdown: it runs one such search per (session x payment method) pair. Since pos_session_id had no index, every one of those searches was a sequential scan over the whole account_payment table, which degrades badly on databases with a large payment history.
Adding index=True lets PostgreSQL resolve each lookup with an index scan.
Measured on a real customer database (Odoo 18.0), Sale Details report for one PoS config over a full month:
Dataset: 29 sessions, 1,317 orders, 212,160 account_payment rows.
Per-lookup query plan (EXPLAIN SELECT id FROM account_payment
WHERE pos_session_id = X):
before: Seq Scan on account_payment (cost=0.00..27263.00)
after: Index Scan using account_payment_pos_session_id_index
(cost=0.29..11.84)
get_sale_details, called directly and timed:
before: 144.0 s (of which 1,628 account.payment searches = 139.6 s, 97%)
after: 5.5 s
=> ~26x faster
Methodology: get_sale_details was called directly on the model. The ORM cache was invalidated (env.invalidate_all()) before the "after" run so the improvement cannot be attributed to caching. The index creation was the only change between the two runs (before: index dropped; after: index created, 0.2 s). The count/time of account.payment searches was captured by instrumenting Model.search during the "before" run.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prPeruvian accounting reports now use the exchange rate already saved on each accounting entry instead of recalculating it later. This reduces rounding differences and helps produce more reliable reported amounts.
Original PR description
Previously, the `_get_ple_report_data` method computed the currency rate when called. Since the calculation was based on the entry totals, it was prone to rounding errors. This PR makes it use the rate stored in the entry itself. This should lead to more accurate results. opw-6411322
### Issue before this commit: When generating an electronic invoice (e.g., ZUGFeRD/Factur-X) with a 0% tax from a non-EEA supplier (e.g., Switzerland) to an EEA customer (e.g., Germany), the XML tax <ram:CategoryCode> is incorrectly set to 'E' (Exempt) instead of 'G' (Export). ### Steps to reproduce the issue: 1. Download Accounting and l10n_ch 2. Set the VAT for the CH company 3. Create an invoice for a German customer with 0% tax setted (for which you have to set as electronic invoicing
Original PR description
### Issue before this commit: When generating an electronic invoice (e.g., ZUGFeRD/Factur-X) with a 0% tax from a non-EEA supplier (e.g., Switzerland) to an EEA customer (e.g., Germany), the XML tax…
### Issue before this commit: When generating an electronic invoice (e.g., ZUGFeRD/Factur-X) with a 0% tax from a non-EEA supplier (e.g., Switzerland) to an EEA customer (e.g., Germany), the XML tax <ram:CategoryCode> is incorrectly set to 'E' (Exempt) instead of 'G' (Export). ### Steps to reproduce the issue: 1. Download Accounting and l10n_ch 2. Set the VAT for the CH company 3. Create an invoice for a German customer with 0% tax setted (for which you have to set as electronic invoicing the ZUGFeRD template into the Accounting tab of his contact) 4. Send it and see that the tag <ram:CategoryCode> is setted as E instead of G ### Cause of the issue: The logic assigning the 'G' and 'K' tax category codes was only triggered if the supplier was located within the EEA. If the supplier was outside the EEA, the code bypassed this block entirely and fell back to the default 'E' code for 0% taxes. ### Reason to introduce the fix: Update the condition to trigger when either the supplier or the customer is in the EEA. This ensures that cross-border transactions involving at least one EEA party correctly evaluate and apply the 'G' (Export outside the EU) category code. Also the case supplier not in eea with VAT filled in + customer in eea + RC tax with amount != 0 is fixed now (letter G reported instead of S). ### Documentation: [eInvoicing technical guidance document_v1.pdf](https://github.com/user-attachments/files/30831749/eInvoicing.technical.guidance.document_v1.pdf) opw-6407399 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281245
### Before this fix --- When creating a purchase order against a reinvoicing sale order and validating the generated account move line (AML), the behavior differs depending on whether the product already exists on the sale order. If the product is **not** already present on the sale order, a new sale order line is created with the correct quantity from the AML. However, if the product **already exists** on a sale order line, only `qty_delivered` is updated because it is computed from t
Original PR description
### Before this fix --- When creating a purchase order against a reinvoicing sale order and validating the generated account move line (AML), the behavior differs depending on whether the product…
### Before this fix --- When creating a purchase order against a reinvoicing sale order and validating the generated account move line (AML), the behavior differs depending on whether the product already exists on the sale order. If the product is **not** already present on the sale order, a new sale order line is created with the correct quantity from the AML. However, if the product **already exists** on a sale order line, only `qty_delivered` is updated because it is computed from the analytic line association. The `product_uom_qty` remains unchanged, even though the new AML represents additional quantities to be reinvoiced. As a result, the ordered quantity must be adjusted manually. **Example:** * Existing sale order line: Product A, `product_uom_qty = 5`. * A new AML for Product A with quantity `3` is linked to the same sale order. * Before this fix: * `qty_delivered` becomes `8`. * `product_uom_qty` remains `5`. * The ordered quantity is inconsistent with the reinvoiced amount and requires manual correction. #### Steps to reproduce: 1. Create an **Analytic Account (AA1)**. 2. Create a **Project** linked to **AA1**. 3. Create a **Sale Order (SO1)** linked to the project and confirm it. 4. Add a product configured with **Reinvoice Costs = At Sales Price** and **invoice policy = delivered quantities** to **SO1** with quantity **5**. 5. Create a **Purchase Order** for the same product with quantity **3** and set its analytic distribution to **AA1**. 6. Confirm the purchase order and validate the vendor bill to generate the corresponding AML. **Expected behavior:** The existing sale order line should be updated so that both `qty_delivered` and `product_uom_qty` become **8**. **Actual behavior:** Only `qty_delivered` is updated to **8**, while `product_uom_qty` remains **5**, requiring a manual correction. ### After this fix --- When a new AML is associated with an existing sale order line, `product_uom_qty` is updated alongside `qty_delivered`. Since `product_uom_qty` is used in the subtotal computation, keeping it in sync ensures that the sale order accurately reflects the reinvoiced quantity. **Example:** * Existing sale order line: Product A, `product_uom_qty = 5`. * A new AML for Product A with quantity `3` is linked. * After this fix: * `qty_delivered` becomes `8`. * `product_uom_qty` is also updated to `8`. > **Note:** This change keeps the ordered quantity consistent with newly > associated reinvoicing entries and removes the need for manual adjustments. OPW: 6294615 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
In commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added warnings to tell French companies to install the PDP module. When working on commit 720294ee523c6f84d0302e6b7fd634069f72dda7 we noticed the problem that the PDP module is not available without rescanning the available modules ("Update Apps List" in debug mode in the "Apps"). This is fixed in this commit: In case the module is not installed we still show the warning but link to the "Update Apps List" wizard. task-None
Original PR description
In commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added
warnings to tell French companies to install the PDP module.
When working on commit 720294ee523c6f84d0302e6b7fd634069f72dda7
we noticed the problem that the PDP module is not available
without rescanning the available modules ("Update Apps List" in
debug mode in the "Apps").
This is fixed in this commit:
In case the module is not installed we still show the warning
but link to the "Update Apps List" wizard.
task-NoneWe still have cases of people being confused by the fact that they send their invoices, reset it to draft, change something, then re-sending. Of course, the re-sending does not send on peppol, as it's already sent. It's more confusing than anything else. Prevent it if they're sent and not in error. task-6459869 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281525
Original PR description
We still have cases of people being confused by the fact that they send their invoices, reset it to draft, change something, then re-sending. Of course, the re-sending does not send on peppol, as it's already sent. It's more confusing than anything else. Prevent it if they're sent and not in error. task-6459869 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281525
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video preview cover is blurry. Root cause: =========== The product images are rendered in a carousel (the shop_product_carousel template in ) where only the first slide gets the "active" class; https://github.com/odoo/odoo/blob/af1b3ee2e7ac56a35bff5e030c3a831c27dbcf24/addons/website_sale/views
Original PR description
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video…
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video preview cover is blurry. Root cause: =========== The product images are rendered in a carousel (the shop_product_carousel template in ) where only the first slide gets the "active" class; https://github.com/odoo/odoo/blob/af1b3ee2e7ac56a35bff5e030c3a831c27dbcf24/addons/website_sale/views/templates.xml#L3224-L3226 every other slide is "display: none". A product video is rendered as a live <iframe> inside its slide, so when the video is not the first media its iframe loads while its container has no dimensions (0x0). The embedded player then initializes as a small mobile player and loads a low resolution cover thumbnail (120x90), which looks blurry once the slide is shown at full size. Reloading only the iframe while the slide is visible fixes it, a full page reload does not. Fix: ==== Defer loading the video iframes located on hidden slides their src is moved to a data-src attribute on start and restored once the slide becomes visible. The player then initializes at full size and loads a high resolution cover. opw-6349394 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
In this commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added the `l10n_fr_pdp.what_is_pdp` action as PDP version of the `account_peppol.what_is_peppol` action. But one case does not work / was not tested properly: We are registered on PDP already. In that case we just want to go back to the "move send" wizard. But the `what_is_pdp` action does not support that currently. So there is a traceback. Steps to reproduce: 1. Install `l10n_fr_pdp` 2. Activate French E-Invoicing / PDP in Dem
Original PR description
In this commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added the `l10n_fr_pdp.what_is_pdp` action as PDP version of the `account_peppol.what_is_peppol` action. But one case does not work / was…
In this commit 004b56a5c31841bc0bf4a9f17902bbff8d0f509d we added the `l10n_fr_pdp.what_is_pdp` action as PDP version of the `account_peppol.what_is_peppol` action.
But one case does not work / was not tested properly: We are registered on PDP already.
In that case we just want to go back to the "move send" wizard. But the `what_is_pdp` action does not support that currently. So there is a traceback.
Steps to reproduce:
1. Install `l10n_fr_pdp`
2. Activate French E-Invoicing / PDP in Demo mode
3. Create and post an invoice for a French PDP partner (e.g. just use the "FR Company")
4. Click "Send"
5. In the "move send" wizard disable the "French E-Invoicing (Demo)" option
6. Click on "Why should you use it?" in the warning
7. Click "Got it" in the window that pops up.
8. Traceback (see below)
```
Traceback (most recent call last):
File "/home/odoo/src/odoo/odoo/http.py", line 2167, in _transactioning
return service_model.retrying(func, env=self.env)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/service/model.py", line 157, in retrying
result = func()
^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 2134, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 2382, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/addons/base/models/ir_http.py", line 333, in _dispatch
result = endpoint(**request.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 754, in route_wrapper
result = endpoint(self, *args, **params_ok)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/addons/web/controllers/dataset.py", line 36, in call_kw
return call_kw(request.env[model], method, args, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/api.py", line 535, in call_kw
result = getattr(recs, name)(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/addons/account_peppol/models/res_config_settings.py", line 159, in button_peppol_reregister
self.ensure_one()
File "/home/odoo/src/odoo/odoo/models.py", line 6277, in ensure_one
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: res.config.settings()
```
task-None
Thanks to KYMAG for finding / reporting the issue.Before this commit, a test answering a route with a response of its own, `new Response(stream)`, reads `content-type: application/json` back from it, a header it never set. At the debug log level, the request itself fails: Unexpected token 'o', "[object Rea"... is not valid JSON This happens because the mocked fetch guesses the content type from its result, and a response is neither a string, a `Blob` nor a `FormData`, so it falls back to JSON, on the very headers the response carries.
Original PR description
Before this commit, a test answering a route with a response of its own, `new Response(stream)`, reads `content-type: application/json` back from it, a header it never set. At the debug log level, the request itself fails:
Unexpected token 'o', "[object Rea"... is not valid JSON
This happens because the mocked fetch guesses the content type from its result, and a response is neither a string, a `Blob` nor a `FormData`, so it falls back to JSON, on the very headers the response carries. The response logger then reads the body as text, "[object ReadableStream]" for a stream, and parses it as JSON.
This commit guesses the content type of a raw value only, so a response keeps the headers it was built with.At installation of the l10n_pl_bank_verification module or when upgrading from a lower version, the field l10n_pl_verification_id on the account.payment model gets computed and it makes the upgrade crash out. Adding a init to the model to create the column to prevent the ORM from computing the field at module installation no-task
Original PR description
At installation of the l10n_pl_bank_verification module or when upgrading from a lower version, the field l10n_pl_verification_id on the account.payment model gets computed and it makes the upgrade crash out. Adding a init to the model to create the column to prevent the ORM from computing the field at module installation no-task
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: - The colon notation comes from Uruware's technical manual and is not how identity card numbers are written in Uruguay: users write `3.402.010-1` or plain digits (`34020101`). Real users facing the error keep reformatting a number whose check digit is simply wrong, believing the problem is the forma
Original PR description
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: -…
## Description When a CI/NIE number fails the check digit validation, the raised error suggests `3:402.010-2 or 93:402.010-1 (CI or NIE)` as the expected format. This is misleading for two reasons: - The colon notation comes from Uruware's technical manual and is not how identity card numbers are written in Uruguay: users write `3.402.010-1` or plain digits (`34020101`). Real users facing the error keep reformatting a number whose check digit is simply wrong, believing the problem is the format. - The CI example shown is itself an **invalid** number: the check digit of `3.402.010` is `1`, not `2` — the module's own test suite (`l10n_uy/tests/test_check_vat.py`) uses `3:402.010-2` as an invalid sample. This was reported by an Uruguayan eCommerce user who lost sales because buyers interpreted the message as requiring an unusual format that does not exist in Uruguay. ## Change Replace the examples with valid numbers written in the notations actually used in Uruguay, and clarify that separators are optional — which is exactly what `_l10n_uy_ci_nie_is_valid()` already accepts (it strips `:. ,-` before checking the verification digit): ``` 3.402.010-1 or 34020101 (CI), 93.402.010-1 (NIE); dots and dash are optional ``` Only the `expected_format` string changes; the validation logic is untouched. Existing tests only assert the fixed part of the message (`The CI/NIE number.*does not seem to be valid`), so they are unaffected.