Friday, September 25, 2026
21 changes · master
Enhancements to existing features
Odoo’s Xendit integration now uses Xendit’s newer hosted payment flow, replacing deprecated payment endpoints for new transactions. This keeps payments working as Xendit retires older APIs, improves return status checks when webhooks are delayed, and still supports existing saved cards created with the old system.
Original PR description
The legacy v2/invoices and credit_card_charges endpoints for new payments are deprecated by Xendit. This migrates checkout and card sessions to the /sessions Payment Sessions API redirect flow and…
The legacy v2/invoices and credit_card_charges endpoints for new payments are
deprecated by Xendit. This migrates checkout and card sessions to the
/sessions Payment Sessions API redirect flow and charges v3 tokens through
/v3/payment_requests, while still accepting pre-existing v2 tokens (there is
no documented migration path for them) through the legacy
credit_card_charges endpoint.
Session creation:
- endpoint: v2/invoices -> sessions; response invoice_url -> payment_link_url
- external_id -> reference_id, with session_type PAY or SAVE (for
validation operations) and mode PAYMENT_LINK
- validation operations use the company's own currency instead of the
generic fallback's arbitrary pick, as Xendit's payment channels are
activated per country and an unrelated currency can be rejected
- customer.given_names -> customer.individual_detail.given_names and
surname, split via payment_utils.split_partner_name and sanitized to
alphanumeric (API constraint)
- customer reference_id is made unique per session creation attempt
(customer{partner_id}{random suffix}) as Xendit rejects reused
references and never reuses existing customers
- success_redirect_url/failure_redirect_url ->
success_return_url/cancel_return_url
- payment_methods -> allowed_payment_channels
- addresses removed (not supported by the sessions API)
- country derived from partner.country_id.code, with company fallback
- mobile_number sanitized to E.164 (+digits only, no spaces)
- card payments with tokenization set allow_save_payment_method=FORCED
and card_on_file_type=CUSTOMER_UNSCHEDULED
- removed the inline card form and its direct flow (payment_form.js), the
Xendit SDK and the /payment/xendit/payment route: all methods now
redirect to the Xendit-hosted payment link; the passthrough
_get_redirect_form_view override is dropped as well, and so are the
processing values only the inline form used (rounded_amount, currency,
access_token)
- the now-unneeded xendit_public_key credential is no longer required nor
shown in the provider form; the field itself is kept, as dropping a field
is not allowed in stable
- the session id is saved as soon as the session is created, rather than
waiting for the webhook, so it is available even if the customer returns
first
Tokenized payments:
- charge v3 tokens (prefixed 'pt-') through /v3/payment_requests
(api-version 2024-11-11) instead of /credit_card_charges, using
payment_token_id; channel_code is omitted as it is rejected when paying
with a token
- tokens saved before the migration to the v3 Payment Tokens API aren't
prefixed 'pt-' and aren't accepted by /v3/payment_requests; since there is
no documented way to migrate them, they are still charged through
/credit_card_charges with is_recurring set, same as before this migration
- card_on_file_type is CUSTOMER_UNSCHEDULED for a customer actively paying
with a saved card, or MERCHANT_UNSCHEDULED for an unattended charge
(operation 'offline', e.g. a subscription renewal), to reduce the odds
of a 3DS challenge without forcing skip_three_ds: that flag requires a
dashboard feature most merchants haven't activated, and isn't needed
for card-on-file charges since the card network's own MIT exemption
rules already cover them
- channel_properties requires success_return_url/failure_return_url even
for these off-session charges; built without an access token as the
charge may run outside of a request context (e.g. from a cron)
- if a v3 token charge unexpectedly still requires 3DS authentication
(status REQUIRES_ACTION), a customer-present charge exposes the
authentication URL Xendit returns as the pending_authentication_url
processing value, and the frontend navigates the top window to it
directly rather than submitting a form, since Xendit's page requires the
query string carrying the API key, only accepts GET and refuses to render
inside a frame;
an unattended charge has no cardholder to redirect, so it is set to
error instead of being left pending indefinitely
- create the payment token from payment_token_id; the masked card number
is not included in payment/payment request notifications, but is
included in `payment_token.activation` webhook notifications, which are
handled directly to avoid the extra request; otherwise, it is fetched
with a GET on /v3/payment_tokens/{id} (_xendit_make_request now
supports the GET method)
Status sync on return:
- a customer returning from checkout, or from a 3DS challenge for a v3
token charge, is checked directly against Xendit (via a new
_xendit_sync_from_provider) before falling back to the previous
pending-by-default behavior, in case the webhook is delayed or dropped
- the token charge's success return URL now carries the reference and an
access token when issued from a live request, so a customer returning
from a 3DS challenge can also be checked this way; a charge with no
request context (e.g. a cron renewal) is unaffected, as there is no
cardholder to redirect in that case
- the return controller now also matches `pending` transactions, since a
v3 token charge is already in that state by the time of the 3DS return
Webhook handling:
- unwrap the {event, data} envelope sent for session events
- look up transactions by exact reference_id match (or external_id for
legacy credit_card_charges notifications), falling back to stripping the
random suffix Xendit appends to payment request references
- store payment_session_id or payment_request_id as provider reference
- extend the status mapping: pending (PENDING, ACTIVE, REQUIRES_ACTION),
done (SUCCEEDED, PAID, CAPTURED, COMPLETED),
cancel (CANCELLED, EXPIRED, CANCELED)
Upgrade compatibility:
- the inline_form template is emptied rather than removed: the provider
record is noupdate, so existing databases keep inline_form_view_id
pointing to it, and removing the view would abort the module update
(ondelete='restrict')
- _should_build_inline_form is overridden to never build the inline form:
until the module is updated, the view keeps its old card inputs in
database, which would otherwise still be displayed and whose values would
be silently discarded, as the payment goes through the redirect flow
- bump the module version so partners upgrading notice the change
Webhook migration notice:
- Xendit replaced the single "Invoices paid" webhook field with separate v3
event groups (Payment tokens v3 / Payment requests v3). Databases that had
Xendit configured before this change stop receiving payment and card token
status updates until the Xendit Dashboard is updated with the new fields
- remind admins of this by hooking into the daily autovacuum cron instead of
a dedicated one or an upgrade-triggered migration script: SaaS/.sh don't
force module upgrades, so a migration script would only catch the
providers, companies, and admins that existed at the exact moment of the
upgrade, and a dedicated ir.cron record wouldn't exist on already-installed
databases until then either. @api.autovacuum methods are discovered
directly from the Python class, so they run immediately everywhere, keep
covering providers and admins added later, and can be removed later by
deleting the method, with no leftover cron record to clean up through a
migration
- track whether admins were already notified by checking for an existing
pending activity instead of adding a stored field on payment.provider: a
new column wouldn't exist either on databases where the module isn't
upgraded, and the ORM would error on any domain filtering on it. This
means marking the reminder done re-schedules it on the next run, since
there's no way to tell "resolved" apart from "dismissed" without a field;
that's accepted, as the underlying condition (the webhook still needs
reconfiguring) hasn't actually changed either way
- notify base.group_system, account.group_account_manager, and
sales_team.group_sale_manager rather than only Sales admins: those are the
groups actually granted write access to payment.provider and its Xendit
credential fields, or otherwise likely to own the provider's
configuration, and covering all three means databases using Xendit
without the Sales app installed (e.g. through Invoicing or Point of Sale)
still have someone to notify. A user in more than one of these groups is
only notified once
- the activity note links the "webhook configuration" and "Xendit Dashboard"
mentions inline to the Odoo documentation and to the Xendit Dashboard's
webhook settings, respectively, calling out the October 1, 2026 date after
which the old webhook field stops being honored. The cron itself stops
scheduling new activities after October 7, 2026, since there's nothing
left to prevent once Xendit has already cut over
Task-6373405
Forward-Port-Of: odoo/odoo#277626Spanish electronic invoicing screens are streamlined so users request cancellations from a single action menu instead of separate buttons. Veri*Factu documents now make supporting JSON and XML files easier to access, and SII payloads stay attached to the related document for clearer record keeping.
Original PR description
During the l10n_es [EDI rework](https://www.odoo.com/odoo/project/967/tasks/6175455), the PO introduced some tweaks that would be nice to have. As those tweaks have nothing to do with the rework, we have decided to add them in a different PR: - Replace the direct SII/TicketBAI cancel buttons with a server action bound to the form's action menu, so cancellation goes through a single "Request Cancellation" entry instead of a dedicated button. - Expose the raw JSON attachment on Veri*Factu documents in the list view and add a button to download XML - Attach the SII JSON payload to the SII document record itself instead of the invoice, so it travels with the document it belongs to. task-6523345 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#289111
Customers using self-ordering can now search for their country when entering a phone number instead of scrolling through a basic list. The system also recognizes international calling codes automatically, making phone entry faster and less error-prone.
Original PR description
Replace the native country selector with a searchable SelectMenu and automatically detect the calling code when an international phone number is entered. --- Task: https://www.odoo.com/odoo/project/1737/tasks/6565694 Forward-Port-Of: odoo/odoo#287897
Improves the way accounting tax details are calculated by reducing repeated database work and reusing prepared data. This should make tax-related accounting operations faster and more scalable, especially for companies processing large volumes of journal items.
Original PR description
Issues with the current query: ============================== 1. The query repeatedly scans the entire `account_move_line` table. Rather than first filtering down to only the relevant move lines and…
Issues with the current query: ============================== 1. The query repeatedly scans the entire `account_move_line` table. Rather than first filtering down to only the relevant move lines and reusing that subset, each stage starts from the full table, increasing the amount of data processed. 2. The same tables are joined repeatedly across different CTEs. Instead of carrying forward data that has already been computed, each CTE re-fetches and re-joins the same tables, resulting in unnecessary work. 3. Several JOIN conditions use `COALESCE` and `OR` expressions. These predicates are difficult for the query planner to optimise, leading to inefficient execution plans. 4. The per-row LATERAL joins are expensive because they repeatedly execute a large UNNEST(CASE ...) mapping along with ARRAY_AGG and sorting for every row. Solution: ========= 1. Filter the required account_move_line records once into a temporary table and use this reduced dataset throughout the query. 2. Reuse information from upstream temporary tables wherever possible instead of repeatedly joining the same base tables, reducing redundant joins and unnecessary computation. 3. Move COALESCE and OR logic out of JOIN predicates wherever possible, and simplify joins using precomputed join keys to help the planner choose more efficient join strategies. 4. Precompute the flattened tax mappings once in temporary table and reuse it, eliminating repeated LATERAL joins, UNNEST, ARRAY_AGG, and sorting operations. Note: ===== This commit also removes the fallback parameter and makes it a default feature. This is because the fallback is always used with the query in the entire codebase. Explain Analyze for 250K AMLs: ------------------------------------------- [Old Query](https://github.com/user-attachments/files/30706145/250k_old_query_explain_analyze.txt) | [New Query](https://github.com/user-attachments/files/30706163/250k_new_query_with_cte_explain_analyze.txt) ----------------------- task-3941950 Enterprise PR - https://github.com/odoo/enterprise/pull/126025
Tax report detail calculations have been optimized to run more efficiently. This should help accounting reports load faster and support smoother reporting workflows, especially where detailed tax data is involved.
Original PR description
This PR supports the changes made to the tax details query in the Community PR. task-3941950 Community PR - https://github.com/odoo/odoo/pull/279118
Resolved issues and error corrections
This fixes a search issue where bills of materials linked to archived products could be missed, even when users explicitly searched archived records. Businesses can now reliably find historical or inactive manufacturing records when filtering by archived products.
Original PR description
When searching for an archived bom (filer archived activated), if the main product is also archived, the search on the produt will not find the bom. Steps to reproduce: ------------------- * Create…
Code cleanup and technical improvements
The mail module now registers model fields when records are actually initialized instead of relying on a fixed startup map. This makes later-loaded mail updates visible without rebuilding the store, improving reliability while keeping performance broadly stable.
Original PR description
Before this commit, the store knows every field of every model before a single record exists: makeStore instantiates a throwaway record per model to collect its declarations, then walks the models…
Before this commit, the store knows every field of every model before a single record exists: makeStore instantiates a throwaway record per model to collect its declarations, then walks the models twice, once to pair each relation with its inverse, once to map every field, getter and method an _inherits parent provides to the relation it is read through. The problem is that both maps are fixed at boot: a member a model gains later, from a patch in a lazily loaded bundle, is never seen. This commit registers the fields of a model when its first record runs setup(), pairing a relation with its inverse and with its _inherits parent as each side registers, and resolves an _inherits member on access, ModelInternal.resolveParentField(name): a name this model does not own, neither as a field, a technical key nor a member of its class prototype, and that a parent provides. A positive answer is cached, a negative one is not, as the parent may register the field later. The throwaway record and both loops go. Every read of a record goes through the resolution, so the cheapest answers come first: a model with no _inherits returns on one property read, and a name the model owns on one lookup, both before the cache. Over 200k reads, master against this commit: an own field 122ns / 130ns, an inherited one 1115ns / 1136ns, a technical key 95ns / 98ns. Only a name that is none of those, on a model that does inherit, walks the parent again on every read, 98ns / 305ns; opening a channel of 30 messages resolves 44491 names and walks 80 of them.
When searching for an archived bom (filer archived activated), if the main product is also archived, the search on the produt will not find the bom.
Steps to reproduce:
-------------------
* Create product AA
* Create a bom for product AA
* Archive product AA
* Products > bom
* Search for "Archived" and Product: "AA" -> It will not find the bom
Observation:
-------------
When doing the search, it will start a web_search_read ['&', ('active', '=', False), ('product_tmpl_id', 'ilike', 'AA')].
While in _search it will decide if we filter active elements: https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/models.py#L5363-L5369 In the first level since "active = False" is in the domain (for the bom) it will not add the filter.
The _search will optimize the domain query level by level by calling optimize_full.
First the outher layer :
optimize_full (Domain '[]')> _optimize (DomainNary '&')> _optimize_step (DomainNary '&')
Then the children [1: (active bom), 2: (product template display name)]:
_optimize>optimize_step> ...
While optimizing the second child the domain condition is: [('product_tmpl_id', 'any', [('display_name', 'ilike', 'AA')])]
Since it's a any operation, in optimize_step, it will use the _optimize_any_domain_at_level:
https://github.com/odoo/odoo/blob/16aaaafd7d314c04f39b1f633af3b5e15b46d78b/odoo/orm/domains.py#L970-L972
After doing some checks it will continue to optimize per level with the related comodel:
https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/domains.py#L1389-L1393
Since it's an "any" operator it need to respect the following guidelines : https://github.com/odoo/odoo/blob/60b5b99d569dbe183ef9c980217ccfe9bf243756/odoo/orm/domains.py#L98-L101
this is no done in `_optimize_any_domain_at_level` since it resolves a relational field's 'any' domain by looking up a fresh comodel recordset without adjusting its context.
Which means that for the following level, on the product template, the active_name is not on the domain anymore and the context was not passed corretly, so _search will automaticly add the filter ("active = True").
opw-6514762
Forward-Port-Of: odoo/odoo#290380
Forward-Port-Of: odoo/odoo#285942This fix improves the readability of chat message bubbles in dark mode after a recent visual theme change made blue and green bubbles too dark and hard to tell apart. Reply previews were also refined with clearer borders and smoother styling, making conversations easier to scan.
Original PR description
Dark theme was tweaked with frost, and this negatively affected the message bubble color by making them much darker and undistinguishable between blue and green color. This commit adapt colors to…
Dark theme was tweaked with frost, and this negatively affected the message bubble color by making them much darker and undistinguishable between blue and green color. This commit adapt colors to match not perfectly but very close to the old color as before frost in dark theme. The border are made more visible in dark theme to match the increased overall contrast that frost provides. Reply-to message have their tweaked too: - overall rounder instead of just half of it - background-color is a slightly tinted of the message bubble color and the theme's primary luminance. - border has weight and color matching in all directions, matching the message bubble color <img width="1562" height="1113" alt="Screenshot 2026-09-25 at 00 58 40" src="https://github.com/user-attachments/assets/16836d95-dd5e-4e75-b668-b8bdd6f3ca3b" /> <img width="1555" height="1079" alt="Screenshot 2026-09-25 at 00 58 47" src="https://github.com/user-attachments/assets/7049d8fa-891d-4b8c-a87f-8c3a6a42cbfe" /> Forward-Port-Of: odoo/odoo#290570
Managers assigned as an employee's Time Off approver can now access that employee's Time Off button even if they do not have broader Time Off permissions. This ensures approvers can manage requests for their team without needing unnecessary access rights.
Original PR description
Issue: A user without any rights on Time Off should still be able to manage the requests of the users he's manager of. However, the computation for `show_leaves` only considers Time Off Officers/Admins and the employee themselves, while it should also consider the employee's Time Off approver. Fix: Include the employee's Time Off approver when computing `show_leaves`, allowing them to access the employee's Time Off smart button. Reproduction Steps: 1. Set a user (e.g. Marc Demo) to "No" for Time Off. Note: If reproducing in v19, Administrator rights on Employees are also needed to access the private employee form view where the issue occurs. 2. Set that user as another employee's Time Off approver. 3. Log in as that user and open the employee's form view. 4. Observe that the Time Off smart button is not visible. Related Tickets: opw-6445714 Forward-Port-Of: odoo/odoo#290047 Forward-Port-Of: odoo/odoo#281589
Point of Sale now avoids reusing a receipt number while a draft order may still exist locally, such as after a reload or second browser tab opens. This prevents duplicate receipt references on paid orders and helps keep printed receipts and fiscal integrations consistent.
Original PR description
Steps to reproduce: - Add a product to a draft order, do not pay - Open the same PoS in a second tab of the same browser (the first tab is sent to the backend), or reload while a draft is being…
Steps to reproduce: - Add a product to a draft order, do not pay - Open the same PoS in a second tab of the same browser (the first tab is sent to the backend), or reload while a draft is being removed - Pay the restored draft, then make and pay a new order Issue: Both orders carry the same pos_reference (e.g. 264-3-000701). The number is printed on both receipts and sent to fiscal integrations that use it as the unique invoice number. Cause: The receipt number is issued client side by DeviceIdentifierSequence, a per-device counter kept in localStorage that all tabs of a browser share. When a draft is removed, its number is pushed on `unsynced_number_stack` to be reused by the next order. `removeOrder` pushes the number synchronously, then deletes the order from IndexedDB without waiting for the transaction. If the page unloads before it commits (the tab is closed by another tab opening the same PoS, a redirect to the backend, a reload), the next load restores the draft from IndexedDB with its number while the stack hands that same number to the next new order. Both are then created on the server with different uuids and the same pos_reference: the server only deduplicates by uuid. Fix: Recycle the number only once the order is gone from IndexedDB. A new `recycleOrderNumber` helper waits on the deletion of the order before calling `saveUnusedNumber`; `removeOrder` uses it after `localDeleteCascade`. `deleteOrders` awaits the recycling so that a delete followed by a new order still reuses the freed number, as before. opw-6558992 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#290305 Forward-Port-Of: odoo/odoo#287667
Fixed an issue where a selection dropdown could reopen and remain visible after a user picked an option. This improves form usability and helps prevent automated workflow checks from failing due to a stuck dropdown.
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#290539
Forward-Port-Of: odoo/odoo#287888This fixes an automated employee event registration test so it opens the expected app menu before running. It helps keep quality checks reliable across Odoo editions and reduces false test failures.
Original PR description
**Trigger:** Running `test_onsite_employee_registration` in a community database starts in Discuss instead of enterprise's usual home menu which crashes the test on the first step. This commit adds the `showAppsMenuItems` util to the test. To ensure the test starts in the correct view. runbot-946289 Forward-Port-Of: odoo/odoo#289486
This fix ensures the HTML editor toolbar shows the correct font size after users remove formatting from text. It also prevents unnecessary nested formatting and keeps the remove-format option disabled when only default styling is present, making editing behavior clearer and more reliable.
Original PR description
### Steps To Reproduce: - Add this content in the HTML editor: `<h2 class=display-3-fs>heading 2</h2>` - Select the heading text. - Click Remove Format. - Check the font-size input in the toolbar.…
### Steps To Reproduce: - Add this content in the HTML editor: `<h2 class=display-3-fs>heading 2</h2>` - Select the heading text. - Click Remove Format. - Check the font-size input in the toolbar. ### Description of the issue/feature this PR addresses: - The font-size class was removed from the block element, but the font-size input still displayed the value associated with the removed class. - The toolbar did not show the correct font size for default block classes (like `o_default_font_size`). - Applying a new font size inside default block classes created nested spans instead of splitting them. - The "Remove format" button remained enabled even when selection had no custom formatting (only default block classes). ### Desired behavior after PR is merged: - The font-size input is updated after removing the font-size class and correctly displays the font size of the resulting block element. - Update the `getFontSizeDisplayValue` utility to find correct CSS variable dynamically to also find the font size of default block classes. - Add default font size classes (like `o_default_font_size` and headings) to `format_class_predicates` resource in `font_plugin.js`. This allows them to be split and replaced instead of creating nested spans. task-6321166 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#290228 Forward-Port-Of: odoo/odoo#272023
Bank reconciliation partner searches now include contacts owned by a selected branch company's parent company, in addition to global contacts. This fixes a multi-company issue that prevented users at branch companies from selecting the right parent-company contacts during reconciliation.
Original PR description
When setting a partner from the bank reconciliation control panel, the partner lookup domain only considered global contacts and contacts directly linked to the selected company IDs. This caused a multi-company issue for branch companies, where users could not find contacts owned by their parent company. The domain is now updated to include: Global contacts (company_id = false) Contacts whose company is a parent of the selected companies (company_id parent_of companyIds) Forward-Port-Of: odoo/enterprise#132677 Forward-Port-Of: odoo/enterprise#118719
Attachments can no longer be removed from approval requests after they are approved, refused, or cancelled. This preserves the supporting documents tied to finalized decisions and helps keep approval records complete and reliable.
Original PR description
## Problem: When an approval request is in the approved, refused, or cancelled state, users are still able to remove attachments. This occurs because the chatter uses keep_on_messages, which updates the attachment to reassign its parent model rather than performing a deletion, bypassing the existing @api.ondelete check. ## Solution: Override the write method to check if an attachment is being detached from an approval request. If the linked request is in the approved, refused, or cancelled state, raise a UserError to prevent the modification. ## Steps to reproduce (runbot v20): 1. Install approvals. 2. Create and submit an approval request, attaching a file to it. 3. Validate, refuse, or cancel the approval request. 4. View attachment in the chatter and click the X button on the attached file to delete. 5. Notice that the attachment is successfully detached from the approval request without throwing a warning. opw-6596029 Forward-Port-Of: odoo/enterprise#132850
Fixed an issue where budget amounts in financial reports could lose their proper formatting after a user opened a cell for editing and clicked away without making changes. This keeps displayed figures clear and consistent, reducing confusion when reviewing budgets and profit and loss reports.
Original PR description
Steps to reproduce:- - Create a budget with a P&L report. - Set a value in a cell. - Then press the edit amount, click somewhere else without changing the amount. - The amount is no longer formatted properly. Cause: When user clicks edit, we populate formatted value for locale format type but when user clicks outside without change, in function `onBlur` we compare populated amount(which is formatted) with cell's no format value. So it mismatches and `focused` is never set `false`. Fix: Make getter `editableValue`, which returns `editableNumericValue` for locale format type and no format value otherwise. Now use this `editableValue` in `onFocus`, `onBlur`(this solves the bug) and `inputValue` to make it more clear. [task-6413368](https://www.odoo.com/odoo/project/967/tasks/6413368) Forward-Port-Of: odoo/enterprise#132802 Forward-Port-Of: odoo/enterprise#131989
The signing item popover layout now has better spacing and cleaner styling. This removes a duplicated visual container effect that caused extra borders and shadows, making the signing interface easier to read and more polished.
Original PR description
Prior to this commit, the item popover layout did not have proper spacing between elements, making it look cramped and less visual appealing. The popover also used the popover class twice, resulting…
Prior to this commit, the item popover layout did not have proper spacing between elements, making it look cramped and less visual appealing. The popover also used the popover class twice, resulting in a double border and shadow. This commit adjusts the spacing and popover styling. | Before | After | |--------|--------| | <img width="277" height="198" alt="Screenshot 2026-09-23 at 13 41 09" src="https://github.com/user-attachments/assets/b550932f-b412-4e15-b337-6e3312057cdf" /> | <img width="304" height="221" alt="Screenshot 2026-09-23 at 13 40 09" src="https://github.com/user-attachments/assets/75abd257-0d95-4c82-86c0-910f6dac03e4" /> | | <img width="285" height="65" alt="Screenshot 2026-09-23 at 13 43 00" src="https://github.com/user-attachments/assets/43e1818a-5568-4271-bf48-f6454281280d" /> | <img width="299" height="68" alt="Screenshot 2026-09-23 at 13 43 20" src="https://github.com/user-attachments/assets/6b95fbe3-663e-4d70-b778-ba1f7219a9ab" /> | task-6594820 Forward-Port-Of: odoo/enterprise#132750
German tax report XML exports now correctly include the reporting period when the tax return frequency is set to quarterly. This helps businesses submit complete and accurate quarterly tax filings without manually checking or correcting the exported file.
Original PR description
The `Zeitraum` tag was not set when the tax return periodicity is `quarterly`. The `account_tax_periodicity` field defines `trimester` as the key for the `quarterly` label. In [account_generic_tax_report.py] though, the code compared the periodicity with the `quarterly` label instead of the `key` trimester. **Steps to reproduce**: - Install the `l10n_de_reports` module and switch to the DE Company. - Go to Accounting settings and set `Tax Return Periodicity` to `quarterly`. - Navigate to Accounting > Reporting > Tax Return. - Export the tax report as `XML`. - Open the generated XML file and observe the `Zeitraum` tag. [account_generic_tax_report.py]: https://github.com/odoo/enterprise/blob/338630decbec76580766dd3b38c3241ecfb4c77a/l10n_de_reports/models/account_generic_tax_report.py#L54 Ticket [link](https://www.odoo.com/odoo/project.task/6581536) opw-6581536 Forward-Port-Of: odoo/enterprise#132944 Forward-Port-Of: odoo/enterprise#132593
The Documents portal link now opens in the main browser window when accessed from the website preview. This prevents an error that could interrupt internal users when navigating to Documents from their portal home page.
Original PR description
Steps to reproduce: - Install documents and website - Go to /my/home from the website preview (as an internal user) - Click on the "Documents" portal entry - Go back to /my/home and click on it again…
Steps to reproduce:
- Install documents and website
- Go to /my/home from the website preview (as an internal user)
- Click on the "Documents" portal entry
- Go back to /my/home and click on it again
Issue:
A traceback is raised:
TypeError: Cannot read properties of null (reading 'body')
at WebsiteBuilderClientAction.onIframeLoad
Cause:
The link is opened inside the website preview iframe. For internal users, /my/documents redirects to /odoo/documents, which is served with `X-Frame-Options: DENY`, so the browser blocks it and the iframe's `contentDocument` becomes inaccessible. On the first click, the Chrome workaround in `onIframeLoad` catches the SecurityError and reloads the iframe with `iframe_reload=1`. On the next click, that workaround is skipped because `iframe_reload` is already in the src, and accessing `contentDocument.body` crashes.
Fix:
Register /my/documents in the `isTopWindowURL` registry so that the website preview opens it in the top window instead of the iframe, as done in website_knowledge for knowledge routes.
task-6455756Opening a previously sent Sign template with no fields no longer triggers an access error. The system now avoids adding a placeholder signer when the template is already tied to a sent request, so users can view these templates normally.
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#132898 Forward-Port-Of: odoo/enterprise#132516
Australian payroll now defaults casual employees to the regular casual tax treatment instead of daily casual. This helps ensure eligible employees can have student loan withholding applied correctly.
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