Monday, September 28, 2026
15 changes · saas-19.1
Security fixes and vulnerability patches
AI-powered document sorting no longer shows disruptive on-screen errors when API credentials are missing, while still recording the issue on the document for follow-up. The update also prevents invalid AI API key details from appearing in user-facing messages, reducing the risk of exposing sensitive information.
Original PR description
Prior to this PR, any users who have enabled the ai_document sort without any API key enable will have a error notification when adding the document in a folder where ai_sort is enabled. We'd like to remove this notification if the API key is not set in the configuration, and instead have the error log in the chatter of the document only. How to reproduce: - Disable AI Keys from the environment - On any record, add an attachment (ex: in HR app) - Click on the "Add to Documents" - Move it to the Inbox folder (where ai_sort is enabled) Current behavior: - A notification error is shown on top right for each time it attempts to sort the document - The error message is stored in the chatter of the document. (keep this behavior) Expected behavior: - If no credits (API key) is set, no error should be shown on screen. - The error message is stored in the chatter of the document. (keep this behavior) task: 5499622 Forward-Port-Of: odoo/enterprise#125521
Resolved issues and error corrections
This update removes an unnecessary default value from an internal test method parameter. It aligns the code with best practices, reducing the chance of confusing behavior or future maintenance issues without changing user-facing functionality.
Original PR description
[RUF077](https://docs.astral.sh/ruff/rules/method-receiver-default/#method-receiver-default-ruf077) specifies that method receiver parameters, such as `self` and `cls`, should not have default values. The reasoning is because these parameters are usually bound by the method binding protocol, so a default value on a receiver parameter is almost certainly a mistake and can lead to confusing behavior or runtime errors. This method seems to not reference self, and this should not cause errors, but it is best practice to not do this. runbot-[947144](https://runbot.odoo.com/odoo/error/947144) Forward-Port-Of: odoo/odoo#290616
This fix prevents users from opening right-click message actions in the chatter while a new record is still being created. It avoids failed actions such as pinning or copying message links before the related record exists.
Original PR description
**Steps to reproduce:** - Go to Contact app - Create a new record but don't save it - "Creating a new record..." appears in the chatter - Righ-Clicking shows the popup action menu - Trying to pin or create the message link will fail **Issue:** Chatter message actions should be blocked when the record is not created yet. This is not the case for the new right-click popup (see [1]) but it was working for the default `mail.Message.actions`: ```xml t-if="props.hasActions and message.hasActions and !isEditing and !env.inChatter?.disabled" ``` **Fix:** Add the missing `!env.inChatter?.disabled` to `onContextMenu` check before trying to open the message actions (like in [2]). [1] https://github.com/odoo/odoo/commit/ddf2f1711b02456f37d27b17cfc9dc6590b348b5 [2] https://github.com/odoo/odoo/commit/caaa1b97da6a459ecf4a3032110288a6ddf069a1 opw-6592792
This fixes the loyalty program setup so the minimum purchase field is fully hidden for Buy X Get Y promotions. It prevents users from configuring a minimum purchase condition that should not apply to this type of reward, reducing confusion and setup errors.
Original PR description
I think the point was to hide the entire `Minimum Purchase` section when the program type is `buy_x_get_y`, but only the label is hidden. This commit will also hide the div under the label, so no minimum purchase can be set on `buy_x_get_y` programs --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288281
This fixes a testing issue in the Bulgarian SAF-T module that caused broad failures when an optional bank account validation component was absent. The change isolates the affected IBAN scenario so normal report tests remain reliable, with no expected impact on real customer use.
Original PR description
Fixes an issue that make most tests fail when the base_iban module is not installed. The BG SAF-T report requires additional information for non-iban bank accounts and thus raises errors when they are not present. If the base_iban module is not installed, the iban verification never happens and an iban account can never be flagged as such. As there is insufficient information on that iban account, it raises validation errors in most other tests. The module is automatically installed when a Bulgarian fiscal localisation is added so the problem will not occur in a real situation. It only affects the tests. To fix the issue in stable, we cannot add the module to the dependencies. The workaround to keep most of the testing logic intact is to isolate the scenario with iban accounts in its own test and skip it if the 'base_iban' module is not installed. runbot-945995 Forward-Port-Of: odoo/enterprise#132587
Fixed a visual issue in Sales quotations where optional product tables could appear lighter than their surrounding area when using dark mode. This keeps the quotation dialog consistent and easier to read for users working with optional products.
Original PR description
**Steps to reproduce:** - Set preferences to dark mode - Create a quotation and add a product with optional products (e.g customizable desk) -> Background of optional products is lighter than the…
**Steps to reproduce:** - Set preferences to dark mode - Create a quotation and add a product with optional products (e.g customizable desk) -> Background of optional products is lighter than the surrounding div **Behavior:** The `div` showcasing optional products is set to be a bit darker to add contrast in the modal. In light mode, table's background is transparent, which allows it to adapt to a darker container. However, this is not the case in dark mode, which causes a mismatch between table and div backgrounds. By default `$table-bg` follows `$body-bg`: https://github.com/odoo/odoo/blob/3442814d57cd9420d7dc6d022d15cd5d1b3def74/addons/web/static/lib/bootstrap/scss/_variables.scss#L738-L740 And is later set to transparent here: https://github.com/odoo/odoo/blob/3442814d57cd9420d7dc6d022d15cd5d1b3def74/addons/web/static/src/scss/bootstrap_overridden_frontend.scss#L74-L75 However when darkmode is enabled, `$body-bg` is re-assigned, which forces `$table-bg` back to the default dark color, bypassing the transparent override. https://github.com/odoo/odoo/blob/3442814d57cd9420d7dc6d022d15cd5d1b3def74/addons/web/static/lib/bootstrap/scss/_root.scss#L132-L139 --- This commit ensures that `table-bg` is explicitly set to transparent for tables displaying optional products. opw-6569713 Forward-Port-Of: odoo/odoo#288127
Italian invoices using the Import/Export fiscal position now apply the correct single 0% tax instead of adding two 0% taxes at the same time. This prevents incorrect tax setup on invoice lines and helps keep Italian accounting and e-invoicing data accurate.
Original PR description
Upon Import/Export, the 0% EX N7 tax is added by default on each tax excluded line, as it incorrectly shares the same default Import/Export fiscal position with the standard 0% EX tax. This causes both to be applied simultaneously to a single invoice line. 1. Install Accounting and `l10n_it` 2. Switch to IT company 3. Go to Invoices and create a new one 4. Select the Import/Export fiscal position 5. Add a line with a new product (so it's clean of custom product taxes) 6. Both `0% EX` and `0% EX N7` taxes are applied, instead of just `0% EX` Ticket [link](https://www.odoo.com/odoo/project.task/6518926) opw-6518926 Forward-Port-Of: odoo/odoo#285586
KPI reports now ignore archived returns, so hidden or inactive returns no longer appear in business summaries. This keeps reporting dashboards accurate without forcing users to mark returns as completed just to remove them from view.
Original PR description
[FIX] account_reports: KPI provider: properly ignore inactive returns Returns can be archived (setting the 'active' field to False) from the UI, so that the user can hide them when not needed,…
[FIX] account_reports: KPI provider: properly ignore inactive returns Returns can be archived (setting the 'active' field to False) from the UI, so that the user can hide them when not needed, without risking them being regenerated by the cron, and without havin to mark them as completed, which could give misleading information when browsing the history. The problem was here the KPI provider did not properly ignore such returns. ======================================== [FIX] account_reports: make KPI provider test with existing data in db The KPI provider query completely ignores multi-company. Because of that, the test wasn't properly sandboxed, and could fail if any non-test company of the database contained a return made from any untested return type, as it would add one element to the result of get_account_reports_kpi_summary. We now make sure to archive all existing returns, whatever their company, before running the test. Forward-Port-Of: odoo/enterprise#131438
Users can now download all files from a chatter message as a ZIP even when some attachments are stored in cloud storage. This prevents failed downloads and keeps the bulk file download action working consistently across local and cloud-hosted attachments.
Original PR description
Clicking "Download Files" on a chatter message fails when one of its attachments is stored in the cloud. Cloud attachments normally return a stream containing a signed URL. For individual downloads, Odoo redirects the browser to that URL, letting the cloud provider serve the file directly. The ZIP controller instead needs the file's bytes to build the archive on the server. It calls `Stream.read()`, which cannot read URL streams and raises `ValueError: Cannot read an URL`. Enable a `cloud_storage_force_download` context flag on the attachments when the cloud controller delegates ZIP creation to the mail controller. With this flag, the cloud attachment model fetches the file through the provider's signed URL and returns a data stream that the existing ZIP controller can read. opw-6570065 Forward-Port-Of: odoo/odoo#289819
Kenyan eTIMS submissions now exclude taxes that do not have a KRA tax code, such as levies paid to other authorities. This prevents over-reporting tax totals to the Kenya Revenue Authority and keeps invoice reporting aligned with the correct tax destination.
Original PR description
Issue: Non KRA taxes are sent in eTIMS tax total Steps to reproduce: - With l10n_ke_edi_oscu - Create a 2% Tax named CTL with no KRA Tax Code - Create an invoice - Add a line with 16% and CTL taxes - Confirm - Send to eTIMS Current behavior: - eTIMS JSON (unaccessible) is created with 'totAmt' (total_tax_included) including non-KRA Taxes Expected Behavior! - eTIMS JSON 'totAmt' doesn't incldue non-KRA Taxes Note: eTIMS is used to report to Tax Autority, however some "taxes" (e.g. Tourism levy) are paid to other authorities (e.g. Tourism Fund) and shouldn't be included in eTIMS. opw-6456478 Forward-Port-Of: odoo/enterprise#131577
This fixes formulas used in the Swiss balance sheet report so the report calculates figures correctly. It helps Swiss companies rely on more accurate financial statements for review and compliance purposes.
Original PR description
Change some formulas in the Swiss balance sheet task-6379692 Forward-Port-Of: odoo/enterprise#123895
Dropship operations that only create lots no longer show the misleading "Pick From" option. This ensures lot names and expiration dates entered by users are applied correctly, avoiding silent mistakes in delivered product traceability.
Original PR description
Steps to reproduce 1. In Inventory Settings, enable Lots & Serial Numbers and Expiration Dates. 2. Create a storable product tracked by lot with Expiration Date enabled. 3. Create and confirm a…
Steps to reproduce 1. In Inventory Settings, enable Lots & Serial Numbers and Expiration Dates. 2. Create a storable product tracked by lot with Expiration Date enabled. 3. Create and confirm a dropship of it, then open the move's Detailed Operations. 4. The pre-filled line works: typing a batch and an expiration date creates exactly that lot at validation. 5. To split the quantity, click "Add a line": it opens the "Pick From" popup, and choosing "Create" there creates the lot and attaches it to the line. 6. On that line, edit "Lot/Serial Number" and "Expiration Date", then Validate. -> Expected: the values entered on the line are used. -> Actual: they are ignored; the lot created through "Pick From" is delivered with its own expiration date, the name and date typed on the line have no effect. Issue --- On a create-lots-only non-incoming operation like `Dropship`, `show_quant` is derived from `picking_code` alone, so the "Pick From" quant picker is shown even though the operation only creates lots. Adding a line goes through "Pick From", which creates the lot and attaches its `lot_id` to the move line; from then on the line's `lot_name` and `expiration_date` stay editable but do nothing, since a set `lot_id` is used as-is at validation and the `expiration_date` is recomputed from the lot, so anything typed there is silently dropped. Such an operation has no existing stock to pick from, so `show_quant` is now gated on the lot settings too: "Pick From" is hidden and the line's `lot_name` and `expiration_date` become the only inputs, created into the lot at validation like receipts already do. https://github.com/odoo/odoo/blob/68dcb950df83d70ff2aea0e05c96cc9b57c1a8a9/addons/stock/models/stock_move.py#L646-L647 opw-6530563 Forward-Port-Of: odoo/odoo#290071 Forward-Port-Of: odoo/odoo#287474
The Bulgarian SAF-T reporting feature will no longer install automatically unless the required advanced accounting setup is present. This prevents the feature from appearing in environments that may not be ready to use it, reducing confusion and setup issues.
Original PR description
As the Bulgarian SAF-T Report is destined for advanced accounting, it should only be available and auto-installed when the 'accountant' module is as well. As the 'accountant' module is not part of the dependencies yet (it is from 20.0 on), we cannot be sure that it is already installed. It is then better to stop the auto installation altogether. Forward-Port-Of: odoo/enterprise#132761
This update removes unnecessary logic from the accounting reports download process. It reduces ambiguity in the code path, helping keep report downloads reliable without changing the expected user experience.
Original PR description
`!something` is never nullish, so `?? true` is dead code. Probably the intention was `if (!(data.no_closing_after_download ?? true))`, but reviewer has the final say and I can change it. Forward-Port-Of: odoo/enterprise#131774
Demo data for Belgian payroll testing is now created using the correct Belgian company context. This prevents errors when loading the demo data through the interface, making setup and testing more reliable.
Original PR description
Before the fix, loading the module's demo data through the UI triggered an error. task-6581013 Forward-Port-Of: odoo/enterprise#132632 Forward-Port-Of: odoo/enterprise#132358