Daily updates from Odoo
Friday, July 31, 2026
330 changes
4 changes
Enhancements to existing features
- Display the color palette recommendation progressively, like an AI assistant response. - Remove the large palette preview and open the next screen as soon as a palette is selected. - Adapt the first six layout previews to the available screen height and keep generated themes at the same dimensions. - Remove template name pills from the catalog and product screens. | Before | After | | ------------- | ------------- | | <img width="1524" height="924" alt="image" src="https://gi
Original PR description
- Display the color palette recommendation progressively, like an AI assistant response. - Remove the large palette preview and open the next screen as soon as a palette is selected. - Adapt the first six layout previews to the available screen height and keep generated themes at the same dimensions. - Remove template name pills from the catalog and product screens. | Before | After | | ------------- | ------------- | | <img width="1524" height="924" alt="image" src="https://github.com/user-attachments/assets/4c054a4d-04c0-4dfe-aa96-8bf8a9f7ecdd" /> | <img width="1523" height="925" alt="image" src="https://github.com/user-attachments/assets/ebb5f594-1760-458b-a024-f1fcc6ecc03e" /> |
Resolved issues and error corrections
to reproduce: ============= - Have a database with a large number of kit BoMs (e.g. ~160k phantom mrp.bom records). - Open Inventory Valuation. - The request never returns and hangs forever. problem: ======== Commit 11e9c1297439 started searching the valued products with `('qty_available', '!=', 0)`. On `product.product` this triggers the mrp override `_search_qty_available_new`, which loads every phantom BoM in the database and computes `qty_available` (via BoM explode) for each
Original PR description
to reproduce: ============= - Have a database with a large number of kit BoMs (e.g. ~160k phantom mrp.bom records). - Open Inventory Valuation. - The request never returns and hangs forever. problem:…
to reproduce:
=============
- Have a database with a large number of kit BoMs (e.g. ~160k phantom
mrp.bom records).
- Open Inventory Valuation.
- The request never returns and hangs forever.
problem:
========
Commit 11e9c1297439 started searching the valued products with
`('qty_available', '!=', 0)`. On `product.product` this triggers the mrp
override `_search_qty_available_new`, which loads every phantom BoM in the
database and computes `qty_available` (via BoM explode) for each kit. On top
of that, the override builds the kit products recordset with repeated
`kit_products |= ...` unions, which is O(n^2). With a large catalog of kits
the combination of O(n) heavy explodes and O(n^2) unions never returns.
On top of the performance issue, the new search dropped the kit exclusion
that `_get_accounts_by_product` previously applied through
`_get_valuation_product_domain` (`('is_kits', '=', False)` in mrp_account),
so phantom products - which are never valued on their own - were wrongly
pulled into the valuation.
solution:
=========
Restore the kit exclusion: search the valued products through
`_get_valuation_product_domain()` (which adds `('is_kits', '=', False)` in
mrp_account) instead of the ad-hoc `('is_storable', '=', True)` domain, so
phantom products are no longer valued.
Add a `skip_kit_qty_available` context key on `_search_qty_available_new` so
callers that intentionally exclude kits can skip the costly kit BoM expansion
and return the base (quant-based) result directly. The key is set in
mrp_account (via `_get_valuation_product_context`), alongside the domain that
already excludes kits, so the optimization and its precondition stay in the
same layer.
Also make the remaining kit path in `_search_qty_available_new` scale: build
the kit products recordset in a single pass instead of O(n^2) recordset
unions, and use a set for membership checks.
Benchmark:
==========
for `_get_report_data()` (averaged over 5 runs):
| # Input data (phantom kits) | Before PR | After PR |
| :---: | :---: | :---: |
| 1,000 | 2.558 s | 35.5 ms |
| 5,000 | 10.991 s | 41.6 ms |
| 10,000 | 20.869 s | 66.0 ms |
| 25,000 | 61.807 s | 80.7 ms |
| 50,000 | 195.382 s | 87.9 ms |
the improvement is **~99% faster**
opw-6312168
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#273982A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantit
Original PR description
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantity from 10 to 4 - Re-confirm: the pull rule generates a return of 6 with to_refund set but no origin_returned_move_id nor picking.return_id - Validate that return Before: delivered stays at 10. After: delivered is 4. opw-6345628 Forward-Port-Of: odoo/odoo#274399
The implementation of the highlight of missing required settings (#249321) was discarding the `SearchableSetting` if the `<setting>` had no `id`. This behavior disabled the highlight of the setting formPage in those cases. This commit fixes the issue by allowing `settingId` to be undefined. Note that `settingId` can safely be undefined since it is used only for the highlight behavior and during a case of url hash check. task-6364849 Forward-Port-Of: odoo/odoo#276871
Original PR description
The implementation of the highlight of missing required settings (#249321) was discarding the `SearchableSetting` if the `<setting>` had no `id`. This behavior disabled the highlight of the setting formPage in those cases. This commit fixes the issue by allowing `settingId` to be undefined. Note that `settingId` can safely be undefined since it is used only for the highlight behavior and during a case of url hash check. task-6364849 Forward-Port-Of: odoo/odoo#276871
5 changes
Resolved issues and error corrections
Issue: --- `code` is hidden in `payment.method` from without dev mode, which cause an validation error when creating a new payment method. opw-6390285 Forward-Port-Of: odoo/odoo#278671
Original PR description
Issue: --- `code` is hidden in `payment.method` from without dev mode, which cause an validation error when creating a new payment method. opw-6390285 Forward-Port-Of: odoo/odoo#278671
to reproduce: ============= - Have a database with a large number of kit BoMs (e.g. ~160k phantom mrp.bom records). - Open Inventory Valuation. - The request never returns and hangs forever. problem: ======== Commit 11e9c1297439 started searching the valued products with `('qty_available', '!=', 0)`. On `product.product` this triggers the mrp override `_search_qty_available_new`, which loads every phantom BoM in the database and computes `qty_available` (via BoM explode) for each
Original PR description
to reproduce: ============= - Have a database with a large number of kit BoMs (e.g. ~160k phantom mrp.bom records). - Open Inventory Valuation. - The request never returns and hangs forever. problem:…
to reproduce:
=============
- Have a database with a large number of kit BoMs (e.g. ~160k phantom
mrp.bom records).
- Open Inventory Valuation.
- The request never returns and hangs forever.
problem:
========
Commit 11e9c1297439 started searching the valued products with
`('qty_available', '!=', 0)`. On `product.product` this triggers the mrp
override `_search_qty_available_new`, which loads every phantom BoM in the
database and computes `qty_available` (via BoM explode) for each kit. On top
of that, the override builds the kit products recordset with repeated
`kit_products |= ...` unions, which is O(n^2). With a large catalog of kits
the combination of O(n) heavy explodes and O(n^2) unions never returns.
On top of the performance issue, the new search dropped the kit exclusion
that `_get_accounts_by_product` previously applied through
`_get_valuation_product_domain` (`('is_kits', '=', False)` in mrp_account),
so phantom products - which are never valued on their own - were wrongly
pulled into the valuation.
solution:
=========
Restore the kit exclusion: search the valued products through
`_get_valuation_product_domain()` (which adds `('is_kits', '=', False)` in
mrp_account) instead of the ad-hoc `('is_storable', '=', True)` domain, so
phantom products are no longer valued.
Add a `skip_kit_qty_available` context key on `_search_qty_available_new` so
callers that intentionally exclude kits can skip the costly kit BoM expansion
and return the base (quant-based) result directly. The key is set in
mrp_account (via `_get_valuation_product_context`), alongside the domain that
already excludes kits, so the optimization and its precondition stay in the
same layer.
Also make the remaining kit path in `_search_qty_available_new` scale: build
the kit products recordset in a single pass instead of O(n^2) recordset
unions, and use a set for membership checks.
Benchmark:
==========
for `_get_report_data()` (averaged over 5 runs):
| # Input data (phantom kits) | Before PR | After PR |
| :---: | :---: | :---: |
| 1,000 | 2.558 s | 35.5 ms |
| 5,000 | 10.991 s | 41.6 ms |
| 10,000 | 20.869 s | 66.0 ms |
| 25,000 | 61.807 s | 80.7 ms |
| 50,000 | 195.382 s | 87.9 ms |
the improvement is **~99% faster**
opw-6312168
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#273982A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantit
Original PR description
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantity from 10 to 4 - Re-confirm: the pull rule generates a return of 6 with to_refund set but no origin_returned_move_id nor picking.return_id - Validate that return Before: delivered stays at 10. After: delivered is 4. opw-6345628 Forward-Port-Of: odoo/odoo#274399
With the Shared Customer Account setting enabled, a user created in c1 cannot access the shop in the website of c2 Steps to reproduce: 1. Install eCommerce and Contacts 2. Go to Settings > Users & Companies > Companies and create two companies c1 and c2 3. Go to Website > Configuration > Websites and create two websites w1 with company c1 and w2 with company c2 4. Change the order of the websites so that w1 is at the top 5. Go to Website > eCommerce > Pricelists and create a pricelist pl
Original PR description
With the Shared Customer Account setting enabled, a user created in c1 cannot access the shop in the website of c2 Steps to reproduce: 1. Install eCommerce and Contacts 2. Go to Settings > Users &…
With the Shared Customer Account setting enabled, a user created in c1 cannot access the shop in the website of c2 Steps to reproduce: 1. Install eCommerce and Contacts 2. Go to Settings > Users & Companies > Companies and create two companies c1 and c2 3. Go to Website > Configuration > Websites and create two websites w1 with company c1 and w2 with company c2 4. Change the order of the websites so that w1 is at the top 5. Go to Website > eCommerce > Pricelists and create a pricelist pl1 in c1 assigned to w1 and pl2 in c2 assigned to w2 6. In an incognito tab, go to w1 and create a new account 7. As admin, go to Website > Configuration > Websites and change the order of the websites so that w2 is at the top 8. In an incognito tab, connect with the previously created account and go to the shop 9. An error is thrown (This error only happens when geoip works, i.e. when `_get_geoip_country_code` returns something) Issue: When geoip returns a country code, we search through all the pricelists available for that country code but some of them can be restricted to a company which raises an access error. We need to be able to access them in order to filter the ones that are not available on the current website Solution: Access all pricelists with sudo, they will be filtered out with `_is_available_on_website` opw-3574089 Forward-Port-Of: odoo/odoo#268863
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no l
Original PR description
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no longer carries a product_id: it only has a free-text. The Factur-X/CII export template rendered ram:Name directly from line.product_id.name with no fallback. For a line without a product, this produced an empty ram:Name element, which cleanup_xml_node then stripped entirely from the XML, leaving only ram:Description. Solution: Fall back to the line's name when there is no product opw-6391121 Forward-Port-Of: odoo/odoo#277418
2 changes
Resolved issues and error corrections
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantit
Original PR description
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantity from 10 to 4 - Re-confirm: the pull rule generates a return of 6 with to_refund set but no origin_returned_move_id nor picking.return_id - Validate that return Before: delivered stays at 10. After: delivered is 4. opw-6345628 Forward-Port-Of: odoo/odoo#274399
Miscellaneous changes
Backport of [1]. Builder image tests were flaky in full-suite runs because earlier tests left slow requests pending. Bogus snippet thumbnails, obsolete modify_image mock data, and made-up attachment URLs triggered expensive website 404 rendering and starved the browser connection pool. Avoid rendering missing thumbnails, use data URIs or existing static images in fixtures, return the current modify_image response shape, and give the CORS test image explicit dimensions. [1]: https://gith
Original PR description
Backport of [1]. Builder image tests were flaky in full-suite runs because earlier tests left slow requests pending. Bogus snippet thumbnails, obsolete modify_image mock data, and made-up attachment URLs triggered expensive website 404 rendering and starved the browser connection pool. Avoid rendering missing thumbnails, use data URIs or existing static images in fixtures, return the current modify_image response shape, and give the CORS test image explicit dimensions. [1]: https://github.com/odoo/odoo/pull/277424 Forward-Port-Of: odoo/odoo#279584 Forward-Port-Of: odoo/odoo#279314
9 changes
Resolved issues and error corrections
recently in 19.1 a commit adds a warning and disables the button in the signup for if you are alreafy logged in. The issue is that for websites without the header (which is the case for internal, but can happen to customers) there is no way to get to the homepage (except from writing to the url or hiting the back button) This created an issue in internal as an example, since our customers have aparently bookmarked the login page, and they would simply login again before this. For some people,
Original PR description
recently in 19.1 a commit adds a warning and disables the button in the signup for if you are alreafy logged in. The issue is that for websites without the header (which is the case for internal, but…
recently in 19.1 a commit adds a warning and disables the button in the signup for if you are alreafy logged in. The issue is that for websites without the header (which is the case for internal, but can happen to customers) there is no way to get to the homepage (except from writing to the url or hiting the back button) This created an issue in internal as an example, since our customers have aparently bookmarked the login page, and they would simply login again before this. For some people, since they would access the page so often, google automatically fill up the url when they type odoo to odoo/web/login This commit aims to introduce a link to the home page that will be displayed in the warning message. This way cusotmers can get to the home page, and we will hopefully stop the support tickets this has created opw-6331783 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
The state_id field was not cleared when editing an address and switching to a country without states — the state selector reset visually, but the stale state_id was still saved to the backend. Steps to reproduce: 1. Add a product to the cart. 2. Go to checkout and edit the address, selecting a country that has states. 3. Edit the address again, now selecting a country without states. 4. Save and check the contact in the backend: state_id still holds the state from the previo
Original PR description
The state_id field was not cleared when editing an address and switching to a country without states — the state selector reset visually, but the stale state_id was still saved to the backend. Steps to reproduce: 1. Add a product to the cart. 2. Go to checkout and edit the address, selecting a country that has states. 3. Edit the address again, now selecting a country without states. 4. Save and check the contact in the backend: state_id still holds the state from the previous country. Solution: reset the state_id select options for the new country. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278660 Forward-Port-Of: odoo/odoo#278125
Steps to reproduce: - Edit a page. - Drop a carousel snippet like s_quotes_carousel - Click "Add Slide" with the browser console open(for concrete race condition) => Traceback: TypeError: Cannot read properties of null (reading 'classList') Cause: `slide()` used the editor window's `Carousel` instead of the iframe's. This created a second Carousel instance for the same element. Both instances updated the indicators at the same time, causing one to remove the active indicator before the
Original PR description
Steps to reproduce: - Edit a page. - Drop a carousel snippet like s_quotes_carousel - Click "Add Slide" with the browser console open(for concrete race condition) => Traceback: TypeError: Cannot read properties of null (reading 'classList') Cause: `slide()` used the editor window's `Carousel` instead of the iframe's. This created a second Carousel instance for the same element. Both instances updated the indicators at the same time, causing one to remove the active indicator before the other tried to use it, leading to the traceback. Fix: Use `this.window.Carousel` so the iframe's existing Carousel instance is reused instead of creating a second one. task-6084484 Forward-Port-Of: odoo/odoo#279117 Forward-Port-Of: odoo/odoo#275903
### Issue before this commit: 1. When generating a FatturaPA XML for a self-invoice (reverse charge / autofattura, e.g. TD17-TD19) with the "Reference" (ref) field filled in, the supplier's invoice number was placed under <DatiOrdineAcquisto> instead of <DatiFattureCollegate>. 2. When a credit note was generated from a vendor bill, the <IdDocumento> in <DatiFattureCollegate> contained Odoo's internal document number (e.g. BILL/2026/07/0002) instead of the actual reference of the invoice rece
Original PR description
### Issue before this commit: 1. When generating a FatturaPA XML for a self-invoice (reverse charge / autofattura, e.g. TD17-TD19) with the "Reference" (ref) field filled in, the supplier's invoice…
### Issue before this commit: 1. When generating a FatturaPA XML for a self-invoice (reverse charge / autofattura, e.g. TD17-TD19) with the "Reference" (ref) field filled in, the supplier's invoice number was placed under <DatiOrdineAcquisto> instead of <DatiFattureCollegate>. 2. When a credit note was generated from a vendor bill, the <IdDocumento> in <DatiFattureCollegate> contained Odoo's internal document number (e.g. BILL/2026/07/0002) instead of the actual reference of the invoice received from the supplier (ref). ### Steps to reproduce the issue: ISSUE 1: 1. Download Accounting and l10n_it 2. Go to Vendor -> Bills 3. Create a bill with: 1. Italian company as vendor 2. Product with tax 22% S RC 3. Bill reference filled (ex. FT00001) 4. Send it to SDI, open the XML and see that the tag <IdDocumento> is inside the tag <DatiOrdineAcquisto> while it sohuld be inside <Datifatturecollegate> ISSUE 2: 1. From a bill created click Credit Note 2. Send to SDI again, open the XML and see that the tag <IdDocumento> contains the bill reference created in Odoo while it should take the reference of the original invoice SENT by the vendor ### Cause of the issue: 1. The template's t-elif chain did not distinguish between self-invoices and regular documents, so any value in record.ref was routed to DatiOrdineAcquisto regardless of context. 2. Separately, the linked_moves loop always used linked_move.name to populate <IdDocumento>, which for vendor bills/refunds is Odoo's own sequential number, not the supplier's original invoice number. ### Reason to introduce the fix: 1. For the official FatturaPA Technical Specifications, DatiOrdineAcquisto must only reference a purchase order, while DatiFattureCollegate must reference a related invoice — which is the correct category for the supplier document being integrated in a self-invoice. This is confirmed by the Agenzia delle Entrate documentation: https://www.agenziaentrate.gov.it/portale/documents/d/guest/allegato-a-specifiche-tecniche-vers-1-9 (p.107, chapter Compilazione del documento XML con codice TD17) 2. For credit/debit notes, <IdDocumento> inside <DatiFattureCollegate> must contain the number of the original invoice being referenced/varied, not an internally generated document number, as clarified here: https://www.pa.sm/ticket/kb/faq.php?id=46 opw-6117968 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276523
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Original PR description
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Issue: --- `code` is hidden in `payment.method` from without dev mode, which cause an validation error when creating a new payment method. opw-6390285 Forward-Port-Of: odoo/odoo#278671
Original PR description
Issue: --- `code` is hidden in `payment.method` from without dev mode, which cause an validation error when creating a new payment method. opw-6390285 Forward-Port-Of: odoo/odoo#278671
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantit
Original PR description
A return coming back from the customer must always decrease the delivered quantity of a sale order line, even when it is not linked to its original delivery (no origin_returned_move_id and its picking has no return_id) These unlinked returns are created through a negative procurement, example: when the ordered quantity is reduced below the delivered one. Steps to reproduce: - Confirm a sale order of 10 and deliver the 10 - Cancel the order and reset it to draft - Reduce the line quantity from 10 to 4 - Re-confirm: the pull rule generates a return of 6 with to_refund set but no origin_returned_move_id nor picking.return_id - Validate that return Before: delivered stays at 10. After: delivered is 4. opw-6345628 Forward-Port-Of: odoo/odoo#274399
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic attribute and two values. 2- Publish the product and uncheck sell when out-of-stock and check show product when the qty is less than 5. 3- Create a purchase order with qty = 4 for the first value, so a variant is created for it. 4- Go to the website shop. Open the product. 4 available qty i
Original PR description
Issue: --- If a product template has dynamic attributes, some variants might not exist. For those variants, we are showing wrong stock in the website. To reproduce: 1- Create a product with a dynamic attribute and two values. 2- Publish the product and uncheck sell when out-of-stock and check show product when the qty is less than 5. 3- Create a purchase order with qty = 4 for the first value, so a variant is created for it. 4- Go to the website shop. Open the product. 4 available qty in stock is shown for the first variant which is correct. 5- Select 2nd variant. As you see, still 4 available qty is shown which is wrong. As the out-of-stock sale is unchecked, an out-of-stock warning should be shown. Cause and Fix: --- This is due to `isMainProduct` being always False when `product_id` is not set which makes `free_qty` and `out_of_stock` not to be updated. opw-6237602 Forward-Port-Of: odoo/odoo#277259 Forward-Port-Of: odoo/odoo#273104
### Issue before this commit: When importing an electronic invoice from SDI that is failing with an exception, the resulting account.move record was saved in the "Miscellaneous Operations" journal instead of the correct one (Vendor Bills), even though the move_type itself was correct. ### Steps to reproduce the issue: Pre steps: you need to have access to https://iap-services-test.odoo.com/odoo 1. Download Accounting and l10n_it 2. Go to Settings > Companies and set the VAT of IT company
Original PR description
### Issue before this commit: When importing an electronic invoice from SDI that is failing with an exception, the resulting account.move record was saved in the "Miscellaneous Operations" journal…
### Issue before this commit: When importing an electronic invoice from SDI that is failing with an exception, the resulting account.move record was saved in the "Miscellaneous Operations" journal instead of the correct one (Vendor Bills), even though the move_type itself was correct. ### Steps to reproduce the issue: Pre steps: you need to have access to https://iap-services-test.odoo.com/odoo 1. Download Accounting and l10n_it 2. Go to Settings > Companies and set the VAT of IT company the same as the one in the xml 3. Go to Settings > Italian Electronic Invoicing and select Test 4. Go into the code and insert an Exception inside the function _l10n_it_edi_import_invoice after self.move_type = move_type (or create any type of exception from the user interface) 5. Go to IAP service into IT EDI app and see that your company is there as user 6. Click into the record > receive move button > upload your xml > create 7. Go to your DB > Scheduled Actions > filter with IT > IT EDI: Receive invoices from the SdI > Run Manually 8. Go to Journal entries, remove the filter and find your imported bill 9. You can see it was inserted into the Miscellaneous Operations Journal instead of a Vendor Bill Journal ### Cause of the issue: The move is created inside a savepoint context manager, designed so that even if parsing fails, an empty move with the attachment still remains. The problem is that if the exception is raised, the savepoint rollback undoes everything that follows, but the journal was already determined before the correct move_type was known, leaving the move in the wrong default journal. ### Reason to introduce the fix: The fix is needed to ensure that, regardless of where parsing fails, the move's journal is correctly set even if an exception occurs so that it is possible to find the move in the correct section even if not imported correctly. opw-6397712 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279566 Forward-Port-Of: odoo/odoo#278111
4 changes
Resolved issues and error corrections
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, i
Original PR description
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill…
Steps to reproduce: Use the real testing credentials Make sure the invoice sequence is not generated on the real testing credentials 1. Create an invoice with overseas partner 2. Create ewaybill Error from the portal: `[372] Invalid or Blank Consignee Ship-to State Code` It is currently a flaw in the government portal because in Government portal there is no option for the Other country (99) for Ship to state code and only option for Other Teritory(97) It is because in reality, it should the port ship to state code but there cases where goods can be transfered to nearby country i.e. Bangladesh, Nepal where good can taken by road from India In that case the state code should be 97 task-6431082 **Second Commit** - [FIX] l10n_in_ewaybill: import/export GSTIN should be URP Steps to reproduce: Use the real testing credentials Create a SEZ partner Create an invoice and ewaybill Select the type of Ewaybill as Export Tax Invoice We get error code-450 which clearly states, `450 For outward-export ewaybill, To GSTIN has to be either URP or SEZ` --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the receipt of 2 Comp - Go to the valuation > Both units of Comp are valued at 100 for a total of 200 ### Cause of the issue: The issue has been introduced by: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063 To be more precise, the price unit of each u
Original PR description
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the…
### Steps to reproduce: - Create 3 AVCO products: Super Kit, Kit, Comp - Super Kit BoM: 2 x Kit - Kit BoM: 1 x Comp - Create and confirm a purchase order for 1 X Super Kit at 100 - Validate the receipt of 2 Comp - Go to the valuation > Both units of Comp are valued at 100 for a total of 200 ### Cause of the issue: The issue has been introduced by: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063 To be more precise, the price unit of each unit of Comp is expected to be computed by the `_get_price_unit`. This method used to rely on the `product_qty` appropriately: https://github.com/odoo/odoo/pull/158849/changes/713701a5035d342263e3fef2a2819b5696b6d063#diff-687527af1723e60816358020c4d83687479df62ca0cfd71079f0a82afb4b3efeL27 However, backorder adapt the move demand and hence did not provide the appropriate demand in this flow that computation logic was changed to rely on the `bom` and `bom_line` quantities: https://github.com/odoo/odoo/blob/29977a6a80442af49ecefa7fef54f085483d8f77/addons/purchase_mrp/models/stock_move.py#L20-L40 This new computation is not correct in case of nested boms since the `bom_line` only carries the unit demand on the last explosion stage. ### Additional issue: If nested kit boms lead to the creation of 2 moves with the same `cost_share` and `bom_line_id`, these moves will be merged without summing their `cost_share` leading to an under pricing of the kit since its related `stock_move`'s `cost_share` will not sum up to 100 percents anymore. This issue is tested in `test_avco_purchase_nested_kit_explode_cost_share_backorder_2` and fixed similarly to demand merging: https://github.com/odoo/odoo/blob/613f3cb7b2f4813ce6c8f53718a6cca841b081ad/addons/stock/models/stock_move.py#L1122-L1134 ### Note: We also modify the test `test_valuation_with_backorder` to be understandable and to make appropriate asserts. opw-6253776 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#276242
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Original PR description
Portal subscribe task can create tb because the task was archived before unsubscribing. opw-6397850 Forward-Port-Of: odoo/odoo#279071 Forward-Port-Of: odoo/odoo#278667
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no l
Original PR description
Steps to reproduce: - Enable the ZUGFeRD (or Factur-X) e-invoicing format on a German customer. - Create a sale order for that customer, confirm it, create an invoice with a down payment. - Confirm and send the invoice to generate the PDF/XML. - Validate the XML (e.g. on portinvoice.com): it is rejected because the invoice line is missing the mandatory ram:Name field, only ram:Description is present. Cause of the issue: a down payment invoice line created from a sale order no longer carries a product_id: it only has a free-text. The Factur-X/CII export template rendered ram:Name directly from line.product_id.name with no fallback. For a line without a product, this produced an empty ram:Name element, which cleanup_xml_node then stripped entirely from the XML, leaving only ram:Description. Solution: Fall back to the line's name when there is no product opw-6391121 Forward-Port-Of: odoo/odoo#277418
5 changes
Enhancements to existing features
No description available.
Resolved issues and error corrections
**Steps to reproduce:** This issue is hard to reproduce because it requires a live ZATCA connection: - As a user with read-only permission on journals, send an invoice to ZATCA. - You get an access error on the journal, and the invoice is unchanged (You can try sending it again to ZATCA). **Issue:** What happens is: - A user with read-only permission on journals sends an invoice to ZATCA. - If ZATCA responds with a 200 (successfully submitted), we try to write on the field `journal.l10n
Original PR description
**Steps to reproduce:** This issue is hard to reproduce because it requires a live ZATCA connection: - As a user with read-only permission on journals, send an invoice to ZATCA. - You get an access error on the journal, and the invoice is unchanged (You can try sending it again to ZATCA). **Issue:** What happens is: - A user with read-only permission on journals sends an invoice to ZATCA. - If ZATCA responds with a 200 (successfully submitted), we try to write on the field `journal.l10n_sa_latest_submission_hash` - With no write permissions, the write fails and all changes are rolled back (on odoo, not on ZATCA) - We can send the invoice again to ZATCA, resulting in duplicates. **Solution:** - Added a sudo when writing on the field: `journal.l10n_sa_latest_submission_hash` opw-6320179 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Problem: When creating inline code from formatted text, the formatting is not preserved for the text that follows the inline code. Solution: Preserve the active text formatting (e.g., bold, italic, underline) when inserting inline code, ensuring subsequent text on the same line retains the previously applied styles. Steps to reproduce: - Go to To-Do → Create New. - Type some text in bold. - Insert an inline code block. - Continue typing after the inline code. - Observe that the text
Original PR description
Problem: When creating inline code from formatted text, the formatting is not preserved for the text that follows the inline code. Solution: Preserve the active text formatting (e.g., bold, italic, underline) when inserting inline code, ensuring subsequent text on the same line retains the previously applied styles. Steps to reproduce: - Go to To-Do → Create New. - Type some text in bold. - Insert an inline code block. - Continue typing after the inline code. - Observe that the text after the inline code is no longer bold. opw-6395163 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
When updating mail notifications during `mail.mail._send()`, a `SerializationFailure` raised while flushing the notification recordset leaves the current transaction in an aborted state. As `_send()` continues handling the exception, accessing fields: - https://github.com/odoo/odoo/blob/127f1316540ec6cc6880ea3515e6923ba3903bc7/addons/mail/models/mail_mail.py#L816 So, any subsequent SQL query fails with `InFailedSqlTransaction`, masking the original concurrency error. Avoid acces
Original PR description
When updating mail notifications during `mail.mail._send()`, a `SerializationFailure` raised while flushing the notification recordset leaves the current transaction in an aborted state. As `_send()`…
When updating mail notifications during `mail.mail._send()`,
a `SerializationFailure` raised while flushing the notification recordset leaves the current transaction in an aborted state.
As `_send()` continues handling the exception, accessing fields:
- https://github.com/odoo/odoo/blob/127f1316540ec6cc6880ea3515e6923ba3903bc7/addons/mail/models/mail_mail.py#L816
So, any subsequent SQL query fails with
`InFailedSqlTransaction`, masking the original concurrency error.
Avoid accesing to `mail.message_id` with aborted cursor, preserving the original `SerializationFailure`.
A regression test is added to simulate a concurrency failure during
`flush_recordset()` and verify that the cursor is no longer used dirty
The logger for the unittest without the fix is the following:
```log
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "odoo/addons/mail/models/mail_mail.py", line 719, in _send
notifs.flush_recordset(['notification_status', 'failure_type', 'failure_reason'])
File "<string>", line 3, in flush_recordset
File "unittest/mock.py", line 1139, in __call__
return self._mock_call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "unittest/mock.py", line 1143, in _mock_call
return self._execute_mock_call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "unittest/mock.py", line 1204, in _execute_mock_call
result = effect(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/addons/test_mail/tests/test_message_post_concurrent.py", line 93, in mocked_mail_notification_flush_recordset
return original_flush_recordset(self, *vals, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 6788, in flush_recordset
self._flush(fnames)
File "odoo/odoo/models.py", line 6852, in _flush
model.browse(some_ids)._write_multi(vals_list)
File "odoo/odoo/models.py", line 4938, in _write_multi
self.env.execute_query(SQL(
File "odoo/odoo/api.py", line 993, in execute_query
self.cr.execute(query)
File "odoo/odoo/sql_db.py", line 371, in execute
res = self._obj.execute(query, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.SerializationFailure: could not serialize access due to concurrent update
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "odoo/addons/test_mail/tests/test_message_post_concurrent.py", line 107, in test_mail_send_dirty_cursor
mails.send()
File "odoo/addons/mail/models/mail_mail.py", line 652, in send
self.browse(batch_ids)._send(
File "odoo/addons/mail/models/mail_mail.py", line 818, in _send
mail.id, mail.message_id)
^^^^^^^^^^^^^^^
File "odoo/odoo/fields.py", line 1309, in __get__
self.compute_value(recs)
File "odoo/odoo/fields.py", line 1491, in compute_value
records._compute_field_value(self)
File "odoo/odoo/models.py", line 5302, in _compute_field_value
fields.determine(field.compute, self)
File "odoo/odoo/fields.py", line 113, in determine
return needle(records, *args)
^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/fields.py", line 710, in _compute_related
record[self.name] = self._process_related(value[self.related_field.name], record.env)
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 7083, in __getitem__
return self._fields[key].__get__(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/fields.py", line 1272, in __get__
recs._fetch_field(self)
File "odoo/odoo/models.py", line 4120, in _fetch_field
self.fetch(fnames)
File "odoo/addons/mail/models/mail_message.py", line 756, in fetch
return super().fetch(field_names)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 4158, in fetch
fetched = self._fetch_query(query, fields_to_fetch)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/models.py", line 4245, in _fetch_query
rows = self.env.execute_query(query.select(*sql_terms))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "odoo/odoo/api.py", line 993, in execute_query
self.cr.execute(query)
File "odoo/odoo/sql_db.py", line 371, in execute
res = self._obj.execute(query, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block
```
Real error in production:
```log
2023-04-15 10:43:14,978 106451 ERROR my_db odoo.sql_db: bad query: UPDATE "mail_notification" SET "failure_reason" = "__tmp"."failure_reason"::text, "failure_type" = "__tmp"."failure_type"::VARCHAR, "notification_status" = "__tmp"."notification_status"::VARCHAR FROM (VALUES (4426629, 'Error without exception. Probably due to concurrent access update of notification records. Please see with an administrator.', 'unknown', 'exception')) AS "__tmp"("id", "failure_reason", "failure_type", "notification_status") WHERE "mail_notification"."id" = "__tmp"."id" ERROR: could not serialize access due to concurrent update
```
```log
2023-04-14 10:43:14,978 106451 ERROR my_db odoo.sql_db: bad query: UPDATE "mail_mail" SET "failure_reason"='Error without exception. Probably due do sending an email without computed recipients.',"headers"='{''X-SMTPAPI'': ''{"ip_pool": "Transactional"}'', ''X-Odoo-Objects'': ''sale.order-1436960''}',"state"='exception',"write_uid"=1,"write_date"=(now() at time zone 'UTC') WHERE id IN (2548540)
ERROR: current transaction is aborted, commands ignored until end of transaction block
```
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
# UPDATE 2026-07-22
The reviewer requested to remove the large docstring
For record, the docstring was
```python
"""Reproduces a concurrency scenario where `mail_mail._send()` fails with a PSQL SerializationFailure after
flushing `mail.notification` records. After such a failure, the cursor is left in an aborted
(`InFailedSqlTransaction`) state, so any further SQL access (e.g. reading `mail.message_id` like
https://github.com/odoo/odoo/blob/127f1316540ec6cc68/addons/mail/models/mail_mail.py#L816)
would raise a new error masking the original SerializationFailure.
Setup:
- Uses a separate `cursor()` to create and commit a message with its `mail.mail` and `mail.notification`
records, so they are visible to a second, concurrent transaction.
Concurrency simulation:
- `MailNotification.flush_recordset` is patched so that, right before the real flush runs, a second cursor
updates the same `mail.notification` records (`failure_reason`). This forces PSQL to raise a
SerializationFailure when the original transaction tries to flush those rows.
Assertions:
- `SerializationFailure` is raised confirming the concurrency conflict.
- `mail_mail._send()` logs the expected error message containing the mail `id` and `message-id`
Cleanup: created records are unlinked in `finally`
"""
```
# UPDATE 2026-07-23
The reviewer requested to remove the unittest
For record, the unittest was
```diff
diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py
index 53dd5b9eec52..46a3958a5bff 100644
--- a/addons/test_mail/tests/test_message_post.py
+++ b/addons/test_mail/tests/test_message_post.py
@@ -7,17 +7,21 @@ from datetime import datetime, timedelta
from freezegun import freeze_time
from itertools import product
from markupsafe import escape, Markup
+from psycopg2.errorcodes import SERIALIZATION_FAILURE as SERIALIZATION_FAILURE_CODE
+from psycopg2.errors import SerializationFailure
from unittest.mock import patch
-from odoo import tools
+from odoo import SUPERUSER_ID, api, tools
from odoo.addons.base.tests.test_ir_cron import CronMixinCase
-from odoo.addons.mail.tests.common import mail_new_test_user, MailCommon
+from odoo.addons.mail.models.mail_notification import MailNotification
+from odoo.addons.mail.tests.common import mail_new_test_user, MailCommon, MockEmail
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE_PLAINTEXT
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
from odoo.addons.test_mail.tests.common import TestRecipients
from odoo.api import call_kw
from odoo.exceptions import AccessError
-from odoo.tests import tagged
+from odoo.modules.registry import Registry
+from odoo.tests import TransactionCase, get_db_name, tagged
from odoo.tools import mute_logger, formataddr
from odoo.tests.common import users
@@ -2244,3 +2248,49 @@ class TestMessagePostLang(MailCommon, TestRecipients):
self.assertIn('html lang="es_ES"', email['body'])
else:
self.assertIn('html lang="en_US"', email['body'])
+
+
+@tagged('database_breaking')
+class TestMessagePostConcurrent(MockEmail, TransactionCase):
+ """Mail concurrency edge cases that require real, separately committed transactions
+ instead of the usual rollback-based TransactionCase isolation.
+ """
+
+ def test_mail_send_dirty_cursor(self):
+ """Reproduces SerializationFailure `mail_mail._send()` fails,
+ the cursor is left in an aborted state, so any further SQL access would raise a new error
+ (e.g. reading `mail.message_id` like
+ https://github.com/odoo/odoo/blob/127f1316540ec6cc68/addons/mail/models/mail_mail.py#L816)
+ """
+ original_flush_recordset = MailNotification.flush_recordset
+
+ def mocked_mail_notification_flush_recordset(self, *args, **kwargs):
+ with Registry(get_db_name()).cursor() as cr:
+ cr.execute('UPDATE mail_notification SET failure_reason = %s WHERE id IN %s', ('Forced Concurrent Update', tuple(self.ids)))
+ return original_flush_recordset(self, *args, **kwargs)
+
+ recs2unlink = []
+ with Registry(get_db_name()).cursor() as cr:
+ env = api.Environment(cr, SUPERUSER_ID, {})
+ partner = env.ref('base.user_admin').partner_id
+ try:
+ message = partner.message_post(body='Hello', message_type='comment', partner_ids=[partner.id], mail_auto_delete=False, force_send=False)
+ notifs = env['mail.notification'].search([('notification_type', '=', 'email'), ('mail_mail_id', 'in', message.mail_ids.ids)])
+ self.assertTrue(notifs)
+ mails = message.mail_ids
+ recs2unlink.extend([notifs, mails, message])
+ cr.commit()
+
+ mails = self.env[mails._name].browse(mails.ids)
+ with (
+ mute_logger('odoo.sql_db'), self.assertRaises(SerializationFailure) as exc, self.mock_mail_gateway(),
+ patch(f'{MailNotification.__module__}.{MailNotification.__name__}.flush_recordset', autospec=True, side_effect=mocked_mail_notification_flush_recordset),
+ self.assertLogs('odoo.addons.mail.models.mail_mail', level='ERROR') as log_capture,
+ ):
+ mails.send()
+ finally:
+ for rec2unlink in recs2unlink:
+ env[rec2unlink._name].browse(rec2unlink.ids).unlink()
+
+ self.assertEqual(exc.exception.pgcode, SERIALIZATION_FAILURE_CODE)
+ self.assertIn(f'Exception while processing mail with ID {mails.id} and Msg-Id \'{mails.message_id}\'.', [record.message for record in log_capture.records])
```
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prIn 18.0, self.user_demo inside TestAttachmentController does not have a company id if the --without-demo flag is set. This pr alters the assertion by turning it into an instantion to ensure a selected company
Original PR description
In 18.0, self.user_demo inside TestAttachmentController does not have a company id if the --without-demo flag is set. This pr alters the assertion by turning it into an instantion to ensure a selected company
1 change
Resolved issues and error corrections
### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Set VAT number for DE company and another company you will use for the invoice 3. Switch to german language 4. Create an invoice and send it 5. Download the PDF and upload it on www.portinvoice.com 6. See the following error: the PDF metadata incorrectly states the conformance level as "ERWEITERT" (German), which directly clashes with the correct "EXTENDED" (English) profile declared inside the embedded XML file. ##
Original PR description
### Steps to reproduce the issue: 1. Download Accounting and l10n_de 2. Set VAT number for DE company and another company you will use for the invoice 3. Switch to german language 4. Create an invoice and send it 5. Download the PDF and upload it on www.portinvoice.com 6. See the following error: the PDF metadata incorrectly states the conformance level as "ERWEITERT" (German), which directly clashes with the correct "EXTENDED" (English) profile declared inside the embedded XML file. ### Cause of the issue: Issue comes from this commit 9396790e9cc1ce1c6e5c29b71b5629b31fb16458 where it has been forgotten to disable the translation. ### Reason to introduce the fix: Meet the requirements of the electronic invoice. opw-6023971 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr