Daily updates from Odoo
Saturday, July 25, 2026
6 changes · saas-19.3
Resolved issues and error corrections
WinBooks imports now keep contacts separate when they have the same number but explicitly different VAT numbers. This prevents customer and supplier details from being combined incorrectly, avoiding VAT validation errors that could block an import.
Original PR description
When importing a WinBooks zip, contacts sharing the same number might fail VAT validation if distinct contact data is merged incorrectly. Steps to reproduce: - Create a database with the Belgian…
When importing a WinBooks zip, contacts sharing the same number might fail VAT validation if distinct contact data is merged incorrectly. Steps to reproduce: - Create a database with the Belgian localization installed - Install the account_winbooks_import module - Navigate to Accounting > Configuration > Settings > Initial Setup > Import - Import the WinBooks zip Issue: The import fails with the following error: The VAT number [AAAAAA] for partner [BBBBBB] does not seem to be valid. Note: the expected format is BExxxxxxx. Analysis: In WinBooks, contacts can be of type Supplier or Customer, and it is possible for a supplier and a customer to share the same number. During the import, the system will merge contacts if found with the same number. In the specific case, the VAT number from one contact without country is combined with the country information from another contact, resulting in a VAT validation error. This change prevents the merger of partner data if their VAT numbers are explicitly different. opw-6251772 Forward-Port-Of: odoo/enterprise#119579
Currentyl the unsupported models will be checked like the following ```sql ia.res_model NOT IN ARRAY['model1', 'model2'] ``` This causes a syntax error. Use `ANY` to avoid the issue ```sql ia.res_model != ANY(ARRAY['model1', 'model2']) ``` Reproduce in Odoo shell ```python from odoo.tools import SQL query = SQL(""" SELECT ia.id FROM ir_attachment ia WHERE ia.res_model NOT IN %(unsupported_models)s LIMIT 1; """, unsupported_models=self.env['ir.atta
Original PR description
Currentyl the unsupported models will be checked like the following
```sql
ia.res_model NOT IN ARRAY['model1', 'model2']
```
This causes a syntax error. Use `ANY` to avoid the issue
```sql
ia.res_model != ANY(ARRAY['model1', 'model2'])
```
Reproduce in Odoo shell
```python
from odoo.tools import SQL
query = SQL("""
SELECT ia.id
FROM ir_attachment ia
WHERE ia.res_model NOT IN %(unsupported_models)s
LIMIT 1;
""",
unsupported_models=self.env['ir.attachment']._get_cloud_storage_unsupported_models(),
)
```
opw-6404861
Forward-Port-Of: odoo/odoo#277971It currently deleted the inner mail.message, while it should actually remove mail.mail records. Removing messages is managed at mail.mail unlink level using the 'is_notification' field. Indeed a mass_mailing email should remove the linked mail.message, while a canceled notification email should not remove the chatter message. Followup of odoo/odoo#209494 Forward-Port-Of: odoo/odoo#278321 Forward-Port-Of: odoo/odoo#277993
Original PR description
It currently deleted the inner mail.message, while it should actually remove mail.mail records. Removing messages is managed at mail.mail unlink level using the 'is_notification' field. Indeed a mass_mailing email should remove the linked mail.message, while a canceled notification email should not remove the chatter message. Followup of odoo/odoo#209494 Forward-Port-Of: odoo/odoo#278321 Forward-Port-Of: odoo/odoo#277993
Steps to reproduce ================== - Log in with the portal user (or any user without karma points). - Open a forum post. - Try to mark it as the best answer. Technical ========= From here https://github.com/odoo/odoo/pull/215145/changes/46e5ab20cb33536f0cf12d2fc3690beaa11697ee `htmlJoin` expects its first argument to be an iterable and its second argument separator to be a string. Passing the message string as the first argument causes it to be treated as an iterable of characters,
Original PR description
Steps to reproduce ================== - Log in with the portal user (or any user without karma points). - Open a forum post. - Try to mark it as the best answer. Technical ========= From here https://github.com/odoo/odoo/pull/215145/changes/46e5ab20cb33536f0cf12d2fc3690beaa11697ee `htmlJoin` expects its first argument to be an iterable and its second argument separator to be a string. Passing the message string as the first argument causes it to be treated as an iterable of characters, while the guideline markup passed as the second argument is unintentionally used as the separator. As a result, the guideline link is inserted between every character of the message instead of being appended after it. Wrap the message and the additional guideline text in an array when calling `htmlJoin` so the notification is rendered correctly. Task-6302585 Forward-Port-Of: odoo/odoo#276137
Issue: When a shopper pays for an online order entirely using a reward (e.g. a discount code covering 100% of the total), the order total is zero. With automatic invoicing enabled, an invoice with an amount of 0 is created. But, the 0 invoice is never emailed to the customer. Whereas, for orders where the total is more than 0, an invoice is created, then emailed to the customer. The customer should be emailed the invoice, even if its amount is 0. Steps to reproduce: 1. Enable automatic invo
Original PR description
Issue: When a shopper pays for an online order entirely using a reward (e.g. a discount code covering 100% of the total), the order total is zero. With automatic invoicing enabled, an invoice with an…
Issue: When a shopper pays for an online order entirely using a reward (e.g. a discount code covering 100% of the total), the order total is zero. With automatic invoicing enabled, an invoice with an amount of 0 is created. But, the 0 invoice is never emailed to the customer. Whereas, for orders where the total is more than 0, an invoice is created, then emailed to the customer. The customer should be emailed the invoice, even if its amount is 0. Steps to reproduce: 1. Enable automatic invoicing. 2. Create a code for a 100% discount. 3. As a shopper, add a product to cart on the website. 4. While checking out, apply the 100% discount code to the order. 5. Complete the checkout. 6. Confirm that an invoice was created and posted, but was not emailed to the customer. Explanation: Normally, order confirmation and invoicing are handled by the `_post_process` method on the `payment.transaction` model. With automatic invoicing enabled, `_post_process` confirms the sale order, creates the invoice, and sends the invoice via `_send_invoice` (another method on the `payment.transaction` model). If `sale.async_emails` is enabled, `_post_process` will trigger a cron that invokes `_send_invoice` instead of invoking it directly. When an order is fully covered by a reward, there's nothing to pay. In this case, no payment.transaction record is ever created, and `_post_process` never runs. Instead, the order is confirmed through the `_validate_order` method on the `sale.order` model. The `sale_loyalty` module extends `_validate_order` so that, with automatic invoicing enabled, it will create and post an invoice for zero-amount orders. But, nothing in this path ever calls `_send_invoice` or an equivalent. So, the invoice is created and posted but never sent. Solution: This adds logic for sending invoices to the extension of `_validate_order` in the `sale_loyalty` module. We mirror the logic used in `_send_invoice` in the `payment.transaction` model. Notes: There is duplicated code from `_send_invoice` in this fix. That is because `_send_invoice`, a method on the `payment.transaction` model, can't be used in this flow. A fix that avoids code duplication would require serious refactoring. This will never trigger a cron to send the invoice, even if `sale.async_emails` is enabled. That is because the cron invokes `_send_invoice`. Since fully reward-covered orders are probably not common, any performance benefits of using a cron are probably not significant. But, making a new cron to be used in this case is also an option. opw-6363334 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#277678 Forward-Port-Of: odoo/odoo#275190
**STEP TO REPRODUCE** 1. Install l10n_fr_pdp and select the french company. 2. Go on a contact form, under invoicing, select 'by Approved Platform' for invoice sending. 3. Click on the eInvoice format selection, and notice the format 'France E-invoicing (UBL 2.1)' is not there. Note: with other invoice sending values, it shows up. **CAUSE** In the `_get_ubl_cii_formats_info()` override in `l10n_fr_pdp`, we declare the ubl_21_fr format as not being usable with the peppol invoice sending me
Original PR description
**STEP TO REPRODUCE** 1. Install l10n_fr_pdp and select the french company. 2. Go on a contact form, under invoicing, select 'by Approved Platform' for invoice sending. 3. Click on the eInvoice format selection, and notice the format 'France E-invoicing (UBL 2.1)' is not there. Note: with other invoice sending values, it shows up. **CAUSE** In the `_get_ubl_cii_formats_info()` override in `l10n_fr_pdp`, we declare the ubl_21_fr format as not being usable with the peppol invoice sending method. However, the Approved Platform invoice sending (used to send ubl_21_fr) *is* the peppol invoice sending method in disguise. (we reused the peppol invoice sending method because pdp and peppol are very similar). opw-6387796 Forward-Port-Of: odoo/odoo#276276