Wednesday, August 26, 2026
119 changes · master
Security fixes and vulnerability patches
The website configurator now picks suitable images even when a user enters an industry that is not already listed, helping generated websites look more relevant and polished. Logo uploads are also validated more reliably by checking the actual file type, preventing mislabeled non-image files from being accepted.
Original PR description
## [IMP] website: choose appropriate images for unknown industries When the user types an industry that is not in the list, its id is -1 and no image can be fetched, leaving the preview without pictures, and the generated website with the base theme images. Ask the AI for the known industry that best matches what was typed, with a confidence score, and reuse that industry's images when the confidence is high enough. task-6280905 ## [FIX] website: check if configurator logo is really an image Bug: The logo upload only checked if the file extension was one of an image, so if someone uploads a file with it's extension modified, the file was accepted without errors. Fix: We now check if the mimetype inside the file is correct before uploading the logo in the db. If not, return an error notification. task-6280905
New functionality added to Odoo
A new biometric attendance foundation lets Odoo receive, store, and process employee punch data from biometric devices. Initial integrations for eSSL and Mantra devices automate attendance creation, reduce duplicate entries, and make future provider additions easier.
Original PR description
- Add a new base module hr_attendance_biometric for common biometric attendance functionality. - Introduce a common transaction model to receive and track biometric punch data. - Store raw biometric…
Enhancements to existing features
Odoo now starts real-time notification subscriptions from a confirmed point instead of relying on a short replay window. This reduces the risk of missed updates after connection issues and avoids unnecessary message bursts after long inactivity.
Resolved issues and error corrections
Restores the page state used when mobile bottom sheets, such as the blog mobile menu, are open. This fixes incorrect styling and prevents the page behind the sheet from scrolling, improving the mobile browsing experience.
Original PR description
Regression from #281432: the `bottom-sheet-open` class the bottom sheet plugin set on the body was removed there, following [a review comment of…
Regression from #281432: the `bottom-sheet-open` class the bottom sheet plugin set on the body was removed there, following [a review comment of mine](https://github.com/odoo/odoo/pull/281432#discussion_r3756639100) that wrongly concluded it was dead code — my grep missed `website.scss`, which keys the frontend bottom sheet styles on `body.bottom-sheet-open` and uses it to lock the page scroll behind an open sheet:
```scss
body.bottom-sheet-open {
--BottomSheetStatusBar__entry-background--active: #{$primary-bg-subtle};
...
// Prevent the page from being scrollable when a bottom sheet is open.
overflow: hidden;
}
```
Since then the blog mobile menu is styled wrong and the page scrolls behind the sheet on mobile.
This restores @jucop-odoo's original code from the PR, with two changes:
- `bottom-sheet-open-multiple` is not restored: nothing in odoo/enterprise ever read it.
- the count is decremented from the overlay's `onRemove`, which runs exactly once, rather than from the returned `remove`, which can be called several times for the same sheet.Features or functions removed from Odoo
Helpdesk and Frontdesk no longer show duplicate “Today” date filters now that the system provides this option automatically. This keeps search menus cleaner and prevents saved defaults from pointing to removed filter options.
Original PR description
Remove the Today filters on Closed On / Creation Date / Rated On (helpdesk) and next to Date (frontdesk), that are redundant with the relative filters we introduced in the previous [PR](https://github.com/odoo/odoo/pull/267980) Community: https://github.com/odoo/odoo/pull/284154
Code cleanup and technical improvements
This change restructures how loyalty rewards, gift cards, and eWallets are handled in Point of Sale. Cashiers now claim free product rewards manually, reward lines are clearer at zero price, and the system rechecks loyalty calculations at order validation to improve consistency and prevent misuse.
Original PR description
Enterprise PR-[#127058](https://github.com/odoo/enterprise/pull/127058) Upgrade PR-[#10948](https://github.com/odoo/upgrade/pull/10948) --- Moved the computation of the different loyalty rewards from…
- Add a new base module hr_attendance_biometric for common biometric attendance functionality. - Introduce a common transaction model to receive and track biometric punch data. - Store raw biometric transactions for audit, troubleshooting, and reprocessing. - Provide a common flow to convert provider-specific punch data into a standard format. - Automatically create Odoo attendance records from processed biometric transactions. - Prevent duplicate attendance records by tracking processed transactions. - Match biometric punches with employees using employee or punch codes. Add provider-specific integrations: - Add hr_attendance_biometric_essl for eSSL Security biometric devices. - Add hr_attendance_biometric_mantra for Mantra Softech biometric devices. - Receive attendance logs from eSSL and Mantra through webhook endpoints. - Convert eSSL and Mantra payloads into the common biometric transaction format. - Process provider transactions through the shared attendance integration flow. This modular approach separates common biometric attendance logic from provider-specific implementations, making the integration easier to maintain and allowing additional biometric providers to be added in the future. Task-6053426
Self-order kiosks can now accept QRIS QR code payments, enabling customers in Indonesia to pay without cashier assistance. The system checks payment status in the backend and updates the kiosk automatically, making unattended kiosk ordering smoother and more reliable.
Original PR description
Add a module letting a self-order kiosk accept QRIS QR code payments. A QR payment in a kiosk has no cashier to confirm it, so the backend decides the outcome and pushes it to the kiosk over the bus. The module ships a provider-agnostic kiosk QR framework (`kiosk_qr_mixin`) supporting both webhook- and polling-driven acquirers, kept free of any QRIS reference so it can move to `pos_self_order` once a second QR provider exists, plus the QRIS provider itself, which polls `l10n_id.qris.transaction` for the result. `point_of_sale` registers QRIS as an `external_qr` provider so it shows up in the payment method setup. task-5352384
This update lets AI agents be set up and managed from a chat-style interface, including their tools, data sources, and triggers. Agents can also create and run automations such as scheduled summaries or follow-ups, making AI workflows easier to configure without technical screens.
Original PR description
This PR introduces agentic automations through the AI app. It also extensively refactors the AI app flow - making the discuss chat view, the main way to change agent configurations. Task-6334162
Original PR description
*: im_livechat, mail, point_of_sale, pos_self_order, web, website_livechat. When the client subscribes to new channels, it passes its last known notification id as the starting point of the stream.…
*: im_livechat, mail, point_of_sale, pos_self_order, web, website_livechat. When the client subscribes to new channels, it passes its last known notification id as the starting point of the stream. For the very first connection, the last id defaults to 0. In this case, the server replays notifications from the last 50 seconds. This heuristic can lead to missed notifications (e.g. if the WebSocket fails to connect and retries exceed the window), or to a large useless dispatch when reconnecting after a long period of inactivity. This commit fixes both issues by having the client always start from an explicit, known-good id instead of 0. A topic fetches notifications starting from the lowest last id among its subscribers. If a single subscriber joins with 0 as its last_id, that 0 becomes the lower bound for the whole topic, and the 50s heuristic then applies to everyone sharing it, possibly excluding notifications the topic still needed. Providing a real id also fixes this issue. follow-up of task-5449503 enterprise: https://github.com/odoo/enterprise/pull/128917
This update makes Turkish e-Dispatch workflows easier by adding a single fetch action for XMLs and PDFs, improving upload placement, and naming files so they are easier to match. It also fixes commercial invoice status and banner behavior so users can correctly see whether a recipient response is pending, approved, auto-approved, or rejected.
Original PR description
## Description of the issue/feature this PR addresses: Fetch-button and matching UX improvements for e-Dispatch (receipts & deliveries), plus a fix to commercial (TICARIFATURA) invoice status…
## Description of the issue/feature this PR addresses: Fetch-button and matching UX improvements for e-Dispatch (receipts & deliveries), plus a fix to commercial (TICARIFATURA) invoice status handling and its response banner on account.move. ## Current behavior before PR: - "Upload e-Dispatch (XML)" is only available as a list-view toolbar button. - There is no single action to fetch e-Dispatch XMLs and then their PDFs. - "Update From GİB e-Dispatch (XML)" shows even when no XML is linked to the receipt. - Updating a receipt from its XML does not (re)fetch the matching Nilvera PDF. - Outgoing deliveries do not fetch the Nilvera PDF on status synchronization. - Fetched XML attachments are hard to match (no customer name in the name). - Commercial (TICARIFATURA) invoices that succeed without a recipient answer never reach the "succeed" status, and the commercial response banner (awaiting / approved / rejected) does not display correctly. ## Desired behavior after PR is merged: - "Upload e-Dispatch (XML)" is moved to the Actions menu on the list view. - A new "Fetch e-Dispatches" action fetches the XMLs and then the PDFs, in order (receipts and deliveries). - "Update From GİB e-Dispatch (XML)" is hidden until an e-Dispatch XML is linked. - Updating a receipt from its XML (re)fetches the matching Nilvera PDF. - Outgoing deliveries fetch the Nilvera PDF (via the Sale channel) on a successful status sync. - Fetched XML attachments carry the customer name for easier matching. - Commercial invoices persist "succeed" while awaiting the recipient's answer, and the awaiting / approved / auto-approved / rejected banners display correctly. task-6044179 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
HR will now automatically create company closure days for the next 12 months on a monthly schedule. The wording is also updated from public holidays to closure days, making it clearer that these days can cover any company-wide closure, not only official holidays.
Original PR description
Description of the issue/feature this PR addresses: 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
Partner autocomplete now searches across additional company identifier fields, not just name or VAT. This makes it easier for users to find the right company using identifiers such as DUNS, improving data entry speed and accuracy.
Original PR description
We want to add the autocompletion feature on the multi-ids fields. Before, we could search by VAT number or by name. Now, we can search by name, VAT, or any field in the multi-ids (including DUNS) Related on IAP : https://github.com/odoo/iap-apps/pull/1755 Task-5367705
The icon picker in the website editor now searches Material Symbols on the server instead of sending the full searchable icon list to every browser. This reduces unnecessary page-load weight and makes icon searches feel more responsive, while also making icon font generation more consistent for future updates.
Original PR description
Enterprise PR: odoo/enterprise#127664 [REF] web: rename the webIcon "iconClass" key to "icon" --- __Before this commit__ - The `webIcon` object built from `ir.ui.menu.web_icon` exposed its first part…
Enterprise PR: odoo/enterprise#127664
[REF] web: rename the webIcon "iconClass" key to "icon"
---
__Before this commit__
- The `webIcon` object built from `ir.ui.menu.web_icon` exposed its first
part as `iconClass`, a leftover from the FontAwesome era.
- Since the Material Symbols migration that part is a `data-icon` name,
not a class: every reader passes it to `t-att-data-icon`.
__After this commit__
- The key (and the local variables around it) is named `icon`.
[IMP] html_editor: shorten icon search debounce
---
__Before this commit__
- `SearchMedia` hardcoded a 1000ms debounce, so the icon picker waited a
full second after each keystroke before searching.
__After this commit__
- `SearchMedia` accepts an optional `delay` prop (still 1000ms by
default, leaving the file/image selectors untouched).
- `IconSelector` passes `delay="250"` for a snappier pictogram search.
[IMP] html_editor, web: move ms icons list in backend
---
__Before this commit__
The Material Symbols list used by the media dialog's icon picker was a
generated JS module of ~152 KB for 446 icons, shipped in the assets
bundle to every browser on every page load. Most of that weight is the
`tags` search terms, only ever used to filter the list when the user
types in the picker.
__After this commit__
`generate_icons.py` writes a Python `MS_ICONS` dict in
`html_editor/controllers/ms_icons.py` instead of a JS module, and the new
`/html_editor/material_symbols_search` route searches it server-side
against a `name tags` index built once at import. It returns only
`{name, has_fill}`, so the tags never reach the browser.
The picker fetches the full list on start, then one filtered list per
search. Odoo UI icons (`oi_*`) are still read from the CSS rules and
searched client-side, since they are cheap to discover there.
`MediaDialog` rebuilds the pre-selected icon from the element's
`data-icon` attribute instead of looking it up in the full list.
[FIX] web, mail: make the icon font generation reproducible
---
__Problem__
Two runs of `addons/web/tooling/icons/generate_icons.py` on an unchanged
`icons_wishlist.txt` produce different font files, so a diff on those
binaries tells nothing about whether the icons actually changed.
__Reason__
- `detect_filled_variants` returns a `set`, whose iteration order varies
from one process to the next (string hash randomization). That order
is what gets handed to `fontext` as its ligature list, and `fontext`
numbers the glyphs it keeps in the order it is given them, so the
filled half of `glyf` is reshuffled on every run -- 242 of the 717
glyphs.
- `fontext` stamps `head.created` / `head.modified` with the current
time, and the `TTFont` reading its output recomputed `modified` once
more on save.
__Quick fix__
Sort the suffixed icon names before subsetting, and date the fonts only
when their content actually changed: `save_font` compares the newly built
font with the one already on disk, both with their `head` dates equalized,
and rewrites the same bytes when they match. `created` is kept for the
lifetime of the file, `modified` only follows a real change.
Rebuilding on an unchanged wishlist now leaves every generated font
untouched, so any diff on them means the icons really moved. The WOFF1
fallbacks and the `_pua_cmap` font are regenerated here, as they still
carried the old glyph order.Belgian payroll now better supports flexi-job employment workflows, including Dimona notifications, shift-hour capture, and specific wage calculations. This helps businesses stay compliant with Belgian reporting rules while reducing errors around refused notifications and quarter-based contract limits.
Original PR description
Modifications include: - Updated the dimona workflow to support flexi-jobs, including capturing shift hours for same-day contracts. - Added restrictions to prevent automatic dimonas beyond 31 days and block contracts overlapping multiple quarters. - Added support for a new "Flexi Wage" and specific payroll computations. - Implemented anomaly code handling (460/510) to fall back to regular wage computations when a dimona is refused. - Updated the DMfA XML templates and schemas to include the `FlexiNotion` tag for relevant employees. Task Id : 6033394 Description of the issue/feature this PR addresses: 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
Customer credit and debit notes in India can now be marked as either standard adjustments or price-only adjustments. This helps GST filings, e-invoicing, printed documents, and sales quantities correctly distinguish goods movement from value-only corrections.
Original PR description
Customer credit/debit notes are often issued to adjust prices without any movement of goods, for example, for a discount, rebate, or price revision without any change in quantity. This PR introduces…
Customer credit/debit notes are often issued to adjust prices without any movement of goods, for example, for a discount, rebate, or price revision without any change in quantity. This PR introduces an Adjustment Type field on customer credit/debit notes with two options: - **Standard:** Used when the adjustment involves a change in quantity, such as goods returned, cancelled service contracts, or additional goods supplied. - **Price Adjustment:** Used when the adjustment only changes the amount, with no change in quantity, such as a discount, rebate, or price revision. This distinction allows GST filing, EDI, and sales documents to correctly reflect whether an adjustment involves movement of goods or is purely a value correction. Changes included: - Add an Adjustment Type field on credit/debit notes. - Add the Adjustment Type field to the invoice report so users can clearly identify the type of credit/debit note. - Add Adjustment Type field on account move reversal wizard - For Price Adjustment moves, the EDI reports quantity and unit price as zero on each line while preserving the actual taxable value and tax amounts. - Exclude Price Adjustment moves from invoiced quantity computation in sales documents. task-6370202 Related enterprise Pr - https://github.com/odoo/enterprise/pull/126929 Related Upgrade Pr - https://github.com/odoo/upgrade/pull/11013
This change prevents deletion of records that are still linked to Indian e-Waybill documents, helping businesses avoid accidental loss of compliance-related information. It improves data integrity across invoicing and stock operations where e-Waybills are involved.
Original PR description
task-6445766 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Users can now use a middle-click on the expand button in form dialogs to open the record in a new browser tab. This makes it easier to keep the current workflow open while reviewing or editing related information separately.
Original PR description
This commit adds the ability to detect a middle click on the Expand button to the Dialog API. This is achieved through the `t-custom-click` directive. The expand callback function that is given to the Dialog API, will now receive two parameters: the event and whether it's a middle click. This commit also uses the new API to allow the FormViewDialog and x2ManyFieldDialog form dialogs to expand to a new tab. task-id: 5429014
Website links that open in a new browser tab now include hidden text or updated labels so screen readers can announce this behavior. This helps non-visual users understand what will happen before activating a link, improving accessibility and reducing confusion.
Original PR description
Screen readers generally do not automatically announce that a link opens in a new tab when a user navigates to it, creating a potential barrier for non-visual users who may become disoriented if the current page is replaced without warning. Solution: Add a visually hidden span inside a tags with target=_blank Task-6009931
Point of Sale no longer creates separate rescue sessions when orders arrive after a sales session has closed. Orders are now redirected to an available open session when possible, or staff are notified to reload and resync, reducing duplicate session handling and simplifying operations.
Original PR description
Rescue sessions are not created anymore. Before, they were use to store orders that were sync with sessions after these sessions were closed. Now if we try to sync an order with a closed session, it will either be synced with the current open session (if any) or raise an error if no valid session is available. After that, the frontend will receive a notification from the backend telling that the session is closed and will reload the page and then resync the orders with the new session. Rescue session is thus not needed anymore. task-id: 6332359 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Purchase reporting now includes the related project, so teams can break down purchase orders by project. This makes it easier to build project-based purchase insights in spreadsheets and dashboards.
Original PR description
Before this commit, it was not possible for the user to analyze his purchase orders based on the project related inside purchase report. This commit adds the project_id field inside that report to be able to use in spreadsheet or dashboard app.
Timesheet Assistant suggestions for Helpdesk tickets now show the actual ticket name and carry it into the timesheet form when added. This makes time entry faster and reduces mistakes caused by unclear labels or lost ticket details, while also avoiding incorrect grouping of unrelated events.
Original PR description
Before this commit, the Timesheet Assistant displayed static labels for Helpdesk Tickets. Furthermore, when a user clicked "Add" on a ticket suggestion, the Timesheet Inline Form did not auto-populate the ticket name, as the source ID was lost during the grouping phase. Task: 6320652 Forward-Port-Of: odoo/enterprise#121662
Quality reporting now helps teams compare how well vendors, products, and control points perform based on yearly pass rates. Buyers can see supplier quality information directly on vendor records, purchase requests, and quality control views, making sourcing and follow-up decisions easier.
Original PR description
Quality reporting did not provide a direct way to evaluate successful checks by vendor, product, or control point. This commit's changes: - Compute Quality Rate as the average pass percentage, with every quality check contributing the same weight regardless of control type. - Add partner and effective-date filters to Quality Check Analysis. - Show the yearly Quality Rate on partner forms and group its report by product. - Add an auto-installed Purchase/Quality bridge that shows the vendor's yearly Quality Rate on RFQs. - Add a Suppliers smart button on control points that opens yearly Quality Rate results grouped by partner. - Count distinct partners from incoming receipts as control point suppliers. task-6392885
Room bookings and point-of-sale order tracking now start listening for updates from the correct point in time. This reduces the chance of users missing important live notifications during the brief moment between loading information and subscribing to updates.
Original PR description
…point Add the `bus_info` to the room booking view. Required not to miss any notifications between fetch and first subscription. community: https://github.com/odoo/odoo/pull/283436
Mexican payroll users now get a warning when paid payslips in a pay run are still waiting for their required Payroll CFDI. This helps employers issue documents within the legal deadline based on company size and avoid compliance risk.
Original PR description
RMF Rule 2.7.5 gives Mexican employers a limited number of business days after the payment date to issue Payroll CFDIs, depending on the company's headcount: 3 days up to 50 employees, 5 up to 100, 7 up to 300, 9 up to 500, 11 above. This adds a payroll warning on Mexican payslips in a pay run that are still waiting for their CFDI. One change in hr_payroll: issues were only computed for draft and validated payslips (plus paid ones with wrong data), but a CFDI deadline only starts running once the payslip is paid, so the warning vanished exactly when it mattered. _compute_issues now takes its payslips from a new _get_payslips_to_compute_issues() that localizations can extend. Default behaviour is unchanged, and the Mexican override adds a deliberately narrow set. task-6433985
Payroll users can now access closure days directly from the Payroll app menu. This makes it easier for payroll teams to manage company closure days in the same place as related payroll work.
Studio now uses the shared icon picker already used elsewhere in Odoo, making icon selection more consistent and easier to maintain. Users creating app icons or button boxes can also choose filled icon styles, while internal naming was simplified to better match the current icon system.
Original PR description
Community PR: odoo/odoo#278149 [IMP] web_studio: use the html_editor icon selector --- __Before this commit__ - Studio had its own icon picker (`StudioIconSelector`), a `SelectMenu` built on top of…
Community PR: odoo/odoo#278149 [IMP] web_studio: use the html_editor icon selector --- __Before this commit__ - Studio had its own icon picker (`StudioIconSelector`), a `SelectMenu` built on top of `IconSelector.initFonts()`. That helper is gone: the Material Symbols list now lives in the backend and the html_editor `IconSelector` fetches it on demand, so the whole list is no longer available client-side to feed a `SelectMenu`. __After this commit__ - The Studio component is dropped and the html_editor `IconSelector` is reused directly, wrapped in a `Dropdown` in both places that picked an icon: the app icon creator and the "add a button box" dialog. - The selection to highlight in the grid is rebuilt from the icon name alone, the same way `MediaDialog` does it since the list is fetched lazily. - The picker also offers the filled variant of the icons. It is stored by suffixing the icon name with "_f", which is the ligature of the filled glyph in the font: no renderer has to know about it. [REF] web_enterprise, web_studio: rename "iconClass" to "icon" --- __Before this commit__ - The app icon name was carried around as `iconClass`, from the `webIcon` object down to the `IconCreator` prop, a leftover from the FontAwesome era. - Since the Material Symbols migration that value is a `data-icon` name, not a class: every reader passes it to `t-att-data-icon`. __After this commit__ - The key and the prop are named `icon`.
Managers can now clearly see employees without active contracts in the Attendance Gantt view, where they are highlighted in red with a greyed-out row. The attendance form also shows a warning and links to the employee profile, helping teams resolve missing contract issues faster.
Original PR description
Previously, employees without contracts is not explicit from the attendance gantt view. This made it hard for managers to notice that someone was missing a contract and needed attention. Now these employees are visible with clearly flagged with a red name and fully grayed-out row according to conditions as user have self edit and attendance approver rights. On attendance form displays a warning that employee is not in contract and redirect to Employee Profile. task-6259306
Belgian payroll now better supports flexi-job employment workflows, including shift-hour tracking, Dimona notifications, and dedicated wage calculations. This helps employers stay compliant with Belgian reporting rules while reducing manual corrections when flexi-job declarations are refused or restricted.
Original PR description
Modifications include: - Updated the dimona workflow to support flexi-jobs, including capturing shift hours for same-day contracts. - Added restrictions to prevent automatic dimonas beyond 31 days and block contracts overlapping multiple quarters. - Added support for a new "Flexi Wage" and specific payroll computations. - Implemented anomaly code handling (460/510) to fall back to regular wage computations when a dimona is refused. - Updated the DMfA XML templates and schemas to include the `FlexiNotion` tag for relevant employees. Task Id : 6033394
Price adjustment credit and debit notes will no longer change quantity totals in Indian GST HSN reporting or subscription invoicing. This keeps reported quantities aligned with actual goods or services delivered, while financial amounts and taxes continue to be reported correctly.
Original PR description
Price adjustment credit/debit notes are created to adjust total amounts only, they do not alter quantities, since no goods are returned or additional goods supplied. This PR ensures such moves are consistently excluded from quantity-based computations across GST reporting and subscriptions. Changes included: - Exclude price adjustment moves from the HSN quantity in the GSTR-1 HSN summary, both in the generated spreadsheet (HSN sheet) and the JSON sent to the government portal. Taxable value and tax amounts remain unchanged. - Exclude price adjustment moves from sale subscriptions invoiced quantities task-6370202 Related community Pr - https://github.com/odoo/odoo/pull/279532 Related Upgrade Pr - https://github.com/odoo/upgrade/pull/11013
Bulk account assignment in bank reconciliation now completes much faster when many statement lines are selected. This reduces waiting time for accounting teams and makes high-volume reconciliation work more efficient.
Original PR description
Description =========== The bank reconciliation widget sends the bulk "Set Account" action as a single RPC, but the server still processes most of the work separately for every selected statement…
Description
===========
The bank reconciliation widget sends the bulk "Set Account" action as a single RPC, but the server still processes most of the work separately for every selected statement line.
Each iteration searches for recent statement lines and reconciliation models, writes the account on one move line, and posts its matching confirmation independently. The cost therefore grows linearly with the selection size.
Cache reconciliation-rule lookups for the duration of the bulk operation, while updating the cache when rules are created or deleted.
Also assign the account to all selected move lines in one recordset write, avoiding frequent calls to `_compute_reconciled_payment_ids` 1 by 1.
Finally, collect the lines requiring a matching confirmation and create their chatter entries in one batch.
Benchmark
=========
The benchmark measures one server-side bulk Set Account call; fixture creation is excluded from the timings.
| Selected lines | Before | After | Improvement |
|----------------|--------|-------|------------------|
| 500 | 12.5 s | 2.5 s | 5x faster (-80%) |
Benchmark data cardinality:
- 1 company
- 1 isolated bank journal
- 3 accounts: liquidity, suspense, and target expense
- 500 unreconciled bank statement lines
- 500 suspense move lines passed to the method, one per statement line
- 1 target account shared by all selected lines
- 1 journal/company/account cache group
- 1 batched call, executed sequentially in one cursor
- Negative amounts, avoiding automatic bank-fee rule creation
- 500 distinct references shorter than 10 characters, preventing a new reconciliation model from being created
- No taxes on the target account
Blueprint
=========
```xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
==========================================================
BENCHMARK: Bulk Set Account on Bank Statement Lines
==========================================================
Creates 500 unreconciled statement lines in one isolated bank
journal, then calls set_account_bank_statement_line() once on the
whole recordset, as the bank reconciliation widget's bulk action does.
Negative amounts bypass the unrelated bank-fee rule path. Distinct
labels shorter than 10 characters prevent an automatic reconciliation
model from being created during the run. Without the bulk cache, the
first line only runs the history search and every subsequent line runs
both searches in _check_and_create_reconciliation_rule(). This keeps
the query-count regression visible without changing the scenario.
-->
<record id="benchmark_bulk_set_account_bank_statement_line" model="populate.blueprint">
<field name="name">Benchmark: Bulk Set Account on Bank Statement Lines</field>
<field name="definition_xml" type="xml">
<create model="account.account" count="1" id="bench_bulk_set_account_liquidity" scale="False" parallel="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Benchmark Bulk Set Account Liquidity {counter}'"/>
<field name="code" eval="f'BSAL{counter:03}'" unique="True"/>
<field name="account_type" eval="'asset_cash'"/>
</create>
<create model="account.account" count="1" id="bench_bulk_set_account_suspense" scale="False" parallel="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Benchmark Bulk Set Account Suspense {counter}'"/>
<field name="code" eval="f'BSAS{counter:03}'" unique="True"/>
<field name="account_type" eval="'asset_current'"/>
</create>
<create model="account.account" count="1" id="bench_bulk_set_account_target" scale="False" parallel="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Benchmark Bulk Set Account Target {counter}'"/>
<field name="code" eval="f'BSAT{counter:03}'" unique="True"/>
<field name="account_type" eval="'expense'"/>
</create>
<create model="account.journal" count="1" id="bench_bulk_set_account_journal" scale="False" parallel="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="name" eval="f'Benchmark Bulk Set Account Journal {counter}'"/>
<field name="code" eval="f'BSA{counter:03}'" unique="True"/>
<field name="type" eval="'bank'"/>
<field name="default_account_id" ref="bench_bulk_set_account_liquidity"/>
<field name="suspense_account_id" ref="bench_bulk_set_account_suspense"/>
</create>
<create model="account.bank.statement.line" count="500" id="bench_bulk_set_account_statement_lines" scale="False" parallel="False">
<value name="counter" generator="misc.counter" start="1"/>
<field name="journal_id" ref="bench_bulk_set_account_journal"/>
<field name="date" eval="'2026-08-18'"/>
<field name="payment_ref" eval="f'{counter:08x}'"/>
<field name="amount" eval="-100.0"/>
</create>
<function model="account.bank.statement.line"
name="set_account_bank_statement_line"
ref="bench_bulk_set_account_statement_lines"
batched="True"
parallel="False"
context="{'account_default_taxes': True}">
<value name="journal_id"
generator="relation.one"
comodel_name="account.journal"
ref="bench_bulk_set_account_journal"/>
<value name="suspense_account_id"
generator="relation.one"
comodel_name="account.account"
ref="bench_bulk_set_account_suspense"/>
<arg eval="[
line.line_ids.filtered_domain([
('account_id', '=', suspense_account_id),
]).id
for line in model.search([
('journal_id', '=', journal_id),
], order='id')
]"/>
<arg generator="relation.one"
comodel_name="account.account"
ref="bench_bulk_set_account_target"/>
</function>
</field>
</record>
</odoo>
```
Reference
=========
task-6480127The Pakistan localization now treats 18% GST for 3rd Schedule sale and purchase taxes as already included in the printed retail price. This prevents GST from being added on top of the listed price and aligns tax calculations with local rules.
Original PR description
Description of the issue/feature this PR addresses: 3rd Schedule goods in Pakistan are taxed on the printed retail price, which already includes GST. The sale and purchase "GST 18% 3rd" taxes in the Pakistani chart of accounts were configured as tax-excluded. Current behavior before PR: The 18% is added on top of the product's sales price. Desired behavior after PR is merged: The 18% is extracted from the retail price. task-6466479 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The data cleaning merge preview dialog has been simplified by removing unnecessary titles and repeated descriptions. This makes the information easier to scan and helps users understand merge decisions with less distraction.
Original PR description
Simplify the merge preview dialog by removing noisy titles and redundant descriptions, making its sections more consistent and easier to understand. task-6236930
Point of Sale now asks the browser to protect offline sales data from automatic cleanup when a device runs low on storage. This reduces the risk of losing unsynced orders on PoS terminals, while keeping startup unaffected because the request runs in the background.
Original PR description
All PoS offline data, including orders not yet synced to the server, lives in indexedDB. By default this storage is "best-effort": when the device runs low on disk space, the browser is allowed to silently evict an entire origin's data. On a PoS terminal holding unsynced orders, such an eviction means losing those orders for good. This PR requests persistent storage with `navigator.storage.persist()` when the data service initializes indexedDB. When granted, the origin's storage is exempted from automatic eviction; data can then only be removed by an explicit user action. The request is best-effort: Chrome grants or denies it silently based on engagement heuristics (it is always granted for an installed PWA), Firefox prompts the user once. The call is therefore not awaited in the startup path and its outcome is only logged. See https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist
The web interface’s underlying OWL framework was updated to a newer release. This helps keep Odoo’s user interface technology current and can bring stability, compatibility, and maintainability improvements without introducing a specific new business feature.
Original PR description
Release notes: https://github.com/odoo/owl/releases/tag/v3.0.0-alpha.47 Description of the issue/feature this PR addresses: 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 Forward-Port-Of: odoo/odoo#284147
The older Belgian POS blackbox module is no longer available for new installations because it is planned to be replaced by a newer module. This helps new users avoid starting with a module that will soon be deprecated, while existing setups are not described as being changed.
Original PR description
The module `pos_blackbox_be` will soon be deprecated and replaced by `l10n_be_pos_blackbox`. To avoid new users installing the old module, we make it not installable anymore.
The website SEO dialog now detects when translated page content is out of date and prevents users from accidentally confirming translations they have not reviewed. This helps protect translation quality by asking users to review delayed translations in translation mode before editing related SEO image text.
Original PR description
1. Saving the SEO dialog no longer confirms delayed translations. They stay
delayed until the user saves from translation mode, where they can see what
they confirm. (Reverts the logic of 03fa781.)
2. The dialog now loads the page rendered with `edit_translations`, which is the
only render that marks delayed translations, and looks for one in `#wrap`
(the part the dialog reads its images from). When it finds one, it:
- covers the content checks with a message explaining the content is out of
date,
- keeps the alt inputs disabled
task-[6465390](https://www.odoo.com/odoo/project/974/tasks/6465390)Online shoppers can now choose multiple values within the same product filter, such as Lenovo or HP together with a specific storage size, instead of being forced into overly narrow results. The shop filter panel also behaves more consistently, keeping options visible and improving the placement of clear filters for a smoother browsing experience.
Original PR description
Filters are now completely exclusive, which prevent 0 results but also prevents more "open" searches as "Lenovo" OR "HP" AND "512GB SSD". Allow selecting attribute values that are not exclusive if its from the same attribute. Further improvements: 1. move clear filters to below attribute values 2. avoid closing the offcanvas if no products visible 3. avoid putting the selected attributes on top in the offcanvas 4. avoid hiding filters when no products available 5. avoid small displacement in the sidebar filters when selecting a value task-6341310 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Calendar meetings with Discuss video links are now created and shown in Discuss ahead of time, so attendees can find planned calls before anyone joins. Users can filter for planned meetings and schedule a future Discuss meeting from the Meetings menu, while recurring meetings and deleted meetings are handled more consistently.
Original PR description
Before this commit, a meeting holding a Discuss video call link had no channel until someone opened `videocall_location`: the meeting was absent from the Discuss "Meetings" tab, and its invitation…
Before this commit, a meeting holding a Discuss video call link had no channel until someone opened `videocall_location`: the meeting was absent from the Discuss "Meetings" tab, and its invitation link only started working once an attendee joined the call. The one way to get a meeting out of Discuss was the "Meeting" button, which starts an ad-hoc group call with no date attached to it. This commit gives such a meeting its channel upfront, on create and as soon as the link is added afterwards, and keeps the two in line: - the channel is named after the meeting and describes when it takes place (the recurrence rule for a recurring meeting, its date and time otherwise), and is pinned for every attendee having a user, so the meeting shows in the "Meetings" tab of each of them; - its avatar shows the day of the meeting rather than the day the channel was created, in the reader's own timezone; - every occurrence of a recurrence shares a single channel, which only goes away with the last of those meetings; - deleting a meeting takes its video call away, unless the channel already hosted a conversation: that one is only unpinned, so the chat history stays reachable from Discuss while the video call drops out of the tab. The channel is resolved from the meeting the recurrence machinery left live, rather than from the edited record: editing a whole recurrence archives the occurrence it was asked from and moves the new values to a freshly recreated one, which would otherwise write back the values from before the edit. The "Meetings" tab gains a "Planned" filter, keeping only the video calls backed by a meeting, and its "Meeting" button becomes a menu offering "Start Now" next to "Schedule for later", which opens a `calendar.event` form prefilled with a 30 minute Discuss video call. The button is a menu whatever the modules contributing to it, so that starting a meeting is always found in the same place. Scheduling is only offered in the webclient, as the form view it opens is not part of the public Discuss page. Both calendar parts come from hooks defined on `MessagingMenu` itself, `extraTabActions` and `extraTabFilters`, rather than from the /discuss/ patch declaring the tab: a bundle contributing to a tab may be patched in before the one declaring it, which would then discard the override. task-6460543 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Mail activities and messages now load search results in smaller chunks when a result limit is used. This helps affected views open reliably while still respecting access rules, reducing cases where pages stall because too many records are processed at once.
Original PR description
When a limit is applied in the search method, fetch the data in small batches like we do in ir.attachment. This allows to search with a limit on views while applying security access. Without this, simple views cannot be opened because the ORM searches for all records before filtering them in memory. Note that this cannot be avoided for group by queries and that if we don't set a limit, we will eventually still fetch everything in smaller batches. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284235 Forward-Port-Of: odoo/odoo#265203
Belgian payroll can now carry unused employment bonus amounts into the following month when vacation lowers an employee's pay. This helps ensure payroll calculations and related social security reporting reflect the full bonus entitlement more accurately.
Original PR description
Allow unclaimed employment bonuses to be carried over to the next month when vacation reduces remuneration during the month. - Add bonus carry-over salary rules - Update bonus calculations to include previous month amounts - Modify ONSS restructuring to account for carry-over Task Id: 6147223
The web interface now keeps the default grouping option visible in search filters again. This helps users see and manage preconfigured grouped views consistently, avoiding confusion from a recent change that removed that option.
Original PR description
This reverts commit 7413e08c166ef3123ac7ed6623db1fb366f44e3c (#265574) task-6425644 Forward-Port-Of: odoo/odoo#279043
Salary benefit list and form screens have been visually improved to make benefit setup easier to review and manage. The form now also checks numeric benefit values more clearly, helping users enter valid information and reducing setup mistakes.
Original PR description
Visual improvements for list and form views of salary benefits. Add benefit value check for float type in the form view. task-6253384
The general ledger report can now include invoice dates when that column is configured. This gives finance teams clearer context for ledger entries without needing to cross-check invoices separately.
Original PR description
If a column is added with `expression_label` equal to `invoice_date`, include that in results of `_report_custom_engine_general_ledger`. task-5917897 Forward-Port-Of: odoo/enterprise#128938 Forward-Port-Of: odoo/enterprise#113774
The messaging menu can now be customized so AI-related views can show their own tabs, such as Automation, and hide unnecessary counters when focused on a single agent. The update also improves Discuss reliability by ordering empty conversations consistently, preventing loading flicker, and keeping restored URLs in sync.
Original PR description
ai_app adds its own "Automation" tab to the messaging menu, shows only AI and Automation tabs when the menu is scoped to a single agent, and hides counters in that scoped view. None of that was possible: the tab list and counters were read straight from the store in the template. Move both behind component getters so they can be overridden. Also fixed: - sort threads with no message by channel creation date instead of leaving them in arbitrary order. - show the empty thread placeholder only once the thread is mounted and loaded, so it no longer flashes while messages load. - sync the discuss `active_id` into the action service state, otherwise `action.restore()` rebuilds the URL from a stale value. - Agent cards and the New button open the agent chat in Discuss, not a form, so adding them to the clickbot exceptions. task-6334162
Self-order and kiosk customers can now add a tip during checkout when online payment is enabled. This helps staff capture gratuities that were previously missed, with tips shown as a separate line on the order and receipt.
Original PR description
Purpose ------- Staff were losing potential tips because customers placing orders through the self-order or kiosk flows. Specification ------------- - Add a tip step to the self-order/kiosk checkout flow, available whenever online payment is enabled. - Customers can pick a quick tip percentage (15%, 20%, 25%) or enter a custom amount/percentage via a numpad popup. - The tip is added as a dedicated line and appears on the receipt. Task-6254832 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Report action screens have been reorganized to be easier for users to understand and manage. This reduces technical clutter when configuring reports, especially in accounting and printing-related settings.
Original PR description
Clean the views of ir.action.report to be more user friendly. Before this commit, the views were very technical oriented. Task [link](https://www.odoo.com/odoo/project.task/6285465) task-6285465
Report action views in Odoo Studio have been simplified to make them easier for users to understand. This reduces technical clutter and helps business users configure reports more confidently.
Original PR description
Clean the views of ir.action.report to be more user friendly. Before this commit, the views were very technical oriented. task-6285465
Event registration forms now show general, once-per-order questions before attendee-specific questions. This makes the checkout flow easier to follow and better matches what users expect during registration.
Original PR description
**Purpose:** We keep general questions after individual questions, usually users expect it other way around **Specifications:** Move the once per order block on top of the modal Task-6314437
The FACe electronic invoicing option for Spanish public administration invoices is no longer selected by default, reducing unnecessary steps for most customers. When a user selects FACe for a specific partner, Odoo remembers that choice for future invoices to that partner.
Original PR description
1. The generic wizard was not meant to override correctly the default of extra EDI, this commit fixes this problem. 2. FACe is only relevant for public administrations, so keeping it checked by default added unnecessary noise for the majority of partners that never invoice through it. It is now unchecked by default. The first time FACe is selected for a given partner, the choice is remembered so it stays checked for that partner's subsequent invoices, avoiding the need to re-enable it manually each time. 3. Cleaning some dead code, in particular `_get_ref_string` was never called and should've used `_prepare_default_reversal` instead. task-6345379
Discuss now keeps voice calls inline instead of switching users into a full meeting view, while video calls and meetings still open fullscreen. The messaging interface is also simplified by removing duplicate reaction controls, clearing unnecessary breadcrumbs, and improving the messaging menu layout.
Original PR description
A few design feedback fixes in the Discuss UI:
- Voice calls no longer open the fullscreen meeting view. Starting or joining a call with the phone button now stays inline, both in Discuss and in the chat window. Only video calls and meetings go fullscreen.
- "Open in Discuss" clears the breadcrumbs. Keeping the trail of the app the chat window was opened from ("Events / Marc Demo") was only wasted space.
- Removed the add reaction button displayed next to the reactions of a message: it duplicates the "Add a Reaction" message action, which is always available on hover.
- Removed the w-50 on the messaging menu action button and search bar, so the button takes the width it needs and the search bar gets the rest.
task-6488704
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prBelgian payroll now captures additional apprenticeship employee details, including contract type and contract number. Wage checks and payroll reporting are updated to reflect apprenticeship-specific requirements, helping businesses improve compliance and reduce payroll validation issues.
Original PR description
- add contract types and number to Apprenticeship employees. - update wages checks to adapt Apprenticeship details. Task: 6331079
Deferred account reports now look up related accounts in batches instead of one at a time. This reduces unnecessary database work and can make report generation faster, especially for companies with many accounts.
Original PR description
Browse all source accounts at once when resolving their deferred accounts. This lets the ORM prefetch `deferred_account_id` instead of issuing one query per unique account. Introduced in https://github.com/odoo/enterprise/pull/122696
A warning now appears when a Brazilian NFS-e invoice is still pending, explaining that each manual status check consumes one IAP credit. This helps users make informed decisions and avoid unexpected credit usage.
Original PR description
Add a yellow warning message after issuing an NFS-e and the l10n_br_edi_last_edi_status = pending so the user will know that every time they click on "Check NFS-e Status" it will consume 1 IAP credit. Task-id: [6385938](https://www.odoo.com/odoo/project/967/tasks/6385938)
The Planning settings now describe the geolocation option more clearly, helping users understand what the feature does. The Billing setting text was also standardized by removing extra punctuation for a more consistent settings experience.
Original PR description
## [IMP] planning: update geolocation feature description This commit updates the description of the geolocation feature in the Planning settings. ## [IMP] project_timesheet_forecast_field_service_sale: update setting description In this commit, we remove the dot at the end of the "Billing" setting to match standard setting descriptions. task-6450511
Hong Kong payroll rental allowance records now use a clearer proof expiry date and rely on the existing document attachment flow for proof uploads. HR teams can see the current rental proof expiry directly on employee records and confirm or reset multiple rental records at once, reducing manual work.
Original PR description
Remove the unused payment_proof_file field, superseded by the attach_document-based proof upload flow, and rename valid_up_to_date to "Proof Expiry Date" for clarity. Also surface the current rental's proof expiry on the employee form, and allow confirming/resetting multiple rentals at once from the list view instead of one by one. Upgrade PR: https://github.com/odoo/upgrade/pull/11039 task-6391432
Belgian payslips now show and use a clearer disposable earnings amount before salary attachments are applied. This makes it easier for payroll teams to verify attachment limits and account for external replacement wage amounts correctly.
Original PR description
Currently, the net amount before salary attachments is unclear, making it difficult to verify attachment limits on Belgian payslips. - Add 'NET_REPLACEMENT_WAGE' input rule for external replacement wage. - Add 'DISPOSABLE_EARNINGS' rule to compute net base for attachments. - Update 'ATTACH_SALARY' to calculate limits from disposable earnings. Task: 6410485
Point of Sale now preserves manually entered line prices in the same tax display mode used by the cashier. This prevents small rounding differences from growing on multi-quantity sales and helps refunds match the original sale amount.
Original PR description
A price set manually on an order line is typed in the tax mode the prices are displayed in. It was then inverted through the taxes to recover a price expressed in the tax mode of the taxes, and that…
A price set manually on an order line is typed in the tax mode the prices are displayed in. It was then inverted through the taxes to recover a price expressed in the tax mode of the taxes, and that result was rounded to the "Product Price" precision before being stored. With a "Tax-Included Price" display, typing 100 on a product taxed at 15% stored 100 / 1.15 = 86.9565... as 86.96. The residual rounds away at a quantity of 1, but it is multiplied by the quantity, so 2 units total 200.01 and 10 units total 1000.50. This commit stores the typed price as it was typed and adds a `document_tax_mode` field on the order line holding the tax mode that price is expressed in. The mode is passed when the base line is created, in the frontend as well as in the backend, so the taxes engine resolves the price inclusion per tax instead of the price being converted beforehand. It is reset as soon as the price is recomputed, so any other price keeps following the tax configuration of the product, and it is carried over to refund lines so that a refund matches what was sold. task-6472425 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update lets saved searches and default filters correctly use newer relative date options such as today or this week. It also improves date display consistency for locales using non-Western numerals and keeps older customized views from breaking.
Original PR description
In the previous [PR](https://github.com/odoo/odoo/pull/267980) we introduced relative filters (such as `today`, `this week`, ...) and removed the hardcoded filters that duplicated them…
In the previous [PR](https://github.com/odoo/odoo/pull/267980) we introduced relative filters (such as `today`, `this week`, ...) and removed the hardcoded filters that duplicated them (`3e7106201cfe`), for example:
```xml
<!-- BEFORE (planning/views/planning_views.xml) -->
<filter string="Start Date" name="start_datetime" date="start_datetime" end_month="1">
<filter name="start_today" string="Today" domain="[
('start_datetime', '<', 'today +1d'),
('start_datetime', '>=', 'today'),
]"/>
<filter name="start_this_week" string="This Week" domain="[...]"/>
<filter name="start_next_week" string="Next Week" domain="[...]"/>
</filter>
<!-- NOW -->
<filter string="Start Date" name="start_datetime" date="start_datetime"/>
```
However this would now break if trying to target the new relative filter:
```xml
<field name="context"> { 'search_default_start_datetime': 'custom_start_today' }</field>
```
(This example was not in production code but this PR tried to do it, and it broke on rebase:
https://github.com/odoo/enterprise/pull/125775)
In this PR we make it so that relative filters (not only today) can be activates through context keys and `default_period=`. We also make a number of small improvements / refactoring and 1 localisation fix.
**Commit 1** - [REF] web: tidy relative filter internals
- Make filter and option lookups throw on an unknown id instead of returning `undefined` and failing later.
- Centralize the "period + year" concatenation in one `joinWithYear` helper (2 call sites, one from the previous PR).
- Build `displayedFilterItems` in a single pass, attaching each date filter's relative twin to it.
- Drop a missed hardcoded "Today" in `stock_fleet`'s batch transfer view.
- Re-add the four range attributes to the RNG as deprecated-but-accepted, so a custo view no longer crashes.
(Will do a follow-up PR with an upgrade script to remove it)
**Commit 2** - [IMP] web, base: smart dates as date filter defaults
- Accept relative option ids in both `default_period` and `search_default_<filter>` (`today`, `this_week`, `this_month`, `this_quarter`, `this_year`).
**Commit 3** - [FIX] web: use the active numbering system for hand-built date labels
- The week number of the `formatLocalWeekRange` was not being translated to locale numbering system.
**Enterprise commit** - [REM] helpdesk, frontdesk: drop Today filters duplicating the smart date
- Remove the Today filters on Closed On / Creation Date / Rated On (helpdesk) and next to Date (frontdesk), that are redundant with the relative filters we introduced in the previous [PR](https://github.com/odoo/odoo/pull/267980)
Follow up to: https://github.com/odoo/odoo/pull/267980
Enterprise: https://github.com/odoo/enterprise/pull/128934This fixes a display issue where the employee presence badge could appear in the corner of employee cards instead of on the avatar. The change keeps the badge styling limited to avatar layouts, improving visual consistency in HR screens.
Original PR description
Commit[^1] moved the badge's overlap styling out of a SCSS rule scoped to `.o_hr_employee_form_view … .o_employee_avatar` and into `additionalClasses` on the field registry entry. Those classes are…
Commit[^1] moved the badge's overlap styling out of a SCSS rule scoped to `.o_hr_employee_form_view … .o_employee_avatar` and into `additionalClasses` on the field registry entry. Those classes are applied unconditionally, so `position-absolute top-0 end-0 bg-light rounded-circle` leaked into every usage of the widget. On the kanban card the field sits in a non-positioned `float-end` div, so the badge resolved against `.o_kanban_record` and pinned itself to the card corner as a grey disc. A later commit papered over the symptom with `mt-1` (Commit[^2]), removed here. The overlap styling goes back to SCSS, scoped to `.o_employee_avatar` — the container marking the three "badge over picture" layouts, including the enterprise wizard the original rule missed. Also drops a `margin: unset !important` kanban rule left inert since the same refactor. task-6487546 [^1]: https://github.com/odoo/odoo/commit/dc8f38b1054664a0e382530bb4fc73983e2085cb [^2]: https://github.com/odoo/odoo/commit/fbae74ab0c6fa1d8fc0789b27bd60062a5bd29bf --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Rating snippets now keep their accessibility label in sync when the number of stars changes. This helps screen reader users understand ratings correctly across website pages and mass mailing content.
Original PR description
aria-label was not used correctly on s_rating snippet. Moreover, it was not updated when the number of stars is updated. Now, aria-label is updated correctly. Task-6009931
When users start a timesheet timer from a task linked to a sales order item, the timer now automatically keeps that sales link and shows the billable option immediately. This prevents saved timesheets from losing billing information and reduces manual correction work.
Original PR description
**Problem:** Starting the timer on a task that is linked to a sale order item produces a timesheet that is not linked to it, and the Billable toggle is missing from the timer. **Steps to reproduce:**…
**Problem:** Starting the timer on a task that is linked to a sale order item produces a timesheet that is not linked to it, and the Billable toggle is missing from the timer. **Steps to reproduce:** 1. Install Timesheets and Sales 2. Open a task whose Sales Order Item is set 3. Start the timer from the Timesheets systray 4. Save it and open the resulting timesheet **Current behavior:** The Sales Order Item is empty. The Billable toggle only appears after removing and re-adding the task in the timer. **Expected behavior:** The timer is billable on the task's sale order item as soon as it is opened. **Cause of the issue:** `_get_timesheet_pre_filled_form_data` returns only `project_id` and `task_id`. The timer form merges that pre-fill over `timesheet_default_values`, which `lazy_session_info` computes once per session from `account.analytic.line.new()` - a record with no project and no task, so `so_line`, `allow_billable` and `has_available_so` are all `False` in it. Because the pre-fill carries none of those keys, they keep the task-independent session values: the timesheet stays unlinked from the sale order item, and the Billable toggle stays hidden since it is displayed from `has_available_so`. **Fix:** The pre-fill endpoint is the only place that knows which task the timer is being opened on, so it is where the task-dependent values have to be resolved. Reading them off a new timesheet built with that project and task keeps the endpoint generic - it returns whatever `_get_aw_timesheet_fields_specification` declares, so the sale fields stay owned by sale_timesheet_enterprise rather than being named in timesheet_grid. Dropping the session defaults instead was rejected: they are also the only source of `date`, `user_id` and `company_id` for the timer record, and removing them makes saving fail on the required Date field. opw-6423577 Forward-Port-Of: odoo/enterprise#127800
Financial reports now use a generic Date column instead of Invoice Date, so payments and journal entries show the relevant payment or entry date rather than appearing blank. This makes Aged Payable, Aged Receivable, and Partner Ledger reports clearer and more consistent for users reviewing balances.
Original PR description
- In Aged Payable and Aged Receivable show payment date for payments and journal entry date for misc entries instead of leaving them empty, just like Partner Ledger. - In Partner Ledger, Aged Payable and Aged Receivable, rename 'Invoice Date' column to 'Date', because former will be appropriate for invoice but in case of payment or misc entry, we'll show payment date or JE date. So more generic column name 'Date' is more appropriate. - In tests setup, move for month 11th is created before 10th and no invoice_date was being set, so in report invoice_date was empty, and in tests where we have sort by invoice_date ASC and where nothing is given report sorted it with creation date ASC , because invoice_date was missing. But now when we have added fallback for invoice_date, the report is using that Date column for sorting and therefore tests are modified to show 10th month entries before 11th. [task- 6467119](https://www.odoo.com/odoo/project/967/tasks/6467119)
The payroll employee card now shows the wage from the employee's contract for the relevant payslip period, rather than relying on the payslip value. This gives payroll users a more accurate historical view and correctly presents hourly wages without converting them to a monthly format.
Original PR description
The wage displayed in the payroll information was coming from the payslip itself instead of the employee's contract version at that specific period. And if the employee had an hourly wage, it was forced into a monthly format. This fetches the historical wage directly from the contract version linked to the payslip period and fixes the hourly display. task-6361600
Mexican payroll users can now refresh SAT status directly from a payslip. The change also ensures payslip tax documents are included in automatic SAT status checks, so validated CFDIs no longer remain shown as undefined in Odoo.
Original PR description
Add a new button to update SAT info on MX payslips. target: master task-6231563
This fixes an issue where the same contact assigned to sign multiple times could be prompted to sign again before other required signers had completed their turn. Signing requests now only show the next document to a user when it is actually their turn, helping businesses keep approval flows in the intended order.
Original PR description
### Steps to Reproduce: 1. Create a sign request and have 3 total signers (User, Customer, Employee) 2. Enable Signing Order and make the order as follows: (1) User, (2) Customer, (3) Employee But…
### Steps to Reproduce: 1. Create a sign request and have 3 total signers (User, Customer, Employee) 2. Enable Signing Order and make the order as follows: (1) User, (2) Customer, (3) Employee But make the User and Employee the same contact 3. Send and sign the request > Notice that (1) is able to sign for (3) immediately after, (2) has not signed yet. ### Description of the issue/feature this PR addresses: **Issue:** The signing order is ignored when the same user has to sign multiple times on a document, even if it is configured for a different person to sign in between. This happens because all signature request items are initialized in the 'sent' state upon creation, rather than strictly advancing based on the order. As a result, the system prematurely allows users to sign out of order and prompts them with their next turn too early. **Solution:** To resolve this, the controller was updated to include an `is_mail_sent = True` domain filter. This ensures that the UI's post-sign popup only displays documents where it is explicitly the user's active turn, rather than prompting a premature sign. ### Current behavior before PR: Users are able to sign prematurely, and the system will disregard the configured signing order. ### Desired behavior after PR: Users will only be prompted and able to sign a document when it is explicitly their turn, per the `mail_sent_order`. This way, documents are signed in order. opw-6417327 Forward-Port-Of: odoo/enterprise#128487 Forward-Port-Of: odoo/enterprise#125573
Businesses using only Point of Sale can now access the product variants setting from their POS configuration. This fixes a missing option that prevented OAF plan users from enabling product variants when no other related apps were installed.
Original PR description
If only PoS is installed (if you are on the OAF plan). The variants settings is unavailable and cannot be activated. Steps to reproduce: ------------------- * Install only PoS * Look for variant in settings > Observation: The option is not showing up Why the fix: ------------ The setting is just a copy of the other places where the settings is available. opw-6378568 Forward-Port-Of: odoo/odoo#276179
This update fixes an issue that could cause certain employee searches to fail or return incorrect results for users without access to private employee data. It helps ensure HR search behavior remains reliable while preserving existing access restrictions.
Original PR description
The hack to search fields as a user that has no access to private employee data and searching on the `current_version_id` instead of the provided field since we force to wrap searchable fields domains in a Query in odoo/odoo#280373. The hack did not support usage of the 'any!' operator on `current_version_id` leading to a query like: "hr_employee.id in (select id from hr_version ...)". task-6468820 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284271 Forward-Port-Of: odoo/odoo#283811
A test that simulates buying a rental product on the website has been temporarily disabled because the related rental planning flow is still changing. This avoids repeated false failures while the final process is being settled, with no direct change to customer-facing features.
Original PR description
Given the rapid changes in spec for `{website_}sale_renting_planning` it doesn't make sense to fix the tour only for the flow to break right away after. Therefore, the tour is temporarily disabled until the flow of the module(s) is finalized.
task-6389324
Forward-Port-Of: odoo/enterprise#128170This fixes a display problem that could stop contact or user avatars from loading when their last update date was missing. Users should see a more reliable interface in forms and kanban views instead of encountering an error.
Original PR description
Issue: The `Many2OneAvatarField` and `KanbanMany2OneAvatarField` templates were directly calling `value.write_date?.toMillis()`. Optional chaining does not handle the case where `write_date` is `false`, resulting in a `TypeError` because `toMillis()` is not available on a boolean value. Solution: Added a `uniqueId` getter in both `Many2OneAvatarField` and `KanbanMany2OneAvatarField` to safely handle a missing or false `write_date`. The getter calls `toMillis()` only when `write_date` is available and returns `undefined` otherwise. Both templates now use `uniqueId` for the avatar URL. opw-6464172 Forward-Port-Of: odoo/odoo#284015 Forward-Port-Of: odoo/odoo#282659
The Inventory Valuation report now includes accounting differences for products that currently have no stock on hand. This helps businesses spot and correct unbalanced valuation or variation accounts without losing the performance benefit of skipping full value calculations for zero-quantity products.
Original PR description
## Problem If multiple valuation/variation accounts are used, the Inventory Valuation report will not show the balance of accounts attached to products that have 0 quantity available if a different…
## Problem If multiple valuation/variation accounts are used, the Inventory Valuation report will not show the balance of accounts attached to products that have 0 quantity available if a different account has quantity. ## Solution In order to maintain the performance improvements intended by the commit that introduced the `qty_available != 0` filter, we will avoid calculating `total_value` for products with 0 quantity. We will still run `stock_accounting_value` on these products in order to capture interim accounting value on the Inventory Valuation report. ## Steps to Reproduce (Runbot v19) (defer to the test for more info) 1. Create an extra set of valuation/variation accounts 2. Create a product, avco perpetual accounting the default valuation/variation accounts 3. Create a second product, avco perpetual accounting the new valuation/variation accounts 4. Purchase 1 unit of each of the products and receive, bill both 5. Sell 1 unit of the product attached to the new valuation/variation 6. Go to Accounting > Review > Inventory Valuation, and note that the new valuation/variation accounts are not present. If you click Generate Entry, you will see that these accounts need to be balanced opw-6473319 Forward-Port-Of: odoo/odoo#283787 Forward-Port-Of: odoo/odoo#282819
This fixes an issue where users could not create custom fields from the technical settings after installing AI Fields. The system now ignores an empty model value submitted by the form, preventing an unnecessary validation error and restoring the expected setup flow.
Original PR description
**Steps to reproduce** - Install `ai_fields` - In debug mode, go to Settings > Technical > Fields - Trying to create any field results in a `Validation Error` **Cause** Commit ebeab340264af42450b74a76d69fc284b600f94c introduced a check on `model` to prevent a mismatch. `ai_studio` adds the `model` as an invisible field to the form, sending a `False` value to the `create`. https://github.com/odoo/enterprise/blob/bafa674d287577a0f32e08af165dd9561b1667c0/ai_fields/views/ir_model_views.xml#L39 **Change** Ignore `model` if it is `False` opw-6463182 Forward-Port-Of: odoo/odoo#283018
This fix restores the ability to delegate Studio approval responsibilities as intended. It helps teams keep approval workflows moving when the original approver is unavailable or responsibilities need to be reassigned.
Original PR description
opw-6321766 Forward-Port-Of: odoo/enterprise#128666 Forward-Port-Of: odoo/enterprise#122441
This fixes an error that could stop customers from generating batch payments. The payment file creation process now uses the correct address-cleaning logic, helping users complete payments without disruption.
Original PR description
The aim of this commit is to allow customer to make their batch payment without facing a Traceback. Context: odoo/enterprise@35f5341b9b44cc16eaea311295a064189dd802cb introduced bug during a badly…
The aim of this commit is to allow customer to make their batch payment
without facing a Traceback.
Context:
odoo/enterprise@35f5341b9b44cc16eaea311295a064189dd802cb introduced bug
during a badly handled forward port.
The method was removed in saas-18.3 in favor of a function. The forward-port
was half handled and now surfaces to Odoo's own production.
Generating a batch payment could generates the following Traceback:
```py
File "/home/odoo/src/enterprise/saas-19.3/account_iso20022/models/account_journal_sepa_ct.py", line 69, in _get_PstlAdr
return super()._get_PstlAdr(partner_id, payment_method_code)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/enterprise/saas-19.3/account_iso20022/models/account_journal.py", line 501, in _get_PstlAdr
CtrySubDvsn.text = self._sepa_sanitize_communication(partner_address['state'][:35])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'account.journal' object has no attribute '_sepa_sanitize_communication'
```
Task-id: None (internal issue)
Forward-Port-Of: odoo/enterprise#129084
Forward-Port-Of: odoo/enterprise#129006This fix prevents discounts from being counted twice when calculating withholding tax amounts. Businesses using withholding taxes will see more accurate tax totals on affected accounting documents.
Original PR description
When calculating tax details for `withholding_total_amount_currency`, the discount was already applied to `price_unit` before passing it to `_add_tax_details_in_base_line`. However, `_add_tax_details_in_base_line` already applies the discount itself. As a result, the discount was applied twice, causing the withholding amount to be calculated incorrectly. In this commit, remove the initial discount calculation and pass the original price_unit so that the discount is applied only once. ref- https://github.com/odoo/odoo/blob/master/addons/account/models/account_tax.py#L1804C9-L1804C34 task-6471414 Forward-Port-Of: odoo/odoo#282419
The Sign app no longer replaces existing rules that control when the Mark Done button is shown in the activity scheduling wizard. This prevents the button from appearing in inappropriate cases and avoids conflicts with other apps that also manage its visibility.
Original PR description
Previously, the sign module was completely overwriting the `invisible` attribute on the "Mark Done" (`action_schedule_activities_done`) button in the activity schedule wizard. This inadvertently discarded base conditions (such as hiding the button when `has_error` is true) and caused conflicts with other modules (like `calendar`) that also need to modify this button's visibility. This commit updates the view inheritance to safely append the sign condition using `add` and `separator="or"`, preserving all existing visibility rules. Task-6499751 Forward-Port-Of: odoo/enterprise#129005
The accounting reports date filter now keeps the correct fiscal year when users switch between companies with different fiscal year calendars. This prevents reports from showing incorrect date ranges, helping businesses compare and review financial data more reliably.
Original PR description
Fix year-mode date filter when switching between companies with different fiscal years With two companies configured: one using a standard fiscal year and one using an offset fiscal year, switching between them could produce incorrect date ranges. This happened because the previous company’s `date_to` value was reused to compute the current period for the newly selected company, and vice versa. The fix is to use the `date_to` year instead and select the latest fiscal year ending in that same year. Forward-Port-Of: odoo/enterprise#127945 Forward-Port-Of: odoo/enterprise#117603
This fix keeps accountant knowledge PDF navigation working across newer and older PyPDF versions. It reduces the risk of errors when generating or processing PDF documents after library updates.
Original PR description
On latest versions of PyPDF, the `outlines` attribute was renamed to `outline`, we now handle both attributes to provide compatibility with both versions. A similar issue arises with the `addLink` method, which was renamed to `add_link`, and later replaced by `add_annotation`. Another similar issue arises with the `add_bookmark` method, which was replaced by `add_outline_item`. task-6474715 version-19.4 Forward-Port-Of: odoo/enterprise#127074
Point of sale orders using online payments now validate correctly after items are added or changed. The system updates the order total before checking payment status, preventing incorrect payment errors and reducing checkout disruption.
Original PR description
When products were added after selecting an online payment method and going back to the floor plan, the subsequent validation failed with "Invalid online payments" because the server's amount_unpaid was based on the old order total. Fix: sync the order to the server before querying amount_unpaid, so the server always has the latest total when checkRemainingOnlinePaymentLines is called. Also guard cancelPayment against calling the payment terminal interface on online payment methods that do not use one. task-id: 6330704 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The point of sale customer display now updates more consistently during checkout, ensuring shoppers see the latest order information. The change also reduces unnecessary refreshes and communication behind the scenes, improving responsiveness and stability.
Original PR description
Fixes customer display updates by moving the data format and preparation synchronously inside the useEffect hook. This allows OWL 3 to correctly register reactive dependencies, while only the final dispatch remains debounced (100ms). Optimizations: - Persist CustomerDisplayPosAdapter on Chrome. - Add JSON serialization diffing before dispatch to skip redundant RPC/broadcasts. - Use recursive deepUpdate in-place to avoid complete DOM re-renders. task-id: 6292603 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The image editor toolbar now shows the Wrap text icon correctly when users adjust image alignment. This removes a small visual glitch that could make the option look missing or unavailable.
Original PR description
### Steps to reproduce: - Insert an image in the HTML Editor (e.g. via /media). - Select the image and click on the alignment dropdown option in the toolbar. - Observe that the `Wrap text` icon is invisible. ### Purpose of this PR: - The `Wrap text` icon was set to `text_wrap`, which is an invalid Material Symbols icon name and was missing from the font subset. This PR fixes the issue by updating the icon to `format_image_left` in `image_plugin.js` and `oi_to_ms.scss`, adding `format_image_left` to `icons_wishlist.txt`, and regenerating the icon font subset. task-6448292 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fixes a website builder issue where connection shapes could switch to black or white when adjacent sections used the same background color plus an image or video. Shapes now keep the intended matching color, making page designs more consistent for editors and visitors.
Original PR description
When the user has not set a specific color to a connection shape, and changes the background color of the connected block, the builder automatically updates the color of the shape to match. There is an exception if the color of the shape is the same as the one of its block (in that case it uses a contrasting color). This commit removes the exception if the block also has a image background or a video background (because those will cover the color background once loaded, so that background does not really count) Steps to reproduce: - Open website builder - Drop a block - Set a color as "Background" - Set an image as "Background" (or a video) - Click on the "Shape" option and select a one in "Connections" - Drop a second block - Set the same color as "Background" - Bug: the shape get a contrasting color (black or white) instead of the background color of the neighbor task-6483027
This fixes a small styling issue in the HTML editor’s link popup by removing an invalid layout setting. The popup should continue to behave as before, with cleaner underlying code and reduced risk of browser styling quirks.
Original PR description
This PR aims to remove the unnecessary max-width property having invalid unit value from `link_popover.xml`, as the existing behavior is correct without it. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Point of Sale receipts now show the correct cash rounding line even when a customer pays more than the order total. This improves receipt accuracy and keeps payment totals consistent for cash transactions that require rounding.
Original PR description
**Steps to reproduce:** - Make a rounding method, Nearest and 0.05 of rounding - Make a product that costs $4.99, don't set a tax - Go to the PoS - Order the product - Before paying click the +10…
**Steps to reproduce:** - Make a rounding method, Nearest and 0.05 of rounding - Make a product that costs $4.99, don't set a tax - Go to the PoS - Order the product - Before paying click the +10 button, then pay - The rounding line is not present on the ticket **Why the fix:** When making a normal rounded purchase, by just clicking the "Cash" button, the rounding line will be displayed. This is because we do not try to apply the rounding if the rounding of the remaining is not equal to zero. https://github.com/odoo/odoo/blob/995629db3231de944710751c3184bf1b8b1355c7/addons/point_of_sale/static/src/app/models/accounting/pos_order_accounting.js#L118-L123 When we try to over pay, the remaining will be negative by the amount we overpay, so the amount will be set to zero, and the rounding will not be set. We now take the amount we overpay into account, and deduct it from the amount we paid to then correctly compute the remaining amount without having to deal with the amount overpaid. Some tests were not taking the rounding as it was not working correctly, so it has now been changed now that it works as it should. opw-6025807 Forward-Port-Of: odoo/odoo#283413 Forward-Port-Of: odoo/odoo#256117
The website link tracking page no longer shows SEO optimization, page properties, or link tracker menu actions that are not useful for visitor-facing content. This reduces confusion for website editors and keeps management options focused on pages where they add value.
Original PR description
Since [this commit][1] you're able to optimize the link tracker page using "optimize seo." This makes no sense as it contains no useful content for visitors to the website. Access to the action is now disabled when the current page is the link tracking page. The page properties and link tracker menu items have also been removed for similar reasons. [1]: https://github.com/odoo/odoo/commit/ac55f2bb113ecf7c774fe6e96d28e716184a97d1 Task-6288891 Forward-Port-Of: odoo/odoo#283954 Forward-Port-Of: odoo/odoo#278132
Project margin reports now correctly exclude company-paid expense payment lines from revenue totals. This prevents expenses from appearing as positive “Other Revenues,” giving teams a more accurate view of actual project profitability.
Original PR description
Steps to reproduce --- 1. Install Sales (with Margins) and Project, and open a billable project linked to a sale order. 2. From the project's Expenses view, create an expense paid by the Company and…
Steps to reproduce --- 1. Install Sales (with Margins) and Project, and open a billable project linked to a sale order. 2. From the project's Expenses view, create an expense paid by the Company and post it. 3. Open the project's Actual Margins: the expense shows under Other Revenues as a positive amount. Issue --- Creating an expense from a project's Expenses view keeps `project_id` in the context until its journal entry is built. For a company-paid expense that entry is a payment, and `AccountMoveLine._compute_analytic_distribution` puts the project's analytic distribution on every line that is not receivable or payable, which also covers the Outstanding Payments liquidity line. An analytic amount is the opposite of the move line balance, so the negative liquidity balance becomes a positive analytic line, classified as Other Revenues, on top of the real cost already booked on the expense account. The receivable/payable filter from b6200026ecf1 narrowed the override introduced in ac1995ad6ddf but ignored the liquidity counterpart of a payment; since only profit and loss lines make up a project margin, restricting the distribution to `income` and `expense` accounts keeps the settlement line out of the report. https://github.com/odoo/odoo/blob/f037dead17eb6a74d1ea9c56b0861b285be17516/addons/sale_project/models/account_move_line.py#L10-L19 opw-6326551 Forward-Port-Of: odoo/odoo#283938 Forward-Port-Of: odoo/odoo#273273
When users insert dynamic fields in the HTML editor, related fields now default to a readable display name instead of a technical ID. This makes generated content clearer while still allowing users to choose the ID field when needed.
Original PR description
*: project Before this commit: when clicking a field having sub fields (canFollowRelationFor is true), we just return this field's id, which is not very useful in most cases. After this commit: We created subclass of DynamicPlaceholderPopover, EditorDynamicPlaceholderPopover, which uses EditorModelFieldSelectorPopover. We use the display name of the followable field by default and if the user really want the id, they may choose the id subfield. We also show the followable field's name as the default placeholder instead of "Display name". task-6265223 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283748 Forward-Port-Of: odoo/odoo#272129
When the cookie banner remains enabled, clearing the cookie policy page now restores the default policy page instead of leaving the website without one. This prevents administrators from losing access to the setting and helps keep cookie-related website configuration consistent.
Original PR description
Steps to reproduce: - enable the cookies bar in the website settings and save - clear the "Cookie Policy Page" field and save The website was left without a cookie policy page while the cookies bar was still enabled. Since the settings view only displays the field when it has a value, it disappeared with no way to set it back, other than toggling the cookies bar off and on again. The default policy page was only restored when the `cookies_bar` flag itself changed, so a write clearing only `cookie_policy_id` slipped through. Restore the default page whenever the policy is emptied while the cookies bar remains enabled, so the field reappears with the default page after saving. task-6356766 Forward-Port-Of: odoo/odoo#273901
This update fixes an issue where selecting a suggested word on mobile with Gboard could place the replacement text incorrectly and delete only part of the original word. It improves text editing reliability for users working in Odoo's HTML editor on mobile devices.
Original PR description
Before this commit: on mobile, when typing using Gboard and select a word suggestion will only delete the last character and put the new word at the beginning of the word to be replaced. This is because Gboard extends the selection to the text to be corrected, then deletes it, and inserts the corrected text. This flow falls in our previous fix for MS Swiftkey's delete backward, and wrongly uses cached old selection instead of using extended new selection from Gboard. After this commit: We strict the Swiftkey fix further, and only execute it when the cursor is at the beginning of the p element. Related commit: https://github.com/odoo/odoo/commit/822fd4e8fec7e114e6748dd8c9b4969f423fb290 task-6233756 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#278266
The web interface now handles invalid filters on relational properties without crashing the editor. Users can still open the property settings to correct or remove the problematic filter, preventing them from being locked out of editing or deleting the property.
Original PR description
When a relational property has a domain the server cannot evaluate, opening its definition editor crashes. The editor calls search_count on that domain to show how many records match, both when it…
When a relational property has a domain the server cannot evaluate, opening its definition editor crashes. The editor calls search_count on that domain to show how many records match, both when it opens and on every later render. The server raises a ValueError and the call has no error handling, so the whole editor goes down. The bad domain stays saved on the property, so reopening the editor fails the same way and the property can no longer be edited or deleted. Wrap the search_count call in _updateMatchingRecordsCount (property_definition.js) in a try/catch and show no count when it fails. This is the only place the editor counts matching records, so guarding it here handles a bad domain from any source, the field selector or the code editor. The field selector still shows its warning on the invalid path, so the user can fix or delete the property. Steps to reproduce: 1. On a model that has a Properties field, add a Many2one property and set its Model to a model that itself has a Properties field. 2. Open the property Domain and click New Rule. 3. In the field selector pick the Properties entry, then close the selector. => An error dialog appears and the property can no longer be edited or deleted. Ticket [link](https://www.odoo.com/odoo/project.task/6101311) opw-6101311 Forward-Port-Of: odoo/odoo#283802 Forward-Port-Of: odoo/odoo#259886
This fix prevents Accounting payment terms from crashing when a user enters zero or negative day values for end-of-month due dates. Instead of showing a technical error, the system can now handle the calculation safely and show the appropriate validation message when the record is saved.
Original PR description
Steps to reproduce: - Install `Accounting` module - Payment Terms > Create NEW - Add a new Due Term line with "Days end of month on the" and a negative amount of days(eg: -1) Traceback: `ValueError: day is out of range for month` When `days_next_month` is set to a negative value, it is passed directly to `relativedelta` as the 'day' value. Since a negative value is not a valid day of the month, the due-date computation raises a `ValueError`. Use the end of the month for the calculation when `days_next_month` is non-positive. This prevents the traceback while computing the payment term and allows the proper validation error to be raised when the record is saved. opw-6453640 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283865 Forward-Port-Of: odoo/odoo#281904
This fix ensures Odoo correctly refreshes whether taxes and related accounting rules are marked as used when invoices, expenses, purchases, or point-of-sale records change. This helps prevent outdated tax status information from appearing in accounting-related workflows.
Original PR description
Currently, `is_used` is computed using queries on `account.move.line`, `account.reconcile.model.line`, etc. As a result, it has no depends and is not automatically updated when records in either model are created, modified, or deleted. This commit reverse M2M fields for respective models and use it as dependency to `_compute_is_used`. It also adds a missing dependency of `is_used` to `_compute_repartition_lines_str`. Forward-Port-Of: odoo/odoo#283406
This fix prevents Belgian Intrastat reporting from crashing in rare cases where company data is accessed without the expected permissions. It mainly protects future customizations or edge-case setups, with no expected change for normal day-to-day use.
Original PR description
Due to some trouble with tests, we found that in some cases, this function is called on the root company, and if the user does not have the access rights to read data from the company (users with system rights have them by default), it will cause a crash. This situation is not possible with the standard UI, but we fix it in case it becomes possible in a future version or customization. Forward-Port-Of: odoo/enterprise#128212
Mentions in Odoo Mail now choose an active user account for each recipient instead of potentially selecting an archived account. This ensures mentioned people receive their inbox notifications reliably when they have both archived and active user records.
Original PR description
Before this commit, mentioning a partner that has an archived user sent the inbox notification to that archived user, so the mentioned person never saw the mention. This happens because the query picking the user of a recipient joins res_users without filtering on active, and keeps one row per partner with DISTINCT ON and no ORDER BY, so which row survives is arbitrary. One solution could have been to keep every active user of the partner, which is what we want as each of them has its own notification type, but a notification is stored per partner, so the type of a single user applies to all of them. Picking one user is a current limitation. This commit fixes the issue by taking the first active user of each partner in a lateral join, ordered as mail.followers._get_recipient_data already does: internal users first, then the lowest id. Forward-Port-Of: odoo/odoo#284214 Forward-Port-Of: odoo/odoo#283806
Aged Receivables and Aged Payables now calculate aging periods correctly when horizontal groups are applied. This prevents incorrect amounts from appearing in older-period columns, improving reliability of customer and vendor aging reports.
Original PR description
Problem: When using horizontal groups in Aged Receivables / Aged Payables reports, the amounts shown in the Older periods are incorrect. Steps to reproduce: 1. Activate debug mode 2. Go to Accounting…
Problem: When using horizontal groups in Aged Receivables / Aged Payables reports, the amounts shown in the Older periods are incorrect. Steps to reproduce: 1. Activate debug mode 2. Go to Accounting > Configuration > Horizontal Groups 3. Add a new horizontal group that results in at least 2 groups 4. Go to Accounting > Reporting > Aged Receivables / Aged Payables 5. Apply the horizontal group created 6. Notice how the amount in the Older period is incorrect, different from before applying the horizontal group. (It may be coincidentally correct, you can check by applying different aging intervals until you find one that shows the issue) Cause: The periods were not correctly calculated. The number of periods was calculated based on the number of period columns, without taking into account the number of column groups. When using horizontal groups, period columns are duplicated for each group that exists after applying the horziontal group. This is not considered when calculating the number of periods, which results in calculating too many periods and therefore having incorrect durations for each period. opw-6374639 Forward-Port-Of: odoo/enterprise#127570
This fixes monthly auto-planning so work slots can be scheduled through the last working day of the selected month. It prevents sales planning from under-scheduling hours due to timezone conversion issues, improving planning accuracy for service orders.
Original PR description
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To…
Steps to reproduce: --------------------------- 1. Install `sale_planning` with demo data. 2. Create a SO with a planning product, set the quantity to 100 hours, and confirm the SO. 3. Click the "To Plan" button, then click "Auto Plan". 4. Make sure the "Month" filter is selected in the scale options and observe the planned slots. Issue: -------- When auto planning slots for a month, the last day of the month is excluded. For example, slots are scheduled only until July 30th, even though July 31st is a working day. Cause: -------- While preparing the context, `stopDate` is set to July 31st at 00:00. It is then passed to [serializeDateTime()](https://github.com/odoo/odoo/blob/dacaad91bba8f959daf5d89a046c5a1c11e48eec/addons/web/static/src/core/l10n/dates.js#L553-L560), which converts the datetime to UTC. Depending on the user's timezone, this can shift the date to the previous day, causing the last day of the month to be excluded. Solution: ------------ Use `localEndOf()` to set `stopDate` to the local end of the selected range before passing it to `serializeDateTime()`. This ensures the last day of the month is preserved during UTC conversion. **NOTE:** Forward-port the solution from the 18.0 version, which was adapted to the publish shift use case in 18.3 and introduced this issue. Add a HOOT test case to prevent this regression in future versions. References: [18](https://github.com/odoo/enterprise/commit/bc3db24f83473d5646f7c2cfca8ed1c5b064ea2e) and [saas-18.3](https://github.com/odoo/enterprise/commit/c81fba31780869940f726b695ad46a87f69798fb) opw-6391495 Forward-Port-Of: odoo/enterprise#128775 Forward-Port-Of: odoo/enterprise#127950
The AI livechat snippet now has a preview image again, so it displays correctly before the feature is installed. This helps website editors recognize and choose the livechat block more easily in the website builder.
Original PR description
Problem: 1) The preview image of the livechat snippet was removed in this [commit][1] and wasn't replaced with another image. As a result, the uninstalled livechat snippet doesn't display properly in the website builder. Solutions: 1) An image has been added `ai_livechat.png` which is shown on preview Note: This fix will change in master to be up to date with current website snippet previews. The location will be moved to `snippet_previews` and the file type will be changed to `.webp` [1]: https://github.com/odoo/enterprise/commit/df05441e469157890253b5550b5f8735723b28fb Task-5248712 Forward-Port-Of: odoo/enterprise#126361
Mexican point-of-sale global invoices now ignore cancelled refund orders when calculating refund amounts. This prevents valid invoices from failing when a cancelled refund exists alongside a paid refund.
Original PR description
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click…
Steps to reproduce: ------------------- 1. Install `l10n_mx_edi_pos` and set the company to Mexico. 2. In PoS, make an order with one product and pay it. 3. Open the order in the backend, click "Return Products" to make a refund, but don't pay it, cancel it instead. 4. From the same order, click "Return Products" again to make a second refund, and pay it normally. 5. Go to the orders list, select the main order and the paid refund (not the cancelled one), then Actions > Create Global Invoice. -> Observation: error in the global invoice. In the CFDI tab of the main order the line is "Send Global In Error", and hovering on it the detail says "Failed to distribute some negative lines". Why: ---- When we make the global invoice, we remove the refunds from the order. A cancelled refund was never paid, so we should not count it. But we were counting it too. So we removed the refund amount twice in our case, one for the paid refund, and one for the cancelled one, and we end up with an order with negative amount that cannot be distributed. The fix: -------- We now skip the cancelled orders when we search the refunds, the same way it is done above when we collect the refunded orders. opw-6261404 Forward-Port-Of: odoo/enterprise#128026 Forward-Port-Of: odoo/enterprise#120996
This fix restores the default grouping behavior in Gantt views after it was unintentionally removed. Business users will see planned work grouped as expected again, reducing confusion when reviewing schedules and resource planning.
Original PR description
This reverts commit d35390a534fc60354a3c0174d6aa7cd158805f63 (#117918) task-6425644 Forward-Port-Of: odoo/enterprise#125983
This fix updates a payroll test so it checks only rows containing payroll data, instead of counting automatically added blank rows. This prevents false test failures and helps keep payroll release validation stable.
Original PR description
Issue: The original trigger was searching for 2 table rows, when it enforces 4 with added empty rows. The [getEmptyRowIds](https://github.com/odoo/odoo/blob/33dc65bbac165f33030ad3da59ea785b69482b3f/addons/web/static/src/views/list/list_renderer.js#L1104-L1110) enforces max of 4 rows. The condtional (one up the stack) !ctx["this"].props.list.isGrouped&&!ctx["this"].props.noContentHelp returns true, and it adds empty rows. Fix: Since this enforces 4 rows with empty rows we check the rows that have data instead of how many rows are added. Because anything less than or equal to 4 but greater than 0 records it will always be 4 table rows while the conditional above returns true . opw-6349513 <img width="1337" height="674" alt="Screenshot 2026-07-15 at 4 53 51 PM" src="https://github.com/user-attachments/assets/76f53521-ec9f-4807-9083-95533904b5de" /> Forward-Port-Of: odoo/enterprise#127531 Forward-Port-Of: odoo/enterprise#125091
Generated PDFs now render Arabic, Persian, and Hebrew text more reliably, including when mixed with English words or numbers. This improves readability of business documents for users working in right-to-left languages while keeping a safe fallback if the supporting library is unavailable.
Original PR description
# Summary Fixes incorrect rendering of Arabic/Persian/Hebrew (RTL) text in generated PDFs, especially when mixed with Latin digits or English (LTR). Uses python-bidi to compute display order and…
# Summary Fixes incorrect rendering of Arabic/Persian/Hebrew (RTL) text in generated PDFs, especially when mixed with Latin digits or English (LTR). Uses python-bidi to compute display order and introduces a safe fallback when that dependency isn’t available. # Problem - RTL strings were displayed in reverse order in PDFs. - Mixed content (e.g., Arabic + numbers/English) produced garbled ordering. - Existing logic flipped text only when the first character was RTL and no LTR chars existed, which broke mixed cases. # Solution python-bidi follows the Unicode Bidirectional Algorithm and correctly handles real-world mixed strings (Arabic + Latin + numbers + punctuation). Fallback ensures no hard runtime breakage on instances where new dependencies is missing. If python-bidi is missing, behavior is similar to previous “hotfix”: - Pure RTL: reverse text for legible display. - Mixed RTL/LTR: don’t reverse (favor readable English segments over fully correct RTL order). --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The mass mailing feature now correctly excludes temporary wizard screens from the list of models that can be used for mailings. This prevents inappropriate or unusable system objects from appearing as mailing targets, improving reliability and reducing confusion.
Original PR description
The search function ` _search_is_mailing_enabled` mistakenly used `model.is_transient()` (where the model is the `ir.model` record itself) to filter the transient models, which always returns `False` since `ir.model` is a regular persistent model. As a result, transient models (wizards) were never filtered out. This commit fixes it by using`self.env[model.model].is_transient()` to call `is_transient` on the actual model. Task-6458883 Forward-Port-Of: odoo/odoo#283781 Forward-Port-Of: odoo/odoo#282783
The HTML editor now places the drag-and-drop move handle on the correct side when users work in right-to-left languages. This improves editing usability and visual consistency for languages such as Arabic or Hebrew.
Original PR description
Problem: In MoveNodePlugin, `setMovableElement` sets the position of the drag-and-drop handler without considering `this.config.direction === "rtl"`. The handler is placed on the left side regardless of text direction. Solution: - In RTL mode, calculate the handle position from the right edge of the element so it is placed on the right side with the same distance as in LTR. - Update hover hooks, editable bounds, and dropzone rectangles for RTL mode. Steps to reproduce: 1. Open the editor in RTL mode. 2. Hover over a movable block element (e.g. `<p>`). => The move handle appears on the left side of the element instead of the right. task-6442717 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284190 Forward-Port-Of: odoo/odoo#281249
The HTML editor now keeps background colors when users turn colored text into lists or turn list items back into regular paragraphs. This prevents formatting from unexpectedly disappearing and makes document editing more consistent.
Original PR description
Problem: Background color was lost both when converting text with a background color into a list item and when converting a list item with a background color back into a paragraph. Cause: - `insertListAfter` only copied `color` from the font wrapper to `li.style.color`, ignoring `background-color`. - Unwrapping a list item (`ListPlugin`) extracted `color`, `font-size`, and `text-align`, but ignored `backgroundColor`. Solution: - Preserve `background-color` from font wrapper onto `li.style.backgroundColor` when creating a list. - Restore `li.style.backgroundColor` onto a `<font>` wrapper when unwrapping a list item. Steps to reproduce: - Apply background color to a paragraph and toggle list -> background color is lost. - Apply background color to a list item and toggle list off -> background color is lost. opw-6481665 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283158
The ecommerce product comparison page now shows the product tags table only when at least one tag is visible to shoppers. This prevents customers from seeing an empty specifications section when products only have hidden tags.
Original PR description
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the…
The product specifications table is displayed whenever the product has tags, even if none of them are visible on the ecommerce website. The product tags template filters out non-visible tags, but the surrounding table remains rendered and appears empty. Only display the tags table when at least one tag is visible on ecommerce. @Tecnativa TT63855 **Description of the issue/feature this PR addresses:** The condition used to display the product tags table considers all tags associated with the product, including those that are not visible on ecommerce. **Current behavior before PR:** When a product only has non-visible tags, the tags table is displayed without any content. <img width="669" height="350" alt="image" src="https://github.com/user-attachments/assets/4758ea76-5186-4035-a065-aa4c71ce7053" /> **Desired behavior after PR is merged:** The tags table is only displayed when the product has at least one tag visible on ecommerce. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282348 Forward-Port-Of: odoo/odoo#278604
This fixes an issue where Australian tax reporting data could fail if the optional Australian reports module was not installed. The change keeps report calculation logic aligned with the module that provides it, improving reliability for Australian localization setups.
Original PR description
We had a case where l10n_au and account_reports were installed together, but not l10n_au_reports (manually uninstalled ?). Since the custom engine was used on expressions in l10n_au, this failed. Custom engines should always be used and declared within the same module (or a submodule of the one defining the handler) to avoid such issues. opw-6451274 Forward-Port-Of: odoo/odoo#282790
This fix ensures that approved leave properly clears negative extra hours created automatically for missing attendance. Employees and HR teams will see more accurate overtime balances when leave is added for a day that was previously marked as an absence.
Original PR description
Before this commit: --- When [`absence_management`](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/res_company.py#L42) is enabled, a [scheduled…
Before this commit:
---
When [`absence_management`](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/res_company.py#L42) is enabled, a [scheduled action](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/hr_attendance.py#L645) automatically creates an attendance record at [**12:00:00 AM**](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_attendance/models/hr_attendance.py#L649) to mark negative extra hours for employees with missing attendance.
<img width="1147" height="474" alt="image" src="https://github.com/user-attachments/assets/833a4387-bc20-4bb7-817d-9ebe9afa7d71" />
If an employee later creates a leave covering this autogenerated attendance, the extra hours should be reset to `0`. However, this does not happen.
#### Video demonstration:
https://drive.google.com/file/d/1DTNQuQ3uV5nOUVMBazCDo0hBZbKJZUIW/view
This happens because the [domain](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_holidays_attendance/models/resource_calendar_leaves.py#L8) used to fetch attendances for [`_update_overtime`](https://github.com/odoo/odoo/blob/b3444e4bd421a30229b0dc7c8d5eb6c78b8b51ed/addons/hr_holidays_attendance/models/resource_calendar_leaves.py#L34) compares the attendance `check_in` and `check_out` datetimes with the leave `date_from` and `date_to` datetimes.
The leave datetimes are aligned with the employee's working schedule. For example, if the working hours are **8:00 AM–5:00 PM**, the leave is stored from `{date, 8:00 AM}` to `{date, 5:00 PM}`. In contrast, the scheduled action creates the autogenerated absence attendance at **12:00:00 AM** (in the user's timezone). Since this attendance falls outside the leave datetime range, it is excluded from the domain, and `_update_overtime` is never called for it.
After this fix:
---
Instead of building the domain using the leave datetime range, the domain is built using the leave date range. This ensures that all attendances for the affected dates, including autogenerated absence attendances created at midnight, are included and their extra hours are updated correctly.
OPW: 6385811
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#281123Odoo Studio now handles cases where fields shown on a form are generated behind the scenes and cannot be matched during view optimization. This prevents crashes when users edit affected accounting and Dutch reporting views, falling back safely instead.
Original PR description
* = account_invoice_extract, l10n_nl_reports Example of steps: - Install web_studio and `accountant` - Open the corresponding form view in Studio - With debug mode toggle "Show invisible Elements"…
* = account_invoice_extract, l10n_nl_reports Example of steps: - Install web_studio and `accountant` - Open the corresponding form view in Studio - With debug mode toggle "Show invisible Elements" and edit "Invisible" on the second partner_id field - Traceback `normalize()` compares the combined arch without the studio customization to the one with it, in order to compute the smallest possible set of xpaths. To do so, it calls `apply_inheritance_specs` (the low-level function from `odoo.tools.template_inheritance`) directly on the statically combined arch. Some models add or duplicate nodes dynamically in `_get_view()` (Python postprocessing, run after the static view combination). A studio operation can target such a node, since it is what the user actually sees and clicks on. But that node has no counterpart in the purely static combined arch used by `normalize()`, so `apply_inheritance_specs` raises a ValueError. `edit_view()` only catches `ValidationError` to fall back to an un-optimized (but valid) studio arch instead of failing the request. Since the low-level function raises a plain `ValueError` here, that fallback never triggers, and the exception is not caught anywhere. To fix this, we will keep the behavior from version 18.0 and catch the ValueError raised by `apply_inheritance_specs` in `normalize_with_keyed_tree` and re-raise it as a ValidationError, like `ir.ui.view.apply_inheritance_specs` already does elsewhere. This lets `edit_view()`'s existing fallback handle the case gracefully instead of crashing. Additionally, the two models responsible for the dynamically-added nodes described above are fixed at the source. `account_invoice_extract`'s duplicated `partner_id` field and `l10n_nl_reports`'s injected `company_id` field are now marked with `data-used-by`, the same attribute `_add_missing_fields` already sets in `ir_ui_view.py` for the fields it adds. Studio already skip rendering and computing xpaths for any node carrying this attribute (since https://github.com/odoo/enterprise/pull/92862), so these nodes are no longer exposed to the user and can no longer produce a studio operation that `normalize()` is unable to locate. opw-6332911 Forward-Port-Of: odoo/enterprise#127009 Forward-Port-Of: odoo/enterprise#122829
This fix keeps Australian report-specific calculation logic within the same reporting module that provides it. This prevents errors when Australian localization is installed without the optional Australian reports module, improving reliability for affected configurations.
Original PR description
We had a case where l10n_au and account_reports were installed together, but not l10n_au_reports (manually uninstalled ?). Since the custom engine was used on expressions in l10n_au, this failed. Custom engines should always be used and declared within the same module (or a submodule of the one defining the handler) to avoid such issues. opw-6451274 Forward-Port-Of: odoo/enterprise#128132
This update prevents a rare crash in Hungarian Intrastat reporting when company data is accessed in unusual permission scenarios. It helps keep reporting stable for future customizations or edge cases, even though the issue is not expected through the standard user interface.
Original PR description
Due to some trouble with tests, we found that in some cases, this function is called on the root company, and if the user does not have the access rights to read data from the company (users with system rights have them by default), it will cause a crash. This situation is not possible with the standard UI, but we fix it in case it becomes possible in a future version or customization. Forward-Port-Of: odoo/enterprise#128218
This fixes keyboard navigation in the Discuss Channels view so users can move from search results to channel cards without errors. It improves accessibility and lets users browse and open channels using arrow keys and Enter as expected.
Original PR description
**Description of the issue/feature this PR addresses:** ---------------------------------------------- Currently, in Discuss > Channels, pressing the down arrow while the search bar is focused raises…
**Description of the issue/feature this PR addresses:** ---------------------------------------------- Currently, in Discuss > Channels, pressing the down arrow while the search bar is focused raises a traceback instead of moving the focus to the first card. The channels kanban is rendered by a custom template written from scratch, which omits the t-ref="this.rootRef" of the standard kanban renderer, so this.rootRef() is null when the focus-view handler looks for the first card. A second traceback follows once the focus reaches a card, as focusNextCard looks for .o_kanban_group wrappers whenever the list is grouped, while the categories are rendered as plain rows, and the resulting empty list is then accessed by index. **Current behavior before PR:** ---------------------------------------------- - Pressing the down arrow from the search bar raises a traceback - Pressing the arrow keys again from a card raises a second traceback - Keyboard users cannot browse the channels kanban at all **Desired behavior after PR is merged:** ---------------------------------------------- - Pressing the down arrow from the search bar moves the focus to the first card - The arrow keys browse the cards in reading order, across categories - Pressing the up arrow on the first card returns the focus to the search bar - Pressing Enter on a card opens the corresponding channel - Keyboard navigation is consistent with the other kanban views Task-6439024 ---------------------------------------------- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Confirmed manufacturing orders now correctly update their operations when the related bill of materials changes. This prevents removed or edited production steps from staying incorrectly on existing orders, helping teams keep shop floor instructions aligned with the latest manufacturing plan.
Original PR description
### Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first…
### Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation on anything else than the company, name or workcenter - Go back to the MO, click the "Update Bom" button > The second operation is not unlinked and the first operation is not updated ### Cause of the issue: The `action_update_bom` updates the move raws and operations of the MO via the `_link_bom`: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L1214-L1218 For draft MO's all the work of these updates is done via the compute methods and by deleting all the records unrelevant to the new bom: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2603-L2626 And, in that case all the workorders that are not linked to an operation of the bom are expected to be deleted. However, when the MO is not in draft, the update of operations is expected to be performed here: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2647-L2664 However, since the operation of the bom has been deleted, the workorder that is expected to be deleted is not linked to any operation and hence does not satisfy the condition to be deleted: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2663-L2664 Concerning the non update of operations, it happens because the MO's operation are only updated on the three fields: `company_id`, `workcenter_id`, `name`: https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2647-L2664 https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2628-L2629 However, many other changes can and are actually relevant. ### Note: Prior to commit 80e6ed658fb43584bc2fad673ca40d9af6cf0ab6 operations were archived on boms rather than deleted: https://github.com/odoo/odoo/blob/4a5270218fe6fd7d30edb6d684b3340dc7423bab/addons/mrp/views/mrp_routing_views.xml#L53-L55 As such they would still be linked to an operation (but unrelated to the present values of the bom) and hence would fall into the condition of being unlinked from the MO. Since the bom operations are no longer archived there is no way to determine if an operation used to be linked to a bom and we therefore need to chose between deleting all operations unrelated to the present bom or to keep them all (when the MO has been confirmed). Enterprise: https://github.com/odoo/enterprise/pull/120709 opw-6285878 opw-6261738 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#280803 Forward-Port-Of: odoo/odoo#269747
The mail system now checks and cleans message posting data before a message is created, based on the current user’s permissions. This helps prevent inappropriate or unexpected data from being included in discussions, improving reliability and data handling.
Original PR description
This change sanitizes some post data before allowing the post, making sure the data received by `message_post` is clean based on the current user. part of task-6452761 Forward-Port-Of: odoo/odoo#284201 Forward-Port-Of: odoo/odoo#280894
Confirmed manufacturing orders now correctly reflect changes made to their bill of materials when users choose to update them. This prevents obsolete operations from staying on production orders and ensures relevant operation changes are applied, reducing planning and execution errors on the shop floor.
Original PR description
Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation…
Steps to reproduce: - Create a product with a bom and 2 operations - Create an MO for 1 unit of that product - Confirm the MO - On the bom, delete the second operation and modify the first operation on anything else than the company, name or workcenter - Go back to the MO, click the "Update Bom" button > The second operation is not unlinked and the first operation is not updated Cause of the issue: The `action_update_bom` updates the move raws and operations of the MO via the `_link_bom`: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L1214-L1218 For draft MO's all the work of these updates is done via the compute methods and by deleting all the records unrelevant to the new bom: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2603-L2626 And, in that case all the workorders that are not linked to an operation of the bom are expected to be deleted. However, when the MO is not in draft, the update of operations is expected to be performed here: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2647-L2664 However, since the operation of the bom has been deleted, the workorder that is expected to be deleted is not linked to any operation and hence does not satisfy the condition to be deleted: https://github.com/odoo/odoo/blob/f66614193cce18f5a3298d03ce7e5f29d54f07e9/addons/mrp/models/mrp_production.py#L2663-L2664 Concerning the non update of operations, it happens because the MO's operation are only updated on the three fields: `company_id`, `workcenter_id`, `name`: https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2647-L2664 https://github.com/odoo/odoo/blob/31df5033e31c193b4576ef37dfbc5fc683817bc5/addons/mrp/models/mrp_production.py#L2628-L2629 However, many other cahnges can and are actually relevant. Note: Prior to commit 80e6ed658fb43584bc2fad673ca40d9af6cf0ab6 operations were archived on boms rather than deleted: https://github.com/odoo/odoo/blob/4a5270218fe6fd7d30edb6d684b3340dc7423bab/addons/mrp/views/mrp_routing_views.xml#L53-L55 As such they would still be linked to an operation (but unrelated to the present values of the bom) and hence would fall into the condition of being unlinked from the MO. Since the bom operations are no longer archived there is no way to determine if an operation used to be linked to a bom and we therefore need to chose between deleting all operations unrelated to the present bom or to keep them all (when the MO has been confirmed). Community: https://github.com/odoo/odoo/pull/269747 opw-6285878 opw-6261738 Forward-Port-Of: odoo/enterprise#128120 Forward-Port-Of: odoo/enterprise#120709
Tax return submission now checks account settings only for tax groups that are actually used. This prevents businesses from being blocked by incomplete setup on unused tax groups, making tax closing smoother without changing reporting results.
Original PR description
…ax closing Steps to reproduce: - Remove the tax payable and receivable accounts of a tax group for which no move exists. - Open the tax returns view, set the opening date and submit the tax return -> Odoo prevents going further because the tax group configuration isn't fully done, but it's useless to ensure that for tax groups that aren't used. Forward-Port-Of: odoo/enterprise#128197
Product videos in the website shop now load only when their carousel slide is shown, so the preview image loads at the correct size. This prevents blurry video thumbnails and improves the product page experience for shoppers.
Original PR description
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video…
Steps to reproduce: =================== 1. Add a video (e.g. a YouTube URL) to a product from the Sales app. 2. Open the product page on the website. 3. Slide the carousel to the video. => The video preview cover is blurry. Root cause: =========== The product images are rendered in a carousel (the shop_product_carousel template in ) where only the first slide gets the "active" class; https://github.com/odoo/odoo/blob/af1b3ee2e7ac56a35bff5e030c3a831c27dbcf24/addons/website_sale/views/templates.xml#L3224-L3226 every other slide is "display: none". A product video is rendered as a live <iframe> inside its slide, so when the video is not the first media its iframe loads while its container has no dimensions (0x0). The embedded player then initializes as a small mobile player and loads a low resolution cover thumbnail (120x90), which looks blurry once the slide is shown at full size. Reloading only the iframe while the slide is visible fixes it, a full page reload does not. Fix: ==== Defer loading the video iframes located on hidden slides their src is moved to a data-src attribute on start and restored once the slide becomes visible. The player then initializes at full size and loads a high resolution cover. opw-6349394 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#282508 Forward-Port-Of: odoo/odoo#274002
Enterprise PR-[#127058](https://github.com/odoo/enterprise/pull/127058) Upgrade PR-[#10948](https://github.com/odoo/upgrade/pull/10948) --- Moved the computation of the different loyalty rewards from the pos_order.js model to their specific model files. Now the whole flow is started by `pos_order.recomputeRewards` it will delete all existing rewards on the order, then apply the discounts and auto rewards. The order keeps track of - `_active_rewards` - list of rewards manually claimed by the user, with optional qty and reward product - `_disabled_program_ids` - list of loyalty programs which were manually removed from the order by the user, so they are not automatically recomputed on the next pass - `_active_payment_programs` - the payment programs which are used in the order `pos_order.recomputeReward` will compute all the points that a reward can consume and then call `applyReward` which then decides which orderlines need to be added The frontend will keep the order consistent with the existing loyalty programs in the way they were loaded by the browser, to ensure that the displayed data is consistent while being able to still process loyalty orders while offline. Although the frontend does its own processing of loyalty, the backend will redo the same computations on order validation on `pos_order._process_saved_order` That will call `_process_loyalty` which will recompute based on the orderlines how many points the order is eligible for for each program, and how many points are consumed by each reward. Afterwards it will create the history lines based on its own computation, so that bad clients can't fool the system (and we can later trust to use loyalty programs on the self) The only thing which currently doesn't work offline is selling gift cards or ewallets. Before order validation, if there are any payment cards that need to be created, we do a RPC call to `loyalty_card.create_pos_cards` which will create the cards and send the data back to the client so that the new cards are properly added on the order. --- Here are the big UX changes: 1. Free product rewards used to be automatically added to the cart. In the old flow you would add a product, and if eligible for a free reward, another line with the negative price of the product would show up on the cart (which was the reward). Now free product rewards need to be manually claimed from the control buttons and they always show up as 0 priced order lines. 2. There is no more `Reset Rewards` button. Rewards are either applied on the order or not. The user can at any point remove reward lines from the order through the numpad, which will disable the reward. Disabled rewards can always be added back through the normal `Rewards` button. 3. We used to have a hard rule that rewards can't be claimed unless the client is already paying for something on the order. Now clients can use up their loyalty points and apply rewards without needing to also order something else --- Besides all of that, here is a list of all the tours which needed changes due to the new design, and why those changes were made: test_combo_product_dont_grant_point would create 2 combos of $48 each but then 100% discount would only reduce the total by $46 because the products in the combo have different sales taxes, and then expect the total to be 50. Now it applies the correct $48 so the total should be $48 PosLoyaltySpecificDiscountWithFreeProductTour expected for a free product reward to only be enabled when the product is added to the cart or when the reward button is pressed. Now it's treated as any other automatic reward and the free product is added to the order test_promotion_program_with_loyalty_program added a `trigger: auto` to the program. Otherwise the program is created with `trigger = false`, which can't really be produced in a real DB, unless through direct backend ORM calls or SQL. The new module relies on the trigger value for auto computed rewards. (The old one treated Falsy values as auto) test_ewallet_expiration_date expects the eWallet pay button to be available and then to throw an error dialog when trying to add an expired eWallet. Now the button will just be disabled if no valid eWallets can be added to the current order MultipleGiftWalletProgramsTour, RefundRulesProduct modified since the partner selection popup now launches automatically when an eWallet is added on the order with no partner selected EWalletProgramTour1 & EWalletLoyaltyHistory expected the partner popup to show up only after the pay button is pressed if there is no partner selected for the current order. Now the selection popup will show up as soon as a topup product is added on the order with no partner selected MultiplePhysicalGiftCardProgramSaleTour expects the user to select the program for the card when the product is added to the order, but then after the createManualGiftCard step, it asks the user again to chose a program. This seems like repetitive for no reason. So the new module will keep the program which was selected the first time by the user and skip the 2nd prompt PosLoyaltyFreeProductTour, PosLoyaltyFreeProductTour2, PosLoyaltyTour11.2, PosLoyaltyLoyaltyProgram3, PosLoyaltyTour12, test_loyalty_free_product_rewards_2, PosLoyaltyTour10, test_loyalty_reward_with_variant, test_min_qty_points_awarded, test_multiple_reward_line_free_product, PosOrderClaimReward, PosLoyaltyTour1, PosLoyaltyTour8, assumes the old free product behaviour of negative lines and auto applied rewards. It is changed to follow the new flow of requiring manual redeeming for all free product rewards and using 0 cost lines for all free product rewards PosLoyaltyLoyaltyProgram2: 1. It asserts that loyalty rewards cannot be used unless something is ordered first. Should we still enforce this constraint? 2. The last order total checked whether the reward was still applied by making sure the total is 6.40 (2 pens) and there is no negative line. This assertion doesn't work anymore when rewards have 0 price, so now it uses a doesNotHaveRewardLine method to ensure the reward is not applied PosLoyaltyRewardProductTag tries to claim the same reward 2 times on the same order with different products. This is no longer possible, the product for a reward is locked, only the qty can be changed. This is consistent with sale_loyalty and website_sale_loyalty. PosLoyaltyChangeRewardQty DDD Test Partner starts with 100 points. Adding a reward line automatically adds the maximum claimable qty. Also change the price from negative line to 0 test_buy_x_get_y_reward_qty - updated to apply the free product reward manually PosLoyaltySpecificDiscountTour tries to claim the same 10$ reward twice. The customer has enough points to claim the reward 2 times, but the new module doesn't allow applying the same discount reward multiple times, even if the points would allow. This is consistent with sale_loyalty test_receipt_data_pos_loyalty tries to call a `postProcessLoyalty` which doesn't exist anymore and loyalty is synced automatically after orders are validated test_gift_card_communication - the cards are created through the create_pos_cards method on the loyalty.card. Then the _process_loyalty() method replaces `confirm_coupon_programs` and generates all the history and card data, and then send communications for newly created cards test_loyalty_history - `create_pos_cards` creates the new cards instead of `confirm_coupon_programs`. But it only creates empty cards, which are awarded points only on `_process_loyalty` which is called afterwards. Multiple calls to `_process_loyalty` on the same order are considered duplicate, and will not create any new data on the backend. test_reward_line_tax_grouping_key created a coupons program with an automatic trigger and expected the product to apply the points. Coupon programs can only be `with-code` triggered, and the rules don't generate points, they only enable the points on the coupons. So I changed the program type to `promotion` PosLoyaltyTour2 used the old `Reset Rewards` button which doesn't exist anymore, so the removal and re-apply of rewards is done manually. It also has the same free product reward negative line changes --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update renames an internal loyalty coupon reference to a card reference in German POS certification and Mexican POS invoicing areas. It keeps these country-specific POS features aligned with the broader loyalty system refactor, reducing the risk of inconsistencies during future upgrades.
Original PR description
As part of the pos_loyalty refactor, the pos_orderline.coupon_id field was renamed to pos_orderline.card_id. This PR renames wherever the field is used to keep everything consistent Community PR-[#274951](https://github.com/odoo/odoo/pull/274951) Upgrade PR-[#10948](https://github.com/odoo/upgrade/pull/10948)
The expense dashboard was updated as part of Odoo’s Owl 3 migration, replacing an older internal component update mechanism with its newer equivalent. This keeps the HR Expense interface aligned with the platform’s latest technology and helps reduce future maintenance risk without changing user-facing functionality.
Original PR description
As part of the Owl 3 migration, replace onWillUpdateProps hook with the appropriate Owl 3 alternatives. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update replaces an older internal screen-update mechanism across several Odoo apps so they remain compatible with the next version of Odoo's web interface framework. It is a behind-the-scenes cleanup with no intended change to everyday workflows.
Original PR description
One last batch of grouped mechanical `useLayoutEffect` migration before having to do the rest one by one. WHY: useLayoutEffect is removed in OWL3 Enterprise PR: https://github.com/odoo/enterprise/pull/128927 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This change updates several Odoo Enterprise apps to use newer internal screen update mechanisms. It helps keep Documents, Social, Marketing Automation, Spreadsheet, and Timesheets compatible with upcoming platform changes without changing day-to-day functionality.
Original PR description
One last batch of grouped mechanical `useLayoutEffect` migration before having to do the rest one by one. Community PR: https://github.com/odoo/odoo/pull/284142
This change updates internal web interface code as part of Odoo's move to the newer Owl 3 framework. It helps keep the web client maintainable and compatible with future platform improvements, with no expected direct change for day-to-day users.
Original PR description
As part of the Owl 3 migration, this pr aims to replace **onWillUpdateProps** hook with the appropriate Owl 3 alternatives. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr