Thursday, September 24, 2026
12 changes · saas-19.2
Resolved issues and error corrections
Odoo now correctly displays amounts in currencies without decimal places, such as Japanese yen, when optional trailing zeroes are hidden. This prevents values like 100 or 0 from being shown incorrectly in monetary summaries, improving accuracy and trust in displayed financial 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 update fixes several issues in Odoo's internal web testing tools and related test data. It helps teams catch problems more accurately before release, reducing the risk of regressions in areas like messaging, live chat, calendar, accounting, and point of sale.
Original PR description
- https://github.com/odoo/enterprise/pull/131914 Various Hoot/web tests fixes. See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#289788 Forward-Port-Of: odoo/odoo#256814
Point of Sale orders now keep gift card, eWallet, and coupon rewards applied after the cashier refreshes the page or reopens an order. This prevents customers from being accidentally charged the full price when part of the order was already covered by a code-based reward.
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
Fixed an error that prevented users at French companies from sending multiple invoices at once when French Electronic Invoicing was enabled. The batch sending wizard now opens as expected, reducing disruption for accounting teams processing invoices in bulk.
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
The editor now skips channels a user is not allowed to subscribe to instead of stopping the whole subscription request. This prevents unrelated real-time updates from failing when one channel cannot be accessed.
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
This fixes an issue where autocomplete suggestions could reopen after a user selected an item, causing the dropdown to stay visible. The change makes forms and related workflows more reliable by preventing delays and failed automated checks tied to stuck dropdowns.
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#287888This update prevents temporary related many-to-many fields from keeping an unnecessary database relationship. It avoids cases where reading one field could accidentally affect another similar field, improving data consistency behind the scenes.
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 update corrects internal test data definitions so automated checks better match real Odoo behavior. It helps teams catch configuration and data issues earlier, reducing the risk of hidden problems in features such as appointments, documents, knowledge, barcode, VoIP, WhatsApp, and point of sale integrations.
Original PR description
- https://github.com/odoo/odoo/pull/256814 See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#132502 Forward-Port-Of: odoo/enterprise#131914
Australian payroll now defaults casual employees to the regular casual tax treatment instead of daily casual when daily casual support is not available. This helps ensure affected employees can have student loan withholding applied correctly based on their tax-free threshold status.
Original PR description
Issue: Odoo doesn't provide tax treatments for Daily casual employee. However, it the employement basis is set as casual, it incorrectly defaults to RDXXXX (which is Daily Casual). This prevents the employee from having student loan withhold. This commit defaults it back to regular casual based on the tax free threshold status. Most common case. Daily casual case to be handled in Master. task - 6387496 Forward-Port-Of: odoo/enterprise#131469 Forward-Port-Of: odoo/enterprise#130752
Starshipit delivery rates now validate addresses before requesting prices, so incomplete wallet addresses no longer block the whole express checkout process. If Starshipit cannot quote because the address is partial or another carrier-specific issue occurs, other delivery options can still be shown and customers can continue paying.
Original PR description
#### Description of the issue/feature this PR addresses: Rating a Starshipit shipment for an incomplete delivery address makes the whole rating loop fail instead of skipping that carrier. This breaks…
#### Description of the issue/feature this PR addresses:
Rating a Starshipit shipment for an incomplete delivery address makes the whole rating loop fail instead of skipping that carrier. This breaks express checkout (Apple Pay / Google Pay), where the wallet only discloses a partial address before authorization: the customer gets "update postal address" and cannot pay, while manual checkout works.
#### Current behavior before PR:
delivery_starshipit is the only connector that rates a shipment without validating the addresses first, and it ignores the express_checkout_partial_delivery_address context key set by website_sale. The empty street is sent to /api/rates, which answers 200 with {"success": false, "errors": [{"details": "street parameter value is required"}]}, and _send_request turns that into a UserError. As starshipit_rate_shipment lets it propagate, it aborts the rating of every carrier instead of only this one, so no delivery method reaches the wallet. The same happens for errors only the api can report, such as invalid credentials.
#### Desired behavior after PR is merged:
Both the recipient and the warehouse address are validated before the api is called, as the other connectors already do, and the message is returned as an unsuccessful rate rather than raised. Starshipit requires the street, so a streetless address is still refused, but with a neutral message in the express checkout flow since the customer cannot complete it before paying. The Starshipit carrier is simply not proposed among the wallet's options, the other carriers are rated normally, and express checkout completes.
opw-6393393
Forward-Port-Of: odoo/enterprise#132515
Forward-Port-Of: odoo/enterprise#125117Users can now reopen a sent Sign template even when it was created without signature fields. The system no longer tries to add a placeholder signer when the template is already tied to a sent request, preventing an access error and keeping template review workflows smooth.
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 contact data is refreshed in a multi-company database, any newly created child contact now automatically stays linked to the same company as the original contact. This avoids missing company assignments and helps keep 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