Daily updates from Odoo
Friday, June 5, 2026
11 changes · 17.0
Resolved issues and error corrections
This update fixes a potential problem where users could accidentally trigger mass email campaigns bypassing campaign filters. The change prevents users from directly retrying failed mailings linked to marketing automation, reducing the risk of unintended spam and ensuring emails are delivered correctly through the campaign's intended targeting. A unit test has also been added for future maintenance.
Original PR description
When a mailing is managed by a marketing automation campaign, its target domain is dynamically handled by the campaign's activities. If a user clicks the "Retry" button directly on the mailing…
When a mailing is managed by a marketing automation campaign, its target domain is dynamically handled by the campaign's activities. If a user clicks the "Retry" button directly on the mailing template, it bypasses the campaign filters and queues the mailing for the entire target model, causing unintended mass spam. This commit fixes the issue by: 1. Raising a UserError in `action_retry_failed` if the mailing is linked to marketing automation (`use_in_marketing_automation`). 2. Hiding the "Retry" button in the frontend view to prevent confusion. 3. Adding a unit test to ensure this edge case is caught in the future. Steps to reproduce: 1. Create a marketing campaign with a filter and an email activity. 2. Run the activity and ensure at least one email trace fails. 3. Open the mailing template via the "Templates" smart button. 4. Click the "Retry" button on the template form. 5. The mailing is placed in the standard queue, bypassing the domain and targeting all records of the underlying model. OPW-6220106 Forward-Port-Of: odoo/enterprise#118759
This update corrects an issue in the l10n_lu_reports module that caused incorrect balance sheet reports due to incorrect values in XML fields. Specifically, fields 2955 and 2956 must be set to zero, as dictated by eCDF regulations. Fixing this ensures reports are accepted by the eCDF system, preventing rejection and maintaining accurate financial reporting.
Original PR description
Before this commit, fields 2955 and 2956 in the balance sheet could be incorrect. 2955 must always be blank (not exist) and 2956 must always be 0 per: https://ecdf-developer.b2g.etat.lu/ecdf/forms/popup/CA_PLANCOMPTA/2020/en/2/rules page 116 + 117 If they are not these values specifically, submitting the XML to eCDF results in the report being rejected. Steps to reproduce: - Install l10n_lu_reports - Create a journal entry for a closed year (2025) that debits account 142000 and credits another account that starts with a 1 - Go to the balance sheet for 2025 - Download the XML for the report - 2955 is present and 2956 is either not present or is not 0 (behavior varies between versions) Ticket [link](https://www.odoo.com/odoo/project.task/6246564) opw-6246564
This update significantly speeds up the process of synchronizing participants with marketing campaigns. By optimizing a key function, the time taken to update campaign lists has been reduced dramatically – from over 51 seconds to just 0.65 seconds. This improvement will enhance the responsiveness of campaign management and improve user experience.
Original PR description
Replace search_read with search_fetch to avoid unnecessary _read_format call in backend context. Use OrderedSet instead of a custom _uniquify_list helper to get O(1) membership tests when computing records to add or remove from campaigns. Benchmark on a campaign with 115k participants: | Before PR | After PR | |:---------:|:--------:| | 51.71s | 0.652s | opw-6055334
This update resolves a technical problem that prevented PDFs from being correctly attached to invoices when using the Nilvera e-invoicing system. The change ensures compatibility with Python 3.14's stricter data validation rules, allowing the system to handle PDF files properly.
Original PR description
This commit resolves an error encountered when running on Python 3.14, which enforces stricter base64 validation. When adding a PDF to the invoice, the PDF is fetched using the Nilvera client. This client performs an HTTP request and returns a raw binary response, not a base64 representation. However, the Attachment interface handles raw binary data via the 'raw' field, whereas the 'datas' field strictly expects base64-encoded values. runbot-938173
This update fixes a problem where Odoo's error messages related to translation files were unclear, making it difficult to identify the specific file causing the issue. Now, Odoo logs the exact path of the problematic translation file, significantly improving debugging and troubleshooting for translation errors. This enhances the overall stability and usability of the Odoo platform.
Original PR description
Description of the issue/feature this PR addresses: #184630 The error message is unclear as to which is the file containing the error. Current behavior before PR: Before this commit, Odoo would show a not helpful message like: [lang: es][format: po] This does not specify or help the developer to locate the file that contains the error. Desired behavior after PR is merged: After the commit, we correctly log the path of the file that raised the error. closes #184630 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes an issue where images in email marketing templates were stretched and distorted when paired with long text. The change removes unnecessary styling, ensuring images maintain their aspect ratio and fit naturally alongside the text, providing a cleaner and more professional email experience.
Original PR description
The media list snippet forces the image to fill the height of its row. The image column carries align-self-stretch and the image carries h-100, so when the text next to the image is longer than the…
The media list snippet forces the image to fill the height of its row. The image column carries align-self-stretch and the image carries h-100, so when the text next to the image is longer than the image is tall, the row grows to fit the text and the image is stretched to that height (and cropped through object-fit: cover). The longer the text, the more the image is distorted. Drop h-100 from the image and align-self-stretch from its column in the s_media_list snippet and in the mass_mailing_themes templates that reuse it. With no forced height the image keeps its natural aspect ratio and the row height follows its content, so the image is laid out next to the text instead of being stretched to match it. Steps to reproduce: 1. Open Email Marketing and create a new mailing. 2. Select the Blogging template for the mail body. 3. In a media item, replace the text next to an image with a very long paragraph. => The image is stretched and cropped to match the height of the text. Ticket [link](https://www.odoo.com/odoo/project.task/5117571) opw-5117571
This update resolves an issue where updating the extra menu caused the main website menu to unexpectedly close. By closing the extra menu before opening the site menu, the system now behaves consistently and avoids unpredictable errors. This ensures a smoother user experience for website visitors.
Original PR description
[FIX] website: close the extra menu before opening site menu Update of the extra menu item is done multiple times (cfr `afterFontsloading`). If the extra menu item and the site menu were already open before an update of the extra menu item, the result is a close of the site menu. This can lead to undeterministic error. To solve the problem, the extra menu dropdown is closed before opening the site menu. runbot-240955
This update optimizes how Odoo compiles QWeb templates, resulting in faster performance. The change addresses a performance issue introduced in Markupsafe version 2.1.4, specifically related to how it handles HTML tags, leading to significant speed improvements for template rendering.
Original PR description
Starting version 2.1.4 of markupsafe, they decided to adapt the striptags function to use in-python-loops instead of the original implemenation that relied on pre-compiled regex. A problem has been…
Starting version 2.1.4 of markupsafe, they decided to adapt the striptags function to use in-python-loops instead of the original implemenation that relied on pre-compiled regex. A problem has been spotted with qweb templates that used striptags with large inputs, which led to the investigation of this function and it was found that the old implementation is actually faster. The new implementation of markupsafe is O(N x M), where n is the number of tags and M being the length of the input string. The old regex approach does a single c-level scan to check the existence of the regex which is performing much better. The benchmark cases are in the form `<case_description>_<number_of_tags>`. We can see that the only cases where the current implementation is slightly faster is when there are no tags in the input which can be explained by the fact that the while loops will simply exit early. The time lost in the regex implementation is likely due to the deeper call stack to scan for the regex. Apart from that, the old implementation is consistently much more performant, for both small and large inputs. Benchmarks: | Case | Regex µs | Current µs | Speedup | |----------------------------------------|----------|--------------|----------| | plaintext_no_tags_50k_words | 2937.05 | 2795.78 | 0.95x ← current_implementation | | html_entities_only_no_tags_5k | 4843.88 | 4818.92 | 0.99x ← current_implementation | | comments_only_1k | 382.75 | 1985.56 | 5.19x ← regex_old_implementation | | comments_containing_tags_1k | 234.88 | 1243.65 | 5.29x ← regex_old_implementation | | comments_only_50k | 21047.84 | 21081024.01 | 1001.58x ← regex_old_implementation | | comments_containing_tags_50k | 13948.90 | 11301014.95 | 810.17x ← regex_old_implementation | | short_tags_5k | 915.32 | 28235.99 | 30.85x ← regex_old_implementation | | short_tags_20k | 3603.37 | 876001.98 | 243.11x ← regex_old_implementation | | short_tags_50k | 10648.04 | 9064394.50 | 851.27x ← regex_old_implementation | | nested_divs_1k_deep | 148.99 | 880.14 | 5.91x ← regex_old_implementation | | nested_divs_10k_deep | 1730.14 | 54162.25 | 31.31x ← regex_old_implementation | | tags_with_many_attrs_2k | 1165.15 | 16554.24 | 14.21x ← regex_old_implementation | | tags_with_many_attrs_20k | 12659.77 | 7297957.10 | 576.47x ← regex_old_implementation | | multiline_tags_20k | 10227.63 | 5476065.12 | 535.42x ← regex_old_implementation | | mixed_comments_with_tags_text_2k | 309.79 | 3034.69 | 9.80x ← regex_old_implementation | | mixed_comments_with_tags_text_10k | 1593.39 | 79993.58 | 50.20x ← regex_old_implementation | This PR is needed because requirements.txt in Odoo specifies the following dependency: MarkupSafe==2.1.5 ; python_version >= '3.12' \# (Noble) This means that all versions running Ubuntu Noble, will be having the same issue introduced in version 2.1.4 of markupsafe. opw-5999688 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update fixes an issue where the Italian EDI bill import process only recognized the first tax listed in vendor XML files. Now, all taxes specified in the XML are correctly applied, ensuring accurate VAT calculations and reporting for Italian businesses. This improves data integrity and compliance.
Original PR description
Previously, the Italian EDI vendor bill import only processed a single tax per line. When the XML contained multiple tax entries, only the first one was considered and the remaining taxes were ignored. This fix updates the import logic to properly read and apply all taxes provided in the XML, ensuring the vendor bill accurately reflects the full tax structure defined in the file. task-5258180
This update resolves an issue where moving Odoo databases via the command line would inadvertently deregister subscription codes. The new `--move` flag ensures the database's original UUID is retained during a move, maintaining proper database registration. This improves the reliability of server-to-server database transfers.
Original PR description
### What & why `odoo db load` always calls `restore_db(..., copy=True)`, which forces the generation of a new `dbuuid` via `ir.config_parameter.init(force=True)`. That is the right default when…
### What & why `odoo db load` always calls `restore_db(..., copy=True)`, which forces the generation of a new `dbuuid` via `ir.config_parameter.init(force=True)`. That is the right default when *duplicating* a database, but it breaks the intended behaviour when *moving* a database between servers: Enterprise subscription codes are registered against the database UUID, so regenerating it deregisters the moved database. The web database manager already lets the user choose between copying and moving (the `copy` flag of the `/web/database/restore` route), but the CLI exposed no equivalent and forced a copy unconditionally. The CLI is the better tool for server-to-server moves: it isn't subject to reverse-proxy upload/timeout limits and can run unattended or interactively. ### Steps to reproduce the current limitation 1. On server A: `odoo db dump mydb mydb.zip` (Enterprise DB registered to its UUID) 2. On server B: `odoo db load mydb mydb.zip` 3. `database.uuid` has changed → the subscription is deregistered ### Fix Add a `--move` flag to `odoo db load` that maps to `restore_db(copy=False)`, keeping the original UUID. The default remains `copy=True`, so existing behaviour is unchanged. ```sh odoo db load mydb mydb.zip # unchanged: restore as a copy (new UUID) odoo db load --move mydb mydb.zip # new: restore as a move (keep the UUID) ``` ### Backport request This would be greatly appreciated as a backport to 18.0, 17.0, and 16.0 as well. Those are precisely the versions that ship the `odoo db` CLI subcommand, so the fix is applicable to all of them — which is why the backport range is 16.0 → 19.0 and stops at 16.0.
This update corrects a bug that prevented invoices for French public entities in DROM regions (like Martinique) from being correctly processed by Chorus Pro. The system was incorrectly substituting VAT numbers for SIRETs, causing routing failures. This ensures accurate invoice delivery and compliance.
Original PR description
When invoicing a French public entity through Chorus Pro, the SIRET of the recipient was written in the UBL PartyIdentification only when the partner country was France (country_code == 'FR'). Partners located in a DROM (overseas department/region) have a real French SIRET too, but their ISO country code failed the check, so the SIRET was dropped and replaced by the VAT number. This cause the invoice to not be routed correctly in Chorus Pro. Steps to reproduce: - Setup a french company and connect it to Peppol - Create a customer for a public entity located in Martinique, with its SIRET, Peppol address 0009:11000201100044 (Chorus Pro SIRET) and BIS Billing 3.0 format. - Issue and send an invoice to this customer via Peppol. - Open the generated *_ubl_bis3.xml: AccountingCustomerParty PartyIdentification/ID holds the VAT instead of the SIRET, and Chorus Pro never receives the invoice. opw-6153868