Thursday, September 24, 2026
16 changes · saas-19.3
Resolved issues and error corrections
Fixes an issue where a selection dropdown could reopen and remain visible after a user picked an item. This improves reliability for users working with selection fields and prevents related automated checks from timing out.
Original PR description
Before this commit, the autocomplete dropdown of a many2one could open again right after a suggestion was picked, and stay open. The step of mail_template_dynamic_placeholder_tour waiting for it to…
Before this commit, the autocomplete dropdown of a many2one could open again right after a suggestion was picked, and stay open. The step of mail_template_dynamic_placeholder_tour waiting for it to close then times out:
FAILED: [8/36] Tour mail_template_dynamic_placeholder_tour
Step Wait for the drop down to disappear
(trigger: div[name="model_id"] .o-autocomplete:not(:has(.ui-autocomplete)))
TIMEOUT step failed to complete within 10000 ms.
This happens because the search filling the dropdown runs 250ms after the last keystroke, so a suggestion of the previous search can be picked while the next search is still scheduled. Picking a suggestion only closes the dropdown. The scheduled search then runs, opens the dropdown on the selected value, and nothing closes it after that.
This commit fixes the issue by discarding the scheduled search when the dropdown closes.
https://runbot.odoo.com/odoo/error/233574
https://runbot.odoo.com/odoo/error/237744
https://runbot.odoo.com/odoo/error/944454
https://runbot.odoo.com/odoo/error/945517
https://runbot.odoo.com/odoo/error/947141
Forward-Port-Of: odoo/odoo#290317
Forward-Port-Of: odoo/odoo#287888French companies can now select and send multiple invoices without the process crashing. This ensures the batch sending wizard opens as expected, helping users complete electronic invoicing workflows smoothly.
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 Forward-Port-Of: odoo/odoo#289666 Forward-Port-Of: odoo/odoo#289547
This fixes a notification mismatch so tracking-related mail updates are recognized as already handled by browser push notifications. Users should see fewer duplicate alerts when they are away from the Odoo window, making notifications less noisy.
Original PR description
Since #290266 there is a whitelist of message types matching what the server sends via web push, used by the JS `out-of-focus` notifier to skip its own alert and avoid duplicates. `tracking` was added as a server-side push message type in `saas-19.3` (#235719) and should be considered in the JS whitelist as well.
This fixes currency formatting so amounts in currencies without decimal places, such as Japanese yen, no longer lose important zero digits. Business reports and project update summaries will now show correct values like 100 instead of 1, reducing the risk of misleading monetary information.
Original PR description
Description of the issue/feature this PR addresses: In Odoo 19.0, format_amount(..., trailing_zeroes=False) removes significant integer zeroes when the currency has no decimal places, such as JPY.…
Description of the issue/feature this PR addresses:
In Odoo 19.0, format_amount(..., trailing_zeroes=False) removes
significant integer zeroes when the currency has no decimal places,
such as JPY. Project update monetary summaries use this formatter
with trailing_zeroes disabled.
Steps to reproduce in an Odoo shell with the default JPY configuration:
```python
from odoo.tools.misc import format_amount
currency = env.ref('base.JPY')
format_amount(env, 100, currency, trailing_zeroes=False)
format_amount(env, 0, currency, trailing_zeroes=False)
```
Current behavior before PR:
The numeric part of 100 becomes 1, and the numeric part of 0
disappears. Grouped amounts can also lose digits and leave an
incomplete thousands group.
Desired behavior after PR is merged:
Preserve all integer digits for currencies without decimal places.
Only strip trailing zeroes when a fractional part is present.
Regression coverage includes zero, positive, negative and grouped
amounts, both currency symbol positions, and languages with distinct
or identical decimal and thousands separators.
Validation:
- Before the fix: the new regression test fails in 20 subcases.
- After the fix: all 9 tests in TestFormatAmountFunction and
TestFormatLangDate pass on an isolated PostgreSQL 16 database.
- git diff --check passes.
Test selection:
--test-tags=/base:TestFormatAmountFunction,/base:TestFormatLangDate
This PR also includes my signed Individual Contributor License
Agreement in doc/cla/individual/ysnkucuker.md.
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#290374This fix ensures gift card, eWallet, and coupon rewards entered by code remain applied when a Point of Sale order is refreshed or reopened. It prevents cashiers from accidentally charging customers the full amount after a reward had already been used.
Original PR description
Steps to reproduce: - Create a gift card program and a gift card with some balance - Open a PoS session, add a product to a new order and enter the gift card code: a reward line is added and the…
Steps to reproduce: - Create a gift card program and a gift card with some balance - Open a PoS session, add a product to a new order and enter the gift card code: a reward line is added and the total decreases - Refresh the page, or leave the order and take it back from the Orders screen Issue: The reward line disappears and the total goes back to the full price, while the server still holds the pos.order.line with `is_reward_line`, `coupon_id` and `points_cost`. Nothing warns the cashier, so the customer can be charged for a service already paid with the card. Cause: `_code_activated_coupon_ids` is a local field of pos.order: it is neither stored in IndexedDB nor sent to the server, so it is empty once the order is rebuilt on the client. `_updateRewardLines` deletes every reward line and only re-applies the ones whose coupon is found in `_code_activated_coupon_ids` or in `uiState.couponPointChanges`. `updatePrograms` only rebuilds the latter for programs detectable from the order content, so a gift card, eWallet or coupon card activated by code is never recognised again and its reward is dropped by the next `updateRewards` call (TicketScreen.setOrder, barcode scan, partner change, line added...). In 17.0 `codeActivatedCoupons` was exported and restored with the order; the field became local with the 18.0 models rewrite. Fix: Add `_restoreCodeActivatedCoupons` on pos.order and call it from `checkMissingCoupons`, which runs once per rebuilt order (`setup` flags it with `invalidCoupons`) before the programs and the reward lines are refreshed. It re-links every persisted coupon referenced by a reward line that is in neither structure. Nominative cards are excluded so that removing the partner still drops their reward. opw-6568849 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#290257 Forward-Port-Of: odoo/odoo#288102
Hungarian electronic invoice files sent to NAV will no longer include cash rounding as a separate invoice line. This helps keep submitted invoices aligned with Hungarian legal requirements by treating rounding as a settlement difference rather than a sold good or service.
Original PR description
Global cash rounding can be applied to customer invoices. Before this commit, the rounding would be included in the XML file sent to NAV. It would be included as a new invoice line (same as the products lines) and the ATK tax is applied on it. As stated in the legal Hungarian Documentation, an invoice line should always relate to the supply of a good or the service provided. In this case, a cash rounding (which is not a financial advantage or disadvantage) will be considered by the law as a settlement difference, that is not part of the invoice. So, this commit removes cash rounding lines from the NAV XML. Moreover, it uses base_lines for the amounts computation instead of line_ids. task-6527383 Forward-Port-Of: odoo/odoo#286258
This fixes an internal data-model issue where certain related, non-saved many-to-many fields could incorrectly keep a database relationship. The change helps prevent values from being mixed between similar fields, improving data consistency without changing normal user workflows.
Original PR description
When we setup a many2many field which is not stored, it must not have a relation. This is already done for nonrelated fields, but we must do it also for related fields. Otherwise, we may have…
When we setup a many2many field which is not stored, it must not have a relation. This is already done for nonrelated fields, but we must do it also for related fields. Otherwise, we may have side-effects like finding sibling fields in 20.0: in the code below, reading categories would fill roles.
```py
model_id = self.env["ir.model"]._get("test_new_api.foo")
fields = [{
"name": "x_partner_id",
"ttype": "many2one",
"relation": "res.partner",
}, {
"name": "x_role_ids",
"ttype": "many2many",
"relation": "res.partner.category",
}, {
"name": "x_partner_category_ids",
"ttype": "many2many",
"relation": "res.partner.category",
"related": "x_partner_id.category_id",
"store": False,
"readonly": True
}]
for f in fields:
f["model_id"] = model_id.id
self.env["ir.model.fields"].create(fields)
env['test_new_api.foo']._fields['x_partner_category_ids'].relation # should be None
```
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#290286
Forward-Port-Of: odoo/odoo#290067This fix ensures reconciliations without exchange differences use the company currency consistently. It prevents small residual company-currency balances from being left behind when foreign currency is involved, helping accounting records stay accurate.
Original PR description
When doing reconciliation with no exchange difference, the currency used should always be the company currency. Using foreign currency in that case could leave residual amounts in company currency. task-6582723
Message previews in Mail now display the actual text for certain in-app links instead of showing placeholder hash symbols. This makes notifications such as pinned messages and join messages clearer and avoids confusing preview text.
Original PR description
Before this commit, JS-handled links (e.g. pinned messages notification, joined notification) would be formatted as "#" in `htmlToHtmlInline()` (introduced in [1]). This causes message preview for pinned message notification to show as "# #". This commit fixes the issue by specifically handling JS-handled links (recognized by odoo-specific data attributes) and rendering their tet content. [1]: https://github.com/odoo/odoo/pull/238080 task-6571060 Forward-Port-Of: odoo/odoo#290292
Users who receive mentions in Odoo will no longer get two separate alerts for the same message when browser notifications are enabled. This reduces confusion and keeps notification behavior consistent when a tab is open but not in focus.
Original PR description
Steps to reproduce: - Grant browser notification permission (subscribes to web push). - Set user A's `notification_type` to `inbox` in preferences and reload. - Keep A's tab open but unfocused. - As user B, post an @-mention on a non-channel chatter record targeting A. A receives two alerts: the JS one from the `mail.message/inbox` bus event and the native notification from web push. `OutOfFocusService.notify()` gated JS skip on `message.thread.model`, but the server never sends `mail.thread`, so chatter fell through. Replace it with a `message_type` + `!isSelfAuthored` filter mirroring the server side `_notify_get_recipients_for_extra_notifications`. Also swap the `isInbox` SW handshake workaround added by [1] when the user is busy. [1]: https://github.com/odoo/odoo/pull/232431 Forward-Port-Of: odoo/odoo#290052 Forward-Port-Of: odoo/odoo#282981
Time off requests for types that require supporting documents can no longer be saved or moved forward without the required attachment. This helps ensure absence records are complete and compliant with company policy before they remain active in the system.
Original PR description
Problem: When a time off type is configured to require a supporting document, the system should prevent users from submitting a request without one. However, users could bypass this requirement by…
Problem: When a time off type is configured to require a supporting document, the system should prevent users from submitting a request without one. However, users could bypass this requirement by creating and saving a request without uploading a file. Because the validation was not strictly enforced during creation or subsequent write operations, requests could enter or remain in active states without the mandatory documentation. Solution: This commit ensures the system enforces mandatory attachments during the modification of time off requests that require a supporting document. The system now evaluates state transitions to block undocumented submissions. Steps to reproduce(runbot v19): 1. Go to Time Off > Configuration > Time Off Types, create a new leave type and enable the "Allow To Attach Supporting Document" setting. 2. Create a new time off request for this type, leave the attachment empty, and save. 3. Notice that the system allows the invalid request to be saved and persist in the database without a document. opw-6413621 Forward-Port-Of: odoo/odoo#289577 Forward-Port-Of: odoo/odoo#278991
This fix ensures that when a user is not allowed to subscribe to one update channel, the system skips only that channel instead of failing the entire subscription request. This helps keep unrelated real-time updates working reliably for users.
Original PR description
`_build_bus_channel_list` should never raise. Otherwise, the entire request is aborted, meaning the client fails to subscribe to *all* channels, including completely unrelated channels. Instead, if a client do not have permission to subscribe to a channel, the channel should just be skipped. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287142
Payment reconciliation now uses the company currency when any reconciled item belongs to an account that does not support payment reconciliation. This prevents incorrect foreign exchange difference entries and keeps accounting records more accurate.
Original PR description
When reconciling journal items, if one of them is using an account with no payment reconciliation, then the currency of the reconciliation should be the company currency regardless of which currency was used on the items. Also, in that case, no exchange difference entry should be created. task-6582723
Opening a signing template after it has been sent no longer triggers an access error when the template has no fields. This prevents users from being blocked when viewing previously sent templates in the Sign app.
Original PR description
Version: 19.0 Steps to reproduce: - Create a sign template with no sign fields - Send a sign request using that template - Open that template from Templates Issue: After a sign request is sent, record rules make the template read only for sign items (create/write only when there are no sign requests). On open, if the template has no signers, the UI auto creates a dummy signer. For an empty sent template, that create is denied by those rules and raises an Access Error. Fix: Guard the auto create signer flow with a sign requests check so that a sent template does not try to create a new signer on open. Task: 6591146 Forward-Port-Of: odoo/enterprise#132516
When Colombian localization users update contact data in a multi-company setup, any new related contact now automatically uses the same company as the original contact. This prevents missing company assignments and keeps customer records consistent across companies.
Original PR description
Problem: In l10n_co, when updating a contact's data using the "Update data" feature in a multi-company setup, the company of the newly created child contact isn't set. Solution: Set the child contact's company to match the parent contact's company when creating the record, ensuring both contacts belong to the same company. Steps to reproduce (runbot v18): 1. Install l10n_co 2. Set up two companies (Company A and Company B). 3. Create a contact with an email and NIT, and set its company to Company B. 4. Click the "Update data" button. 5. Open the newly created child contact and check its company. The company of the newly created child contact is not set. opw-6528295 Forward-Port-Of: odoo/enterprise#131049
The Luxembourg reports module now uses a working download link for the FAIA XSD file. This prevents failed or empty downloads when users need the required reporting schema file.
Original PR description
The old link points to a file with zero bytes. opw-6344914 Forward-Port-Of: odoo/enterprise#132582