Monday, September 28, 2026
14 changes · master
Enhancements to existing features
Invoice and related sales, purchase, CRM, repair, and website sales forms now show a partner's VAT number when available, or another configured identifier when VAT is missing. This makes it easier for business users to recognize and validate customers or suppliers directly from contact widgets.
Original PR description
*: account, crm, purchase, purchase_requisition, repair, sale, website_sale, base Purpose: The additional identifier should be displayed on the partner contact widget on the invoice's form view if vat is not set. Also, rename context key `show_vat` to `show_identifier` to align the fact that it will either show the vat or the additional identifier. task-6509025 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284022
This improves how Odoo detects changes in mail and messaging data so the interface does not refresh parts of the screen unnecessarily. Users should experience smoother messaging interactions, especially in busy conversations or menus, without any change to existing features.
Original PR description
computed identity comparison uses `Object.is`, so any field rebuilding an array or object each time always looks changed, even when its content is the same, causing useless renders/recomputes. Add an `equals` option to `Record.computed`/`computedUntilStale` and apply `shallowEqual` (or a new `nestedShallowEqual` to mail's computed fields. 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
Payroll users can now select and correct multiple payslips directly from the list view using a new header action. This reduces repetitive work and makes payroll corrections faster and easier to manage.
Original PR description
In this commit, we added a header's button in list view to allow payslips multi correction. task-6591816 Forward-Port-Of: odoo/enterprise#132571
Belgian payroll work exemption information is now handled in the end-of-collaboration form instead of the employee record. This places the fields where HR teams manage departures, making the process clearer while keeping payroll calculations connected to the same underlying data.
Original PR description
Moved Work exemption form fields from employee form (Payslip notebook page, ONSS group) to the employee departure form. The fields are migrated here:https://github.com/odoo/upgrade/pull/11351 task-6526911
Mexican payroll now includes employer cost rules and makes payroll reports easier to access from the pay run view. This helps HR and payroll teams better understand total employment costs and review payroll information more efficiently.
Original PR description
. Improve the visibility and usage for MX payroll and reports view from the payrun view task-6519821
Belgian payroll now warns HR teams when an employee is assigned a youth-related worker subtype but is too old for it. The warning appears in dashboards and employee records, blocks payslip validation until corrected, and offers an action to update affected employee worker codes in bulk.
Original PR description
Few sub-types are for young employees that will not turn 19 this year
If an employee is older for that subtype:
1 - New warning in both dashboard and employee form view
. 1.1 - It blocks payslip validation
. 1.2 - As an action:
.. 1.2.1 we update the subtype worker code
.. 1.2.2 toast message to mention the amount of updated employees
.. 1.2.3 finally redirect user to the list view of employee
Note: Right now the employee type is not changing, need to be discussed with PO.
task-6538516
Forward-Port-Of: odoo/enterprise#130574Payroll users now get a confirmation before recomputing payslips that include manually edited salary lines, reducing the risk of accidentally losing adjustments. The update also records reverted changes in the chatter and improves the visual clarity of payroll line tooltips and empty cells.
Original PR description
Recomputing a payslip erases any manually modified salary lines without any warning. Additionally, the explanation tooltip icon is misaligned and uses the wrong icon. Finally, in list view (the Salary Computation tab in payslips), empty cells appeared darker than the rest of the row. This adds a confirmation popup when recomputing a payslip with manual changes, logs a chatter message when changes are reverted, updates the explanation tooltip and ensures the row's background color is preserved while keeping the text hidden. Task-6398068
The media dialog now presents icons in clearer themed groups, making it easier for users to browse related options. Icon search is also more reliable across languages, accents, and multi-word searches, while new checks help prevent missing or broken icons in future updates.
Original PR description
[IMP] web, website: add tests for the icon list --- __Before commit__ `icons.py` is generated by `web/tooling/icons/generate_icons.py`, but no tests checked whether it contained all icons from…
[IMP] web, website: add tests for the icon list
---
__Before commit__
`icons.py` is generated by `web/tooling/icons/generate_icons.py`, but
no tests checked whether it contained all icons from
`icons_wishlist.txt` and `odoo_ui_icons_config.json`. This could lead
to missing icons if someone forgets to regenerate the fonts.
__What's added__
- A Python test asserting that `ICONS` in `web/icons.py` matches
`icons_wishlist.txt` and `odoo_ui_icons_config.json`.
- A step in the website `media_dialog` tour that verifies each icon
offered by the media dialog renders properly by measuring its width,
ensuring there were no errors during generation.
[IMP] web: order the icon picker's icons by theme
---
The media dialog lists the icons in the order `web/icons.py` declares
them, and that order was alphabetical: `arrow_back` sat between
`article` and `arrow_circle_down`, `crop_landscape` far from `crop`, the
`cc_*` cards scattered through the brand icons. Browsing the dialog
meant scrolling past unrelated icons to reach a variant of the one
already on screen, which only search made bearable.
Both sources are now ordered by theme -- arrows, layout, text, media,
payments, brands ... -- with the variants of an icon kept side by side,
and the generator hands that order over to `icons.py` as it is instead
of sorting it away. `icons_wishlist.txt` carries `#` headers naming
each group, so a new icon has an obvious place to be added to.
The fonts are unchanged: they are still built from a sorted wishlist and
laid out by codepoint, which is what keeps two builds of the same icon
set byte-identical.
[IMP] web, html_editor: translate the icon search tags
---
Every icon of the media dialog carries a long list of search tags
(`wheelchair`, `basket`, `magnifying glass` …) that never reaches the
browser: the needle is matched against them server-side. Those tags
were English only, so a user searching in their own language found
nothing unless the word happened to be part of the icon name.
The tags are now declared with `_lt` in `web/icons.py`, and
`search_icons()` takes an optional `translate` -- `env._` from the
controller -- to match them in the user's language. They stay lazy
because the dict is built at import, long before any language is known.
The English tags remain searchable whatever the language: the index
built at import is matched first, and only the icons it misses are
translated, so an untranslated database and an English needle cost
nothing.
[IMP] web: match every word of an icon search
---
Searching the media dialog for `shopping cart` looked up that whole
string in the tags, and matched only the icons spelling the two words
in that very order. Anything a user types as two hints -- `arrow left`,
`chart bar` -- was therefore likely to return nothing at all.
The needle is now split on spaces and an icon matches when all of its
words are found, in any order, in the icon name or its tags. A word may
come from either language: only the words the English tags miss are
looked up in the translated ones.
[IMP] web: normalize the icon search needle and tags
---
Searching the media dialog for `Cafe` or `café` found nothing: the
needle and the haystack were only lowercased, so case was handled but
diacritics were not. Translated tags make this worse, since most
languages other than English spell their words with accents -- a French
user typing `marche` missed every icon tagged `marché`.
Both sides now go through `remove_accents(text.casefold())`, applied to
the needle, to the index built at import and to the tags translated on
the fly. `casefold()` also folds the cases `lower()` leaves alone, such
as the German `ß`.
The index is built from `tags._source` rather than from an explicit
`_translate('en_US')`: same string, without pretending a language is
known at import time.AI provider settings can now be turned on or off independently from the saved API key. This makes it easier to temporarily disable a provider while keeping credentials in place, and users will see clearer messages when keys are missing or invalid.
Original PR description
Prior to this PR, user had to delete the API key set in order to disable the corresponding provider option in the AI config view. With this PR, we removed the compute method to allow the enable/disable provider option independently from the API key value. Additionally, reworded error messages when API keys are unable and added custom messages when provided API key is invalid. task: 6331248
Indonesian payroll now includes a non-taxable Meal Allowance (Catering) salary rule. This helps employers record catering benefits correctly as benefits in kind while keeping payroll calculations aligned with local requirements.
Original PR description
Add a new salary rule for a non-taxable Meal Allowance (Catering) benefit, categorized as Benefit in Kind. task-6461730
Salespeople are now notified as soon as a customer schedules their subscription to close at the end of the billing period. This gives teams an earlier opportunity to contact the customer and potentially prevent churn.
Original PR description
Before this PR: When a customer closes their subscription at the end of the billing period, only `end_date` changes `subscription_state` stays `in progress` until the period ends. Since tracking only assigned the "State Change" subtype on `subscription_state` changes, this update was logged silently in the chatter with no notification. The salesperson had no way to know the customer intended to leave, and no chance to retain them. After this PR: `_track_log_get_default_subtype` also returns `mail.mt_comment` when `end_date` changes and `is_closing` becomes `True`. The salesperson, already an auto-subscribed follower of that subtype, is now notified as soon as a closing end date is set, giving them a chance to act before the subscription churns. Impact: Salespersons are alerted the moment a customer schedules closure, instead of finding out after the fact, giving them a real window to reach out and reduce silent churn. Taskid-6366414
Sales orders linked to projects now show the milestone option when selling services that are invoiced based on milestones. This makes it easier for users to create and manage milestones directly from the sales order, supporting smoother project billing workflows.
Original PR description
When a product is configured as follows: ------------------ Product Type: Service Service Tracking: Create on Order - Nothing Invoicing Policy: Based on milestones user creates a sale order linked to a project,the milestone stat button will be visible on the sale order. From there, the user can also create new milestones. task-4648575
Click & Collect choices made on the product page are now saved without creating an empty order first. This keeps sales data cleaner and applies the customer's delivery choice only when they actually create a cart.
Original PR description
Selecting a pickup store or the standard delivery method on the product page created an empty sale order just to store the choice, even before the customer added anything to the cart. Keep the selection in the session instead of creating a cart when none exists yet. The choice is applied to the order once a cart is actually created. task-6538541 Forward-Port-Of: odoo/odoo#288321
Followers and recipients are now shown in alphabetical order, making lists easier to scan and use. Loading more entries now retrieves smaller batches of 20 at a time, which can make these requests lighter and more responsive.
Original PR description
Before this PR: Followers and recipients were not sorted. Each request loaded up to 100 followers or recipients. After this PR: Followers and recipients are sorted alphabetically. Additionally, we now load only 20 followers or recipients at a time when making a request to load more. task-id:3293718