Monday, July 14, 2025
20 changes · saas-18.4
Enhancements to existing features
Website form builders can now let dropdown selection fields start with no value when the field is not required. This restores expected flexibility for form visitors and helps businesses collect responses without forcing an unnecessary selection.
Original PR description
Before this commit, users did not have an option to leave the selection field blank in the website form even though it's not set to required. The feature was introduced in commit [1] but was lost when introducing `html_builder`. In this commit, we reintroduce the feature that allows users to choose whether they want the selection field to be required or not. [1]: https://github.com/odoo/odoo/commit/34ddd5dd6a036792c44b865cb7a4671f4ca033cf task-4367641
Bank reconciliation now loads potential journal items much faster by simplifying how company filters are applied. This can significantly reduce waiting time for finance teams working with very large accounting databases.
Original PR description
This fix aims to avoid a subquery in the main query and instead use a
IN clause to improve search performance.
On a database with over 37 million journal items, before the fix, the
query to load the 40 potentials journal items to reconcile in the bank
reconciliation widget took around 50 seconds; after the fix, it take
only 3 seconds (with only 1 company to filter).
Subquery when 'child_of' is used:
```
...
AND (
"account_move_line"."company_id" IN (
SELECT
"res_company"."id"
FROM
"res_company"
WHERE
("res_company"."parent_path" LIKE '1/%')
)
)
```
Query after the fix:
```
...
AND ("account_move_line"."company_id" IN (1))
```
opw-4896863
Forward-Port-Of: odoo/odoo#218700
Forward-Port-Of: odoo/odoo#218034The production planning screen now reloads only the schedules currently visible to the user instead of every affected schedule in the background. This makes updates much faster in complex manufacturing setups, reducing a benchmarked reload from 31.6 seconds to 3.5 seconds.
Original PR description
Description ----------- Only fetch forecasts for MPS schedules visible in the view rather than all impacted schedules. Schedules not visible due to pagination are skipped when reloading the MPS state. Benchmark --------- Adjusting a quantity for a product, which will impact around 1k+ other schedules (hierarchy is deep with boms), `get_production_schedule_view_state` takes: | Before | After | Speed-up | |--------|-------|----------| | 31.6s | 3.5s | 9x | Reference --------- opw-4778588 Forward-Port-Of: odoo/enterprise#87299
Resolved issues and error corrections
Fixes issues where invoices using cash rounding could produce invalid UBL electronic invoice files or import back with different totals. This helps ensure compliant e-invoicing and consistent invoice amounts across export and import, especially for tax-related rounding cases.
Original PR description
Currently these problems can appear when an invoice is cash rounded. 1. In case we use the "Modify tax amount" (`biggest_tax`) cash rounding strategy: The rounding amount is added to the taxes in…
Currently these problems can appear when an invoice is cash rounded.
1. In case we use the "Modify tax amount" (`biggest_tax`) cash rounding strategy:
The rounding amount is added to the taxes in Odoo but not in the UBL XML
- This affects everything that uses `_prepare_invoice_aggregated_taxes`
(and not just UBL XML)
2. The generated UBL XML is invalid (for any rounding strategy).
See below for details.
3. The import of the exported UBL XML does not yield back the same invoice
(even after fixing the export / the previous 2 problems).
Also there are some problems with the correction of tax values
of imported UBL XML (`correct_invoice_tax_amount`).
(They probably do not cause issues in practice but would after this
fix. We adapt the correction as part of the fix for (3).)
#### Runbot: How to generate problematic XML
1. Select BE Company CoA
2. Enable Cash Rounding in the settings
3. Create a cash rounding method
(in the settings where cash rounding can be enabled):
- precision `1.00`
- strategy: any
- profit / loss account: any
4. Create an invoice
- Set a Belgian partner (e.g. "BE Company CoA" is okay)
- Set the the cash rounding method from step 2
- Single Line with price=70.00€ and a 21% tax
5. The total should be 85.00 € (84.70 € w/o the rounding)
In the journal items there should be the following
non payment term items:
- 70.00€ base
- 14.70€ tax
- 0.30€ rounding (depending on the cash rounding strategy the tax is set or not)
6. Confirm & Send (with BIS Billing 3.0)
7. Look at the UBL BIS 3 XML in the `Invoice` element
- `TaxTotal/TaxAmount`: 14.70€
- `TaxTotal/TaxSubtotal/TaxableAmount`: 70.00€
- `TaxTotal/TaxSubtotal/TaxAmount`: 14.70€
- `LegalMonetaryTotal/TaxExclusiveAmount`: 70.00€
- `LegalMonetaryTotal/TaxInclusiveAmount`: 85.00€
- `LegalMonetaryTotal/PayableAmount`: 85.00€
8. This fails validation `BR-CO-15`:
```
Invoice total amount with VAT (BT-112)
= Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110).
```
(`LegalMonetaryTotal/TaxInclusiveAmount` = `LegalMonetaryTotal/TaxExclusiveAmount` + `TaxTotal/TaxAmount`)
Since the cash rounding is included in `LegalMonetaryTotal/TaxInclusiveAmount` but not in
`TaxTotal/TaxAmount` (or `LegalMonetaryTotal/TaxExclusiveAmount`)
#### Tax value correction details (with examples)
Currently we try to fix the tax amounts after importing an invoice.
The function we use for that (`_correct_invoice_tax_amount`) has the following issues:
- We look for `TaxTotal/TaxSubtotal` elements anywhere.
But i.e. such elements can also exist inside `InvoiceLine` elements.
Example:
- module `l10n_dk_oioubl` file `test_xml_oioubl_dk.py`
- function `test_oioubl_import_exemple_file_4` / XML file 'external/BASPRO_01_01_00_Invoice_v2p1.xml'
- The tax total parsed from the document may need to be inverted.
E.g. credit notes can be given as an invoice with negative amounts.
See function `_get_import_document_amount_sign`.
Example:
- module `l10n_account_edi_ubl_cii` file `test_xml_ubl_be.py`
- function `test_import_invoice_xml_open_peppol_examples` / XML file 'bis3_invoice_negative_amounts.xml'
- We compare the tax total from the document only with a single line of that tax.
But there can be multiple lines for a single tax. We have to use the sum of all those lines for the comparison.
Example:
- module `l10n_account_edi_ubl_cii` file `test_xml_ubl_au.py`
- function `test_export_import_invoice` / XML file 'from_odoo/a_nz_out_invoice.xml'
#### The fix
This commit does the following to fix that
1. We include cash rounding lines belonging to a tax in the tax computation for the UBL XML export
(or rather everything any tax computation done with `_prepare_invoice_aggregated_taxes`).
2. After fixing (1) we only have to fix the "Add a rounding line" (`add_invoice_line`) strategy.
This is as follows
- Subtract the cash rounding from the `LegalMonetaryTotal/TaxInclusiveAmount`
- Add node `LegalMonetaryTotal/PayableRoundingAmount` with the value of the cash rounding
3. Cases
- `add_invoice_line`: We create a dedicated invoice line with the amount found in node
`LegalMonetaryTotal/PayableRoundingAmount` (if it is present).
- `biggest_tax`: We update the amount on the tax line to match the value found in the XML.
(Currently we only do this if the difference is not greater than '0.05')
The fixes for the tax value correction on import are also needed for 3./`biggest_tax`.
#### Runbot: example XML after the fix
The export in the example then looks like this for the different cash rounding strategies
- `add_invoice_line`
- `TaxTotal/TaxAmount`: 14.70€
- `TaxTotal/TaxSubtotal/TaxableAmount`: 70.00€
- `TaxTotal/TaxSubtotal/TaxAmount`: 14.70€
- `LegalMonetaryTotal/TaxExclusiveAmount`: 70.00€
- `LegalMonetaryTotal/TaxInclusiveAmount`: 84.70€
- `LegalMonetaryTotal/PayableRoundingAmount`: 0.30€
- `LegalMonetaryTotal/PayableAmount`: 85.00€
The validation for the `LegalMonetaryTotal/PayableAmount` is still
okay since (in the example) it is just `LegalMonetaryTotal/TaxInclusiveAmount` + `LegalMonetaryTotal/PayableRoundingAmount`.
- `biggest_tax`
- `TaxTotal/TaxAmount`: 15.00€
- `TaxTotal/TaxSubtotal/TaxableAmount`: 70.00€
- `TaxTotal/TaxSubtotal/TaxAmount`: 15.00€
- `LegalMonetaryTotal/TaxExclusiveAmount`: 70.00€
- `LegalMonetaryTotal/TaxInclusiveAmount`: 85.00€
- `LegalMonetaryTotal/PayableRoundingAmount`: (not exported)
- `LegalMonetaryTotal/PayableAmount`: 85.00€
#### References
Also see
- https://docs.peppol.eu/poacc/billing/3.0/bis/#_rounding
- https://docs.peppol.eu/poacc/billing/3.0/bis/#_calculation_of_totals
task-4854592
Forward-Port-Of: odoo/odoo#217119
Forward-Port-Of: odoo/odoo#213378This fix prevents completed FPX payments through Razorpay Malaysia from failing because certain payment details are not returned during the redirect flow. Businesses using Razorpay FPX can now accept payments more reliably without customers encountering an error after payment completion.
Original PR description
Steps to reproduce: 1. Create a company based in Malaysia. 2. Configure a Razorpay (Malaysia) account using valid credentials. 3. Enable the FPX payment method for Razorpay. 4. Create a Sales Order and attempt to pay using FPX. Issue: - An error occurs after the payment is completed. Cause: - After this PR: https://github.com/odoo/odoo/pull/163860, we are comparing currency and amount values. However, this information is not available in the data received via the `return_url`. Fix: - Do not compare amount and currency for REDIRECT_PAYMENT_METHOD_CODES. opw-4922299 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
A migration script was corrected so it can reliably match localization data rows and keep existing translations when converting them to the newer format. This helps prevent translated labels and country-specific accounting or identification data from disappearing during upgrades.
Original PR description
The script failed to find the corresponding rows in the data files, leading to some translations being lost when migrating them to the new syntax. opw-4659964 Forward-Port-Of: odoo/odoo#218461 Forward-Port-Of: odoo/odoo#208454
Customers using Latin American localization could be blocked from saving a new billing address during checkout when an existing identification type was already set. The form now keeps that identification information during submission, preventing the address from being incorrectly rejected.
Original PR description
Versions -------- - saas-18.2+ Steps ----- 1. Set up a company & website with Peruvian l10n; 2. add an identification type of "DNI" to your partner data; 3. set the identification number to "09123456"; 5. go to /shop; 6. add product to cart and go to checkout; 7. try to add a new billing address. Issue ----- Cannot save the address, as it thinks you selected the RUC identification type, which is not allowed. Cause ----- When a partner already has an identification type & number, they're not allowed to add new ones to alternate addresses. Issue is that the read-only field added via 5a93da8 is not an actual form field, and doesn't provide its value on submit. Solution -------- Add a hidden `input` element with `l10n_latam_identification_type_id`, after the read-only `t-else` element with a seperate `t-if` to prevent breaking xpaths in stable. opw-4817712 Forward-Port-Of: odoo/odoo#216692
Online shop carts now recalculate the correct taxes when a customer updates their address outside the checkout flow. This helps ensure customers are charged the right tax based on their location before payment.
Original PR description
Versions -------- - 16.0+ Steps ----- 1. Have a fiscal position with a country-based tax mapping; 2. go to `/shop` as a public user, 3. create a new account; 4. add a product to your cart; 5. go to…
Versions -------- - 16.0+ Steps ----- 1. Have a fiscal position with a country-based tax mapping; 2. go to `/shop` as a public user, 3. create a new account; 4. add a product to your cart; 5. go to user settings & add an address that matches the fiscal position; 6. go to checkout & pay for the cart. Issue ----- The fiscal position's taxes aren't applied to the order. Cause ----- The `_compute_fiscal_position_id` method is triggered when changing the `partner_id` or `partner_shipping_id` of an order. It does not trigger when modifying the address of the order's current partner. There is logic in place to recompute fiscal position & taxes when an address gets entered via checkout, but not via any other route. Solution -------- Adding address fields to the `api.depends` of the compute method could introduce the unintended behavior of changing taxes & fiscal position of confirmed sale orders. Instead, we can check for fields relevant to fiscal position in `write`, then search for unconfirmed website orders, and recompute their fiscal position & taxes if need be. opw-4844132 opw-4753332 Forward-Port-Of: odoo/odoo#218516 Forward-Port-Of: odoo/odoo#214588
This fix prevents Linux-based USB receipt printers from failing to print when another connected USB device has missing manufacturer information. It makes the printer detection process more reliable, reducing interruptions at checkout or other receipt-printing points.
Original PR description
On Linux with a USB connected ESC/POS receipt printer, a traceback can occur in the following conditions: - The printer is successfully initialised as an ESCPOS printer by the `python-escpos` library at driver start - Later, when trying to print, some other USB device causes a traceback in the `usb_matcher` method, preventing the print from taking place The fix is to make the `usb_matcher` method more robust by checking the `manufacturer` property exists before using it. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix prevents Odoo from crashing on Ubuntu Jammy systems that use the distribution-provided BeautifulSoup package. It restores support for that environment so related data import and parsing features continue to work reliably.
Original PR description
Ubuntu Jammy ships BeautifulSoup 4.10, which does not have the `XMLParsedAsHTMLWarning` symbol. Because the import guard was removed in #212408 any use of ofxparse or bs4 (directly) on jammy using distro packages will blow up. https://runbot.odoo.com/odoo/error/226555
Deleting attachments is made faster on very large databases by restoring an optimized lookup for related attachment records. This reduces delays during cleanup operations and improves reliability for systems with millions of attachments.
Original PR description
On a DB with 15M+ attachments, deleting a single attachment takes several seconds. Most of the time is spent on the circular `original_id` foreign key.
We add back the index which was removed in [1]
```
Delete on ir_attachment (cost=0.43..8.45 rows=0 width=0) (actual time=0.274..0.274 rows=0 loops=1)
-> Index Scan using ir_attachment_pkey on ir_attachment (cost=0.43..8.45 rows=1 width=6) (actual time=0.252..0.253 rows=1 loops=1)
Index Cond: (id = 82807)
Planning Time: 0.049 ms
Trigger for constraint ir_attachment_original_id_fkey: time=2330.796 calls=1
```
[1] https://github.com/odoo/odoo/commit/eedf37d6e286b995c47b946be1a6b66817094eff
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#218120Turkish Nilvera e-invoice XML notes now include the outstanding invoice amount written in uppercase text, helping meet local formatting requirements. For foreign-currency invoices, the note includes both the Turkish Lira amount and the original currency amount, with zero values correctly written in Turkish.
Original PR description
This commit will add the amount residual in text in the note of the xml we sent to nilvera. If the invoice is in another currency than Turkish Lira, we have to add two notes one for the amount in turkish lira and one in the other currency task-4518269 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#217910 Forward-Port-Of: odoo/odoo#195938
Checkout no longer presents deliveries billed at actual cost as free when the final amount is not yet known. Instead, customers see a clear notice that the delivery cost will be calculated after delivery, reducing confusion and billing surprises.
Original PR description
Issue: If the delivery invoice policy was set to "real" (meaning that we'll invoice the real delivery cost, after delivery), the delivery was shown as being free on eCommerce (since we don't know the cost yet). Fix: Show a disclaimer indicating that the cost will be computed after delivery. opw-4779059 Forward-Port-Of: odoo/odoo#218378
Customers who choose a subscription plan different from the default now get the correct pricing in their cart. This prevents checkout confusion and helps ensure orders reflect the buyer's intended plan.
Original PR description
Selecting another plan than the default one didn't work because it wasn't considered when looking for the applicable pricing.
This update ensures Colombian electronic invoice files correctly handle cash rounding and include rounding-related tax lines. This helps keep tax totals accurate and compliant when invoices use cash rounding.
Original PR description
#### [FIX] l10n_co_dian: UBL related cash rounding issues In the community PR the UBL XML generation was adjusted to support cash rounding. (See there for more details) This commit adjusts the code in `l10n_co_dian` to be compatible. #### [FIX] l10_co_edi: tax_amls include rounding lines Currently rounding lines (with taxes) are not included in the tax lines. This is fixed in this commit. See the community PR / commits for motivation. #### info task-4854592 Forward-Port-Of: odoo/enterprise#89439
Bank reconciliation now uses the same safeguards as journal entry analytic updates, preventing unwanted update loops and keeping analytic information in sync. This helps ensure reconciliation stays reliable when analytic distributions are changed.
Original PR description
Since a recent fix[^1], the analytic distribution on journal entries is updated with any update made on analytic items. In order to avoid loops and do things in the right order, a context key was added. However, the reconciliation widget was also manipulating analytic items, so it needed to use the context key as well. opw-4936028 opw-4933629 opw-4935236 opw-4933456 opw-4933652 opw-4935789 opw-4938570 opw-4938388 opw-4935709 opw-4940233 opw-4938849 [^1]: https://github.com/odoo/odoo/commit/8194c6e2bfedc9437964090b1a498c3e8cff225c Forward-Port-Of: odoo/enterprise#90175
Fixed an issue in barcode receipt processing where changing the unit of measure updated the completed quantity but left the reserved quantity unchanged. This keeps inventory demand and received quantities aligned, reducing confusion and potential stock discrepancies during warehouse operations.
Original PR description
Steps to reproduce:
- Create a storable product “P1”:
- UoM: Unit
- Create a receipt for 200 units of P1
- Mark it as To Do
- Go to the barcode module and start processing the receipt
- Edit the quantity:
- Set it to 2 and change the UoM to Dozen
- Save
Problem
The quantity done is correctly set to 2 dozens, But the reserved quantity remains 200
Solution:
When the UoM is changed, compute and update the reserved quantity accordingly
OPW-4716104
Forward-Port-Of: odoo/enterprise#89131
Forward-Port-Of: odoo/enterprise#85004Creating an Anniversary Discount campaign in Marketing Automation could fail because the customer selection rules were built incorrectly. This update rebuilds and simplifies those rules so the campaign can be created reliably without changing the intended targeting logic.
Original PR description
We had a traceback when trying to create an Anniversary Discount marketing campaing
due to the domain's construction.
Steps to reproduce:
-------------------
* Go to Marketing Automation app
* Go to eCommerce tab --> anniversary discount
* Click " Create Campaign"
> Observation:
File "/data/build/enterprise/marketing_automation/models/marketing_activity.py", line 142, in _compute_inherited_domain
literal_eval(activity.campaign_id.domain or '[]')])
Why the fix:
------------
The problematic line was introduced in this REV: 1aa05c89f3371981922e3dc52d8334104aa425b9.
The domain was originally built in this IMP: cf2124f4f20a805077502587f9a8be0af0a753c3.
The domain was intended to be used dynamically, but after the revision, that usage changed—so the domain needed to be rebuilt.
We've simplified the domain to eliminate overlapping conditions, ensuring the same logic and result, but with improved readability.
opw-4857722
Forward-Port-Of: odoo/enterprise#89375Belgian companies can once again export the Social Balance Sheet report in PDF or XLSX without hitting an error. The fix updates the report export logic to match the current payroll versioning behavior, preventing a crash during a routine reporting task.
Original PR description
Since the introduction of versions, a traceback occurs due to an old function call Task: 4911701
Belgian payroll reporting has been updated to match the 2025/2 DmfA requirements. This helps ensure payroll declarations remain compliant with the latest official reporting rules.
Original PR description
Forward-Port-Of: odoo/enterprise#90016