Monday, August 31, 2026
21 changes · master
Enhancements to existing features
Portal users should see much faster results when filtering their tasks by milestone. This improves responsiveness on the My Tasks page by avoiding a slow database search path.
Original PR description
Go to `/my/tasks`. The search on milestones is really slow. Performance improvement for a portal user: | | Time | Query plan | |--------|--------|--------| | Before | ~3.6s |…
Go to `/my/tasks`. The search on milestones is really slow. Performance improvement for a portal user: | | Time | Query plan | |--------|--------|--------| | Before | ~3.6s | https://explain.dalibo.com/plan/dga21917bc86eg54 | | After | ~60ms | https://explain.dalibo.com/plan/91b818beg2f1077f | For portal users, complex record rules require joining the `project` table. Because the query includes a `limit=1`, the postgresql query planner assumes it will find a matching row almost immediately. Hoping for a "fast exit", it chooses to sequentially scan the `project_id` index to perform a Merge Join. However, if it doesn't find a match early on, it ends up scanning the entire index, resulting in a massive slowdown. We update the `search_count` constraint from `limit=1` to `limit=80`. By increasing the limit, we alter postgresql's cost estimation. The planner can no longer assume a cheap "fast exit" is guaranteed, which forces it to abandon the flawed Merge Join strategy. Instead, it correctly evaluates the query and chooses the index on `milestone_id` to retrieve the records. task-6373729 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#283239
Warehouse users can now split stock move lines into equal-sized packages directly from detailed operations instead of manually creating each line. The process keeps the correct source locations and can suggest package size from the selected package type, making packing faster and less error-prone.
Original PR description
Before, whenever a user wanted to split move lines in different equal by size packages, user had to manually add new lines. Now by provividing the size of a package, it can be done automatically. This functionality takes into account current locations, and preserves them for newly created move lines. Moveover, if several move lines are picked, it is impossible to split them together, only separately. Moveover, Package size can be calculated automatically by providing Package Type. We check the UoMs that have this package type, and get the relative factor with the move UoM. If several UoMs have this package type, then the first one is picked. task-6034274 I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Manufacturing users can now open a complete, ordered view of a bill of materials, including nested subassemblies, directly from the BoM form. This makes it faster to review and update components without manually opening each related BoM one by one.
Original PR description
Currently, when a BoM contains components with child BoM, clicking `Compare BoMs` shows the immediate components only in list view. Users still need to open each BoM separately to edit nested…
Currently, when a BoM contains components with child BoM, clicking `Compare BoMs` shows the immediate components only in list view. Users still need to open each BoM separately to edit nested components. This change will adds a new smartbutton that shows: - Components: number of components of the BoM - SubAssembly: number of sub BoMs inside the BoM Clicking the smartbutton opens the list of exploded components by following the hierarchy order, which provides a complete view of the BoM structure in a single place, allows user to update nested components and without navigating manually. ### About `forced_order_ids` context: ### - The action uses a `forced_order_ids` context key to preserve the exploded hierarchy order instead of the model's default ordering when the list view performs a `search_fetch()`. - When opening the exploded list view -> product form -> BoM of that product, `forced_order_ids` should be reset to `False`. Otherwise, if the same child BoM is added multiple times, the BoM show repeated BoM lines. [video](https://drive.google.com/file/d/1M5k4XXGvr8KASzhBsRRByV3vepM8BBn1/view?usp=drivesdk) - When the user searches for a component through the search panel, `forced_order_ids` should filtered using the records returned by the `super()` call, which only contains the matched records. Without that filtering, the search would stop working correctly and would return all `forced_order_ids`. [video](https://drive.google.com/file/d/1De0IfTyPPTjk02rCOh2yJPHa9wI0Zi_W/view?usp=drivesdk) - It is also used to change the order of the fields in the search view, because when the list is opened from the `Components/SubAssembly` smartbutton, the component search should be on top, in other cases the BoM search should be on top. Ent PR https://github.com/odoo/enterprise/pull/120254 task-6247051
Warehouse barcode scanning now automatically creates a package when a scanned packaging barcode has a package type, such as a box for a pack of six. This helps keep grouped items packed correctly during stock and manufacturing barcode flows, reducing manual packing steps and improving consistency.
Original PR description
When users scan packaging barcodes, they want them to be stored together for UoMs with packaging type specified. For example, if Pack of 6 is scanned and it has a packaging type, you would like a package to be automatically created if packaging barcode for Pack of 6 was scanned. This commit implements exactly that. task-6034274
Timesheets billing target menus and settings have been reorganized and renamed to make them easier to understand. The update also keeps related menu visibility in sync when leaderboard settings change, reducing confusion from outdated menu options.
Original PR description
In this commit, we clean the billing target and menus of Timesheets. In short, we: - Add a new "Billable Time" parent menu that is hidden when billable time targets are not displayed. - Hide the "Tips" configuration menu when the "Billing rate leaderboard" is disabled. - Reword the billable time settings (billable time targets & billable time leaderboard). - Rename the billable time target labels in their configuration view and on the Employee form. Additionally, we ensure the menu cache is invalidated when the leaderboard setting changes, the same way as currently done for the billing rate setting. Without this, the "Tips" configuration menu would stay stale until the cache got cleared by some potentially unrelated reason. task-6486399
Manufacturing scheduling now skips over fully occupied time blocks instead of checking many tiny windows one by one. This significantly speeds up planning when work centers are heavily booked, especially for very short work orders.
Original PR description
### Description of the issue/feature this PR addresses: The workcenter planning logic in _get_first_available_slot can become inefficient when searching for very short available slots. The method…
### Description of the issue/feature this PR addresses: The workcenter planning logic in _get_first_available_slot can become inefficient when searching for very short available slots. The method repeatedly builds small candidate time windows and checks them against existing workorder and leave intervals, potentially iterating many times before finding a free slot. This leads to unnecessary computational overhead in scenarios where a large number of busy intervals exist and the remaining duration to schedule is small. ### Current behavior before PR: The planner checks for conflicts by computing the intersection between the candidate window and the busy intervals. When a conflict is detected, the candidate window is shifted forward (or backward) to the end (or start) of the intersection, and the process is repeated until a free slot is found. This approach requires repeatedly performing full interval merge operations, which becomes disproportionately expensive when the candidate windows are very small and the loop iterates many times. ### Desired behavior after PR is merged: The planner uses a new Intervals.conflicting() helper to retrieve the entire busy interval that overlaps with the candidate window. Instead of advancing only to the end of the intersection slice, the planner can jump directly to the end (or start) of the full busy interval. This avoids repeated full-merge work, reduces the number of iterations needed to find a valid slot, and prevents pathological performance slowdowns in short-duration planning scenarios. ### Benchmarks Profiling _get_first_available_slot with different workorder durations. Database has multiple months that are fully booked. Speedup is more dramatic with shorter durations but there is at minimum minor improvements across the board. | Work Order Duration | Before | After | | --- |---|---| | 1sec | ~2.5min | <1sec | | 1min | ~2sec | <1sec | ### References opw-5437256 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284278 Forward-Port-Of: odoo/odoo#246015
The map view now more reliably opens location details when users select an item and keeps their map layout when they leave and return. Users also get a cleaner side panel with resizable width, making field service planning easier and less repetitive.
Original PR description
First commit fixes an issue where clicking a list item to center the map and open its marker's popup often did nothing at all. The pending record was stored in a plain, non-reactive field, only ever…
First commit fixes an issue where clicking a list item to center the map and open its marker's popup often did nothing at all. The pending record was stored in a plain, non-reactive field, only ever rechecked by updateMap()'s useLayoutEffect, which reruns on every patch of the component - but nothing about the click itself reliably causes a patch: closing pinListPopover (a mobile-only bottom sheet) only triggers one if it was actually open. On desktop, where it never is, closing it is a no-op, so no patch happens and the pending record is never rechecked. The popup would then only show up later, incidentally, whenever some unrelated interaction (toggling a group fold, expanding Unlocated, ...) happened to trigger a patch of its own. Even when it did open, doing so before the map had finished panning to it made it visibly reposition/flicker, since it isn't tracked by Leaflet's own pan animation the way a native Leaflet popup would be. Replace the deprecated Owl 2 useLayoutEffect compat shim with native onMounted/onPatched, and branch on whether pinListPopover is actually open: when it isn't (desktop), open the popup right away instead of depending on a patch that was never going to happen; when it is (mobile), wait for the patch that closing it triggers - the same one that recreates the markers - before opening the popup, so it anchors to the freshly created marker element instead of the stale one about to be removed. Also wait for the pan's moveend before creating the popup, to avoid the repositioning/flicker. Second commit preserves map view state when navigating away to another view and returning, preventing the map from resetting to its default initial state. The following state variables have been moved into the map model so they can be exported via `getLocalState` and restored upon remounting: * **Group Folding:** Retains folded group states via `closedGroupIds`. * **Unlocated Panel:** Maintains the expansion toggle state of `expandUnlocated`. Since closed groups now outlive a reload, the groups a view folds by default can no longer be re-applied on every load: they come from `_getDefaultClosedGroupIds`, only consulted when the closed groups are (re)initialized, i.e. when the groupBy changes. Field service maps fold their material resources through that hook, instead of overwriting `closedGroupIds` after each load, which discarded both the restored state and whatever the user had folded. The current user's position is instead persisted in `localStorage`, keyed by the view and action. When it comes from an address search or from the company's address, the actual coordinates are cached. When it comes from the browser's geolocation API, only the fact that it should be looked up again is cached, since GPS coordinates can quickly become stale. Third commit refines list sidebar and group header styling. Group headers now span the full width of the list sidebar and darken on hover, like group rows in list views. The group pin icon is narrower so it no longer feels bloated next to the group name, and the Google Maps button in group headers no longer has a background or hover effect of its own. Also unifies the "clickable" styling of located pins and unlocated records under a single class, and aligns the unlocated section's fold icon with the one used for other groups. Record rows now carry their own horizontal padding instead of the list itself, so their hover background spans the sidebar's full width. The "my location" button icon uses the default icon size, and the pin list no longer keeps a stray bottom spacing. Fourth commit adds the possibility to resize map view's side panel (pin list) similarly to what is done in the search panel or gantt view. task-6431782
Users can now switch to a Kanban layout when selecting documents from the chatter, making it easier to browse files visually. The dialog has also been simplified by removing less relevant fields and filters such as Favorites, Activities, and Owner.
Original PR description
`*` = ai_documents_source, documents_spreadsheet **Purpose:** When we add document in a chatter, the file selection dialog currently displays only the list view. **Specifications:** - Add a Kanban view option to the document selection dialog. - Remove the Favorites field/filter. - Remove the Activities field. - Remove the Owner field. Task-6236894
AI agents can now work through longer tasks before reaching their activity limit. If they do reach the limit, they are guided to summarize progress and ask the user whether to continue instead of abruptly ending the conversation with an error.
Original PR description
Before this change, AI Agents were limited to a default of 20 successive API calls (agentic loop turns) before stopping. When this limit was exceeded, the Python backend raised a hard UserError exception. This commit increases the default fallback limit as well as introducing a graceful fallback for the case when that limit were to be reached. task-6313259
Document and folder action menus are now more consistent across views, making common actions easier to find. Users can copy links from the sidebar and add stars from right-click or top action menus, while the folder cog menu has been simplified by removing the Info & Tag action.
Original PR description
Purpose ======= Remove the "Info & Tag" action from the folder cog wheel. Make the order of the action consistent across the views, add "Copy Link" in the side bar, and "Add star" in the right-click / top actions. Task-6474895
Belgian payroll calendars now show working time reorganisation details for variable schedules. Users can record a Time Reorganisation Amount for credit-time arrangements, with automatic full-time value filling in one case and a blank value available in another.
Original PR description
. Show Working Time Reorganisation field with the variable calendars . Add Time Reorganisation Amount field to show mount of time for the Credit-Time . If user select 3, display a new field Time Reorganisation Amount (float) below and made it filled with the same value as Hours per week (we expect a full credit time) . If user select 4, display the same field Time Reorganisation Amount (float) below and let it empty task-6449163
The point of sale now highlights available loyalty rewards with an attention animation, helping cashiers notice and apply customer benefits more easily. Text input popups can also clean pasted content by removing line breaks, reducing formatting issues during checkout workflows.
Original PR description
In This Commit: ================ - Add removeNewLines option to TextInputPopup to strip newlines on paste - Add a reward attention animation to rewards button when active Task-6462056
Long-term sickness absences can now be separated into the appropriate sick leave categories based on duration. This helps payroll and HR reporting reflect guaranteed salary, unpaid sick leave, and long-term sick periods more accurately for extended absences.
Original PR description
This commit will support the new split for Long Term Sickness (LEAVE280) Ex:- Leave from 1st Jan 2026 to 30th Jan 2027. This time off will be split into: - LEAVE110 - Sick Time Off: 30 days - LEAVE214 - Sick Time Off (Without Guaranteed Salary): 335 days - LEAVE280 - Long Term Sick: 30 days task-[6307896](https://www.odoo.com/odoo/project/1251/tasks/6307896) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Belgian payroll now separates employee sick leave after 12 months into a dedicated long-term sickness category. This improves payroll accuracy and compliance by distinguishing long-term sickness from earlier unpaid sickness periods, including relapse cases.
Original PR description
Currently, a sick leave for an employee is split into LEAVE110 (guaranteed salary, first 30 days) and LEAVE214 (no guaranteed salary, after 30 days). This commit adds new split for Long term sickness…
Currently, a sick leave for an employee is split into LEAVE110 (guaranteed salary, first 30 days) and LEAVE214 (no guaranteed salary, after 30 days). This commit adds new split for Long term sickness (LEAVE280) for employee which is applied after 12 months sickness. What changed: - New helper `_get_l10n_be_long_term_sick_cutoff_date` return the cuttoff date at which a sickness chain crosses the 12-month threshold accounting relapse using `_l10n_be_get_sick_leave_twelve_months_cutoff_date` which is moved from `hr_payslip` to `hr_employee` as it will be commonly used for leave split and the paylsip work entry generation. - New helper `_get_l10n_be_long_term_sick_split` takes the existing LEAVE110/LEAVE214 split and converts whatever part of it falls on/after the 12-month cutoff into LEAVE280. If the cutoff falls in the middle of a segment, that segment is cut in two. - Applied this check to every place in `_get_l10n_be_sick_leave_split` that can return a final result. - `_get_leave_work_entry_type_dates` (legacy support for old, unmigrated data) gets the same 12-month check before its existing 30-day check. task-[6307896](https://www.odoo.com/odoo/project/1251/tasks/6307896)
Currency amounts written in words now use language-aware forms where supported, so documents can show grammatically correct currency names and number wording. This improves localization for languages with complex plural and gender rules while keeping existing formatting behavior when no specialized wording is available.
Original PR description
## Summary POC exploring a better use of `num2words` in `res.currency.amount_to_text()` (core `base` module). Today `amount_to_text()` converts the integral/fractional parts with a plain…
## Summary POC exploring a better use of `num2words` in `res.currency.amount_to_text()` (core `base` module). Today `amount_to_text()` converts the integral/fractional parts with a plain `num2words(...).title()` cardinal conversion, and always uses the currency's single-form `currency_unit_label`/`currency_subunit_label`. This is fine for English-like languages, but wrong for languages with grammatical number agreement: many Slavic languages use one of 3 noun forms depending on the last digit(s) of the amount (e.g. Ukrainian "гривня"/"гривні"/"гривень"), and some also need the numeral itself gendered (e.g. "одна гривня", not "один гривня", for 1 UAH). `num2words` already ships this data per `(language, ISO currency code)` via `CURRENCY_FORMS` + `pluralize()` + `_money_verbose`/`_cents_verbose`, normally consumed through its own `to='currency'` mode. That mode is not used directly here because it changes Odoo's current sentence shape (it always renders the subunit clause, even when zero, and joins the two clauses with its own separator instead of a translatable "and"). This PR uses that per-language data to build just the two correctly-inflected words, and plugs them into Odoo's *existing* template: - the zero-subunit clause is still dropped (no "...and zero cents"), - the two clauses are still joined with the translatable `"and"`. When `num2words` has no currency data for the `(language, currency)` pair, behavior falls back unchanged to the previous plain-cardinal + translated-label path. ## Example (Ukrainian / UAH) | Amount | Before | After | |---|---|---| | 1.00 | `Один Hryvnia` | `одна гривня` | | 2.00 | `Два Hryvnia` | `дві гривні` | | 5.00 | `П'Ять Hryvnia` | `п'ять гривень` | | 21.56 | `Двадцять Один Hryvnia and П'Ятьдесят Шість Kopiyka` | `двадцять одна гривня and п'ятдесят шість копійок` | (English/EUR and other currencies `num2words` has no data for are unaffected.) ## Scope / open questions (see inline code comments) - This is a POC: only currencies `num2words` explicitly models (`CURRENCY_FORMS`) get the improved output; everything else keeps today's behavior byte-for-byte. - The untranslated literal `"and"` in some locales is a pre-existing translation-coverage gap, unrelated to this change. - `num2words`' own data isn't uniformly correct/complete across all its language modules (e.g. English `EUR` forms don't pluralize `"euro"` upstream) - that's an upstream data quality question, not something this PR should paper over. - Longer term this raises the question of whether `currency_unit_label`/`currency_subunit_label` should stay only as the fallback path.
Users can now update tags directly in the subscriptions list instead of opening each subscription individually. This makes it faster to organize one or many subscriptions at once, improving day-to-day subscription management.
Original PR description
Before this commit, the Tags column in the subscriptions list view was read-only, preventing tags from being edited directly from the list. After this commit, tags can be edited directly from the subscriptions list, either on a single subscription or on multiple selected subscriptions at once. task-6222724
Belgian payroll now includes a warning when an employee may not meet the required minimum age rules. This helps payroll teams spot potential compliance issues earlier and reduce manual checks.
Original PR description
task-6515312
This update improves the HTML editor so users can keep editing list items even when special non-editable elements are present. It also restores safeguards that prevent editing placeholders from appearing in inappropriate places, reducing frustrating cursor behavior and regressions around links.
Original PR description
If a list item has a selection blocker, the user can be stuck and forced to move to the previous or next block instead of being able to continue to edit the list item. To get around this, this commit allows selection placeholders in list items. task-6394918 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279432 Forward-Port-Of: odoo/odoo#278323
Pay later receivables in Point of Sale settlement are now reconciled in one grouped operation instead of repeated partner-by-partner processing. This keeps the accounting outcome unchanged while reducing processing overhead, especially for sessions with many customers.
Original PR description
The pay later receivable lines are reconciled with one `reconcile()` call per partner, and each call filters the whole set of lines again. Reconcile them in a single `_reconcile_plan` call grouped by partner instead: the result is the same, but the recompute cascade of the ORM runs once instead of once per partner. opw-6458271 Related: https://github.com/odoo/odoo/pull/281495 Forward-Port-Of: odoo/enterprise#129637 Forward-Port-Of: odoo/enterprise#127415
Inventory batch picking is now part of the main stock app instead of being split across separate add-on modules. This simplifies setup and maintenance for businesses using delivery, localization, and batch picking features, while also removing unused legacy code.
Original PR description
(Re)opening an useless PR for the sake of the runbot's builds... **Enterprise PR**: odoo/enterprise#128175
This update addresses common website quality issues flagged by Lighthouse, especially around accessibility for keyboard and screen reader users. It also improves privacy and performance by hosting Google fonts locally by default, helping websites provide a smoother and more compliant visitor experience.
Original PR description
This PR fixes some common Lighthouse issues. Please have a look at the different commits for more information.