Monday, September 21, 2026
15 changes · 18.0
Enhancements to existing features
Users now receive a persistent notification when the IAP service is down. This makes service interruptions clearer and helps users understand why related features may not be working.
Original PR description
We wanted to let the user know when the IAP server is down with a sticky notification. Task-6570098
Resolved issues and error corrections
When adding an attribute to a product, Odoo will no longer automatically add every possible value for that attribute. It will only prefill a value when there is exactly one option, preventing unwanted bulk selections and reducing manual cleanup for users.
Original PR description
Description of the issue/feature this PR addresses: - Create a attribute A with 100 values (for exemple list of certification) - Create a new product, add this attribute --> Issue all 100 values (100) is auto added. This commit limit the auto complete, the autocomplete works if there are only one value. 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
This fix prevents Peruvian PLE sales and purchase ledgers from counting selective consumption tax twice when it affects later taxes. It keeps ledger taxable base amounts aligned with electronic invoices sent to SUNAT, improving tax reporting accuracy for affected transactions.
Original PR description
When a tax affects the base of the subsequent ones — `include_base_amount`, which is how the ISC is set up in Peru — Odoo links the tax line it generates to those subsequent taxes, so the ISC tax…
When a tax affects the base of the subsequent ones — `include_base_amount`, which is how the ISC is set up in Peru — Odoo links the tax line it generates to those subsequent taxes, so the ISC tax line ends up carrying the IGV in its `tax_ids`. The PLE ledgers build their base columns by summing every move line related to a given tax group, so that ISC tax line was added to `base_igv` as well and the ISC ended up reported twice: once inside the taxable base and once in its own column. A line of 1000.00 with 100.00 of ISC and 198.00 of IGV was declared with a base of 1100.00 instead of 1000.00. `l10n_pe_edi` already subtracts the ISC from the `cbc:TaxableAmount` of the CPE sent to SUNAT, so the electronic invoice and the ledger disagreed on the very same document. ### What changed The base taxes are now joined only for base lines, instead of repeating the condition on each of the twelve `base_*` columns. Besides keeping tax lines out of the base columns, this also stops a tax line from being counted once per subsequent tax: an ISC followed by both the IGV and the ICBPER produced two rows in the join, and its own column was reported twice. ### Scope `_get_ple_report_data` is shared, so this covers the sales ledger 14.1 and the purchase ledgers 8.1 and 8.2. A configuration where no tax affects the base of a later one is unaffected: the standard Peruvian chart template has no purchase ISC, so ordinary IGV-only purchases report exactly the same values as before. ### Tests - `test_sale_report_isc_base`: an ISC preceding the IGV, on an invoice and on a credit note; the base column excludes the ISC. - `test_sale_report_isc_reported_once`: an ISC followed by the IGV and the ICBPER is reported once. Both fail without the fix. Supersedes odoo/enterprise#128300, which targeted 19.0. Forward-Port-Of: odoo/enterprise#131475
This fixes credit notes using Spain's TicketBAI/Batuz reporting when an equivalence surcharge tax is applied. The surcharge percentage is now sent as a positive value, preventing tax agency rejections and allowing affected refunds to be submitted successfully.
Original PR description
In credit notes, `TipoRecargoEquivalencia` was multiplied by the reversal sign, producing negative values (e.g. -1.40) that violate the Batuz `Tipo3.2Type` pattern and get rejected by the tax agency (`B4_2000001: cvc-pattern-valid`). Unlike `BaseImponible`/`CuotaImpuesto` (amounts that must be negative), `TipoRecargoEquivalencia` is a percentage and must stay positive, as `TipoImpositivo` does. Steps to reproduce: 1. Configure a Spanish company with TicketBAI (Bizkaia). 2. Create a credit note (out_refund) with an equivalence surcharge tax. 3. Send it to TicketBAI; the send fails with a schema validation error. Task: MT-15939 OPW: https://www.odoo.com/es_ES/my/tasks/6573801 @jco-odoo could you review? It's essential to be able to send to Tbai/Batuz with equivalence surcharge tax.
This fix prevents invoice confirmation from crashing when an Ecuadorian company has a VAT value that is not a valid RUC number. It improves reliability for Ecuador electronic invoicing by safely handling incorrect company identification data instead of showing an error.
Original PR description
When the VAT of an Ecuadorian company's partner contains characters other than decimal digits, confirming an invoice raises a traceback. Steps to reproduce the error: - Install ``l10n_ec_edi`` module…
When the VAT of an Ecuadorian company's partner contains characters other than decimal digits, confirming an invoice raises a traceback. Steps to reproduce the error: - Install ``l10n_ec_edi`` module and switch to EC Company - Open the partner of EC Company > Set a VAT containing non-digit characters - Create a new invoice > fill the required fields > Confirm Traceback: ```py ValueError: invalid literal for int() with base 10: 'E' ``` During invoice confirmation, ``_l10n_ec_set_authorization_number()`` method is called to generate the authorization number at [1], It builds the ``key_value`` using the vat of company's partner at [2]. The generated ``key_value`` is then passed to ``_l10n_ec_get_check_digit`` method, which converts each character of ``key_value`` to an integer using ``int()`` at below line. https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L830 When the VAT contains a character that cannot be converted to an integer, It will raises the above traceback. To build the ``key_value``, the company's partner vat should be RUC consisting of 13 digits. Ref: https://www.sri.gob.ec/o/sri-portlet-biblioteca-alfresco-internet/descargar/fb95cafc-a8ca-4a4c-afb6-12c4153165f0/FICHA%2520TECNICA%2520COMPROBANTES%2520ELECTR%25C3%2593NICOS%2520ESQUEMA%2520OFFLINE.pdf Solution: If the identification type of the company's partner is not RUC, return an empty string. [1]: https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L806 [2]: https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L825-L826 sentry-7642961403 Related Community PR: https://github.com/odoo/odoo/pull/280047
Ecuadorian company VAT numbers are now checked earlier to ensure they contain only digits. This prevents invoice confirmation from failing with an unexpected system error and gives users a clear validation message instead.
Original PR description
When the VAT of an Ecuadorian company's partner contains characters other than decimal digits, confirming an invoice raises a traceback. Steps to reproduce the error: - Install ``l10n_ec_edi`` module…
When the VAT of an Ecuadorian company's partner contains characters other than decimal digits, confirming an invoice raises a traceback. Steps to reproduce the error: - Install ``l10n_ec_edi`` module and switch to EC Company - Open the partner of EC Company > Set a VAT containing non-digit characters - Create a new invoice > fill the required fields > Confirm Traceback: ```py ValueError: invalid literal for int() with base 10: 'E' ``` During invoice confirmation, ``_l10n_ec_set_authorization_number()`` method is called to generate the authorization number at [1], It builds the ``key_value`` using the vat of company's partner at [2]. The generated ``key_value`` is then passed to ``_l10n_ec_get_check_digit`` method, which converts each character of ``key_value`` to an integer using ``int()`` at below line. https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L830 When the VAT contains a character that cannot be converted to an integer, It will raises the above traceback. [1]: https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L806 [2]: https://github.com/odoo/enterprise/blob/799e9e94efcc338a8f3a6b7ab85cf77f9898f005/l10n_ec_edi/models/account_move.py#L825-L826 Solution: Raise the ValidationError when the vat contains non-digit character. sentry-7642961403 Related Enterprise PR: https://github.com/odoo/enterprise/pull/126519 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Fixes an issue where closing a website visitor live chat request could show an error instead of closing normally. This improves reliability for website operators handling live chat conversations.
Original PR description
Problem: Closing the chat window of a chat request sent to a website visitor crashes with `TypeError: Cannot read properties of undefined (reading '_proxy')`. Cause: When a record is deleted,…
Problem:
Closing the chat window of a chat request sent to a website visitor crashes
with `TypeError: Cannot read properties of undefined (reading '_proxy')`.
Cause:
When a record is deleted, `MAKE_UPDATE` clears it from the relations holding
it. Holders still alive come from `recordByLocalId` as proxies, holders already
deleted in the same update come from `deletingRecordsByLocalId` as raw records.
On a raw record, `usingRecord[one] = undefined` overwrites the `RecordList`
instead of emptying it, and the next read of that field crashes.
Regression from [7432827d09e6](https://github.com/odoo/odoo/commit/7432827d09e667bb1e2670c7bb719fe24edec59d).
Solution:
Backport of [7b457e243bdf](https://github.com/odoo/odoo/commit/7b457e243bdfcdd4c4b61b4771643c1985bde9cf)
from 19.0: always work on the raw record and remove the deleted record from
its `RecordList`, for `one` and `many` fields alike.
Steps to reproduce:
Set a Livechat channel on the website and add yourself as an operator.
Website > Reporting > Visitors > click `Chat` on a visitor.
Close the chat window. => Error dialog with the `TypeError` above.
opw-6564629This fix ensures Factur-X invoice files include the required customer name even when an invoicing address has no name set. It falls back to the related commercial partner name, reducing validation failures and helping businesses send compliant electronic invoices.
Original PR description
Description of the issue/feature this PR addresses: The Factur-X Refactoring (https://github.com/odoo/odoo/pull/284123) introduced an error that can result in the generated XMLs not being compliant due to the partner name being missing (BT-44) Current behavior before PR: When a contact has an invoicing address without a name set, then the new Factur-X generation does not fall back to the parent name, leaving the corresponding XML entry empty, which is therefore pruned, leaving the Factur-X without a BT-44 and thus failing BR-07 Desired behavior after PR is merged: The new generation method uses the same data source and fallback method as the old Factur-X generator, pulling the `display_name` of the `commercial_partner_id` if the address doesn't have a `name`. This greatly reduces the chance of a generated Factur-X failing BR-07. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Vietnam Profit and Loss reporting is updated to match Circular 99/2025/TT-BTC, including the new investment property gain/loss line and revised numbering for the official printed form. Revenue, cost, and other income calculations are adjusted to avoid double counting and better align with the chart of accounts, while period comparisons remain available without an unsupported growth percentage column.
Original PR description
Circular 99/2025/TT-BTC (effective FY2026) replaces Circular 200/2014/TT-BTC. The Profit & Loss statement (Mẫu B02-DN) gains a new mandatory line, "Lãi/lỗ của hoạt động bán, thanh lý bất động sản đầu…
Circular 99/2025/TT-BTC (effective FY2026) replaces Circular 200/2014/TT-BTC. The Profit & Loss statement (Mẫu B02-DN) gains a new mandatory line, "Lãi/lỗ của hoạt động bán, thanh lý bất động sản đầu tư" (mã số 21), computed from the new 5117/6327 sub-accounts (see the paired l10n_vn commit). Financial income, financial expenses and the interest memo line shift to mã số 22/23/24 accordingly, the "Net profit from operating activities" formula is updated to include the new line, and all following line numbers/labels are renumbered to match the printed form. The interest memo line itself is renamed from "Chi phí lãi vay" to "Chi phí đi vay" (Borrowing costs), per the gazette text. Also, per revised guidance: - Revenue and cost of goods sold now exclude the new investment property sub-accounts (5117/6327), so those amounts aren't double-counted between the main lines and the new gain/loss line. - Other income is now computed from the "income_other" account type instead of a hardcoded account code, matching how the chart of accounts already classifies it. Both the Balance Sheet and Profit and Loss VN reports also declare an extra "Code" string column (for the printed-form mã số) alongside "Balance". Core's growth-comparison heuristic only turns on the auto growth-% column when there are exactly 2 total columns; with Code+Balance that becomes 4 once a comparison period is added, so the % never renders even though the underlying side-by-side comparison values are fine. account.report exposes filter_growth_comparison as a toggle independent from filter_period_comparison for exactly this case (see Trial Balance and the Deferred reports), so both reports set it to False: period comparison stays available, only the auto growth-% column is suppressed. Task-6518304
Default invoice terms and conditions pages can now keep interactive accordion blocks created in the website editor. This prevents the content from breaking after saving and avoids errors when users add new accordion items.
Original PR description
# How to reproduce - Go to Settings > Default Terms & Conditions > Preview - Open the editor - Add a Content Block - Add an Accordion in that Block - Save # The issue The accordion gets messed up and…
# How to reproduce - Go to Settings > Default Terms & Conditions > Preview - Open the editor - Add a Content Block - Add an Accordion in that Block - Save # The issue The accordion gets messed up and trying to add a new item to that accordion raises a traceback # Cause `invoice_terms_html` is an html field with some sanitization enabled : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/account/models/company.py#L172 This sanitization will remove the accordion snippet's buttons that controls the functionality of the snippet : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/website/views/snippets/s_accordion.xml#L9 This issue was already addressed by : https://github.com/odoo/odoo/commit/b6b4db5fb5690436a4284f6a22abf9f3b346a324 But it is not enough in the case of the accordion. The buttons will be removed by the lxml clean.Cleaner : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/odoo/tools/mail.py#L364 # Proposed Solution Disable sanitization entirely, like for blog's content, which can also be edited in the website editor : https://github.com/odoo/odoo/blob/c11ca6d10cac99b367078823dafef46808346267/addons/website_blog/models/website_blog.py#L29 opw-6500378
Fixed an error that prevented French companies using electronic invoicing from sending multiple invoices at once. Users can now select several invoices and open the batch sending wizard without the process crashing.
Original PR description
Sending several invoices at once from the list view of a French company crashes instead of opening the batch sending wizard. Steps to reproduce: - Activate French Electronic Invoicing - Create a French customer with a valid PDP endpoint - Post two invoices for that customer, select them in the invoice list and click 'Send' Issue: The batch sending wizard raises ``` AttributeError: 'account.move.send.batch.wizard' object has no attribute 'company_id' ``` opw-6558940
Sorting contacts by name now gives the expected order when users work in a language other than English. The contact list now prioritizes reliable sorting, with a temporary trade-off that names may not display in the selected language in this view.
Original PR description
Problem: When sorting by name in the contact list view when a different language is selected, the records were not sorted correctly. Steps to reproduce: 1. Select another language than English in the…
Problem: When sorting by name in the contact list view when a different language is selected, the records were not sorted correctly. Steps to reproduce: 1. Select another language than English in the user preferences. 2. Go to Contacts app. 3. Click on the "Name" column to sort by name. 4. Observe that the records are not sorted correctly. Cause: The field used for name was "display_name" which is a computed field that is not stored in the database. The field was used so that the name is displayed in the correct language, but it cannot be used for sorting. When used for sorting, the database will use the value of the field in the default language, and will show the records in the selected language, which will result in incorrect sorting. Solution: Use the field "complete_name" instead of "display_name" for showing the name in the list view. This will enable sorting by name correctly, but will not show the name in the correct language. As discussed in the task, this is an acceptable compromise for now. Reverts odoo/odoo#257539 opw-6475119
This fixes an error that could interrupt the website configurator when a user chooses to add an online store. The setup now handles the website address correctly, allowing the configuration and related demo data loading to complete without a traceback.
Original PR description
Since [1], the registry is loaded without a request context, so a module installation now goes through the fallback of `get_current_website` that reads the url stored on the current thread. That…
Since [1], the registry is loaded without a request context, so a module installation now goes through the fallback of `get_current_website` that reads the url stored on the current thread. That value is a full url, not the `domain:port` the method expects, so IDNA-encoding it splits it on the dots of the path rather than on those of a host name and raises `UnicodeError` as soon as one of the resulting parts reaches 64 characters. The thread url is now reduced to its `domain:port` part. Steps to reproduce: - Start the server locally on a database with demo data, served on `http://localhost:8069`, with only `Website` installed - Create a new website - In the configurator, choose `I want an online store` - Go through each step - After the last step, a traceback appears while loading the accounting demo data: the configurator should apply without error [1]: https://github.com/odoo/odoo/commit/1ae80f9fb18c9c9e67fe0367b94f0d882c711570 Also reported here: https://github.com/odoo/odoo/issues/286432
The Attendances dashboard now shows unavailable days based on the working schedule that applied during each employee contract period. This prevents misleading greyed-out days when an employee's schedule changed over time, helping managers review attendance history more accurately.
Original PR description
Issue: ---------------------------------------- On the Attendances dashboard view, days marked unavailable (greyed out) for an employee don't match the working schedule that was actually in effect…
Issue: ---------------------------------------- On the Attendances dashboard view, days marked unavailable (greyed out) for an employee don't match the working schedule that was actually in effect during each displayed period. Steps to reproduce: ---------------------------------------- - Create two working schedules with different days off (e.g. "Monday off" and "Friday off") - Give an employee a contract on the first schedule, close it, then start a new contract on the second schedule. - Open the Attendances dashboard for a period spanning both contracts - The greyed off day is always the one from the current schedule Cause: ---------------------------------------- `_gantt_unavailability()` computes unavailable intervals via `_get_unavailable_intervals()`, which only looks at the resource's current calendar for the whole requested range, ignoring the different calendars that applied during earlier contract periods. Solution: ---------------------------------------- `calendar_periods_by_employee` already contains the periods with their corresponding calendar. We use it to call `_unavailable_intervals_batch()` on each calendar with their interval and combine the results. opw-6460004
Validated time off requests now create all-day calendar events with a duration that matches the actual date range. This prevents events from being shortened incorrectly when their start time changes, such as during Google Calendar synchronization.
Original PR description
**Problem:** When modifying the start time of an all-day Calendar Event created from the Validation of a Time Off Request, the end time will be shortened dramatically, no longer spanning the correct…
**Problem:** When modifying the start time of an all-day Calendar Event created from the Validation of a Time Off Request, the end time will be shortened dramatically, no longer spanning the correct timespan. In the case of the ticket referred to below, this occurs during Google Calendar sync. **Cause:** When a Calendar Event is created from a Time Off Request being Validated, its duration is set according to the amount of days the Time Off Request spans multiplied by the employee's daily work hours as defined on their contract, even if the Calendar Event will be an all-day event. When the start time of a Calendar Event is modified, the end time is recomputed according to the duration added to the new start time. As such, if the duration of the Event does not match the duration of the dates covered, it will be shortened dramatically. **Purpose:** Modify the values defined when creating a Calendar Event as a result of a Validated Time Off Request so that, if the created Event will be an all day Event, the duration is computed according to `calendar.event._get_duration`. **Steps to Reproduce in Runbot:** 1. Create and Validate a Time Off Request of the Paid Time Off leave type spanning multiple days. 2. Modify the start time of the Calendar Event that is created as a result of the validation. (This step will likely require a nonstandard flow as the start time field is normally hidden for all-day Calendar Events) opw-6426586 Forward-Port-Of: odoo/odoo#284552