Friday, September 18, 2026
54 changes · master
Enhancements to existing features
Blog authors now get a more familiar editing experience, with starter content presented as an editable text block and more control over blog post width. The main blog listing also regains a page title, improving search visibility while still allowing it to be hidden when desired.
Original PR description
- [IMP] website_blog: wrap initial content in a "Text" snippet - [IMP] website, website_blog: allow full container control on blog posts - [IMP] website_blog: add a title on the "All blogs" page Forward-Port-Of: odoo/odoo#289201
Projects created outside the main Project app flow now automatically receive four basic task stages. This helps users and AI-assisted project creation start with a ready-to-use task workflow, reducing setup omissions after imports or automated project generation.
Original PR description
Before this commit, when the user imports a project he creates he sometimes forgot to import his basic stages to be able to quickly add his task. An AI could also forget to create stages when the user asks the AI to generate a business project. This commit adds a default method to generate 4 basic stages when the project is not created manually by the user in the main action of Project app. task-6233294 Forward-Port-Of: odoo/odoo#287735
PayPal payments now show key account details more consistently and support clearer card brand handling. Businesses also get more control over 3D Secure configuration, helping align checkout behavior with their payment requirements.
Original PR description
1. Add Brands for Card Payment 2. Add possibility to configure 3DS 3. Show account id and email always 4. Remove (copy) from payment methods when duplicated task-6537307 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288935
Odoo search filters can now handle records that span a start and end date, not just records that begin in a selected period. This makes it easier for users to find ongoing items such as events, bookings, or assignments that overlap a chosen timeframe.
Original PR description
A date filter (`<filter date="date_start"/>`) filters on a single field: the periods it offers, as well as the relative ones introduced in [1], match the records that *start* in the searched period.…
A date filter (`<filter date="date_start"/>`) filters on a single field: the periods it offers, as well as the relative ones introduced in [1], match the records that *start* in the searched period.
For models describing a range rather than a single date, it is useful to be able to search for records that are ongoing during a given period.
A date filter can now name the field ending that span:
<filter string="Period" name="period" date="date_start" end_date="date_stop"/>
Its periods and its relative options then match the records overlapping the searched period:
['&', ('date_start', '<=', period_end),
'|', ('date_stop', '>=', period_start),
'&', ('date_stop', '=', False), ('date_start', '>=', period_start)]
A record with no end date ends when it starts. Each bound is serialized with the type of the field it is compared to, so that a date field and a datetime field can be paired.
Task-6546268
[1] https://github.com/odoo/odoo/pull/267980
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-prThis update makes record value changes more efficient by applying field updates in batches instead of one at a time. It also preserves the bank name when setting a bank account on an accounting journal, helping keep financial records accurate.
Original PR description
Set field values in batch instead of doing it field by field. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The spreadsheet component has been updated to the latest available version, bringing ongoing improvements from the shared spreadsheet engine into Odoo. This helps keep spreadsheet behavior current and improves how collaboration/session changes are tracked behind the scenes.
Original PR description
### Contains the following commits: https://github.com/odoo/o-spreadsheet/commit/4020a44abd [REL] Package: Bump master to 20.1.0-alpha.0 [Task: 0](https://www.odoo.com/odoo/2328/tasks/0) https://github.com/odoo/o-spreadsheet/commit/88a8b225da [IMP] session: Forward root command in the revision payload [Task: 6574631](https://www.odoo.com/odoo/2328/tasks/6574631) Co-authored-by: Florian Damhaut (flda) <flda@odoo.com> Co-authored-by: Anthony Hendrickx (anhe) <anhe@odoo.com> Co-authored-by: Alexis Lacroix (laa) <laa@odoo.com> Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com> Co-authored-by: Adrien Minne (adrm) <adrm@odoo.com> Co-authored-by: Ronak Mukeshbhai Bharadiya (rmbh) <rmbh@odoo.com> Co-authored-by: Dhrutik Patel (dhrp) <dhrp@odoo.com> Co-authored-by: Rémi Rahir (rar) <rar@odoo.com> Co-authored-by: Pierre Rousseau (pro) <pro@odoo.com> Co-authored-by: Vincent Schippefilt (vsc) <vsc@odoo.com> Co-authored-by: Marceline Thomas (matho) <matho@odoo.com>
The website builder now includes a searchable selector for long option lists, starting with form field types. This makes it faster and easier for users to find the right form field when editing website forms.
Original PR description
*: html_builder This PR introduces a `BuilderSearchSelect` component that has a search functionality, making it easier to find and choose items from large lists of options. It will be initially enabled for the "form field types" selector to simplify picking a field type option. task-5398633 Forward-Port-Of: odoo/odoo#271821
Users can now embed whiteboards from Miro, Google Docs, Figma, and Canva directly in supported Odoo content. This makes project tasks and to-do items better suited for collaborative planning and design work without leaving Odoo.
Original PR description
Supported providers: - miro.com - docs.google.com - figma.com - canva.com task-6102610 Forward-Port-Of: odoo/odoo#262181
Event badges now make key instructions and QR codes easier to use, including when printed and folded. The event session planning experience is also cleaner, with simpler reminder and favorites messaging and fewer confusing badge labels from single-choice questions.
Original PR description
Backport of https://github.com/odoo/odoo/pull/287446 which one is a Forward-Port-Of: https://github.com/odoo/odoo/pull/285915 because freeze v20 meanwhile Several diff for OXP [IMP] event: improve A4…
Backport of https://github.com/odoo/odoo/pull/287446 which one is a Forward-Port-Of: https://github.com/odoo/odoo/pull/285915 because freeze v20 meanwhile Several diff for OXP [IMP] event: improve A4 foldable badge -> required for oxp kenya - if you have ticket instruction, use it on badge + add qr code always visible [IMP] event: don't show tag (question reply) if only one choice -> fix case of "yes, agree" show on badge as tag "yes" [IMP] website_event_track: typo and make it less verbose -> FP quick pass usability [IMP] website_event_track: replace fa-bell by fa-star -> why ? not sure better after... but FP request -> + add menu whishlist directly in event sub menu ## deploy ``` views = [ 'website_event_track.agenda_main_track', 'website_event_track.track_card', 'website_event_track.tracks_search', 'website_event_track.event_track_aside_other_track', 'website_event_track.track_widget_reminder', ] from odoo.upgrade import util for xmlid in views: util.update_record_from_xml(env.cr, xmlid) ``` Forward-Port-Of: odoo/odoo#285915 Forward-Port-Of: odoo/odoo#288790
Product availability searches based on free stock are now much faster, especially in databases with many inventory records. This reduces waiting time and database load when users filter or search products by available quantity.
Original PR description
**Problem:** Searching on free_qty can be slow when there are many stock.move and stock.quant. The search method uses _compute_quantities_dict() to compute the free_qty based on stock.move and…
**Problem:** Searching on free_qty can be slow when there are many stock.move and stock.quant. The search method uses _compute_quantities_dict() to compute the free_qty based on stock.move and stock.quant, which is expensive. **Solution:** A similar field, qty_available, has a faster search method based on stock.quant only. This method can be adapted to support fast searching on free_qty by also aggregating the reserved_qty and subtracting it from the qty_available. **Perf Table:** Record: product.template, with ~20k stock.picking and most products w/o moves. |Record count|Time before|Queries before|Time after|Queries after| |------------|-----------|--------------|----------|-------------| |10k |2.14s |214 |370ms |45 | |50k |6.06s |644 |829ms |50 | |100k |11.42s |1196 |1.05s |46 | opw-6266096 Forward-Port-Of: odoo/odoo#287363 Forward-Port-Of: odoo/odoo#270434
This change improves how Odoo handles updates to core field properties in the Base module. It supports customization workflows more reliably, including scenarios driven by AI-assisted configuration.
Original PR description
For Customizing AI prompt 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
Spanish electronic invoicing screens are being streamlined so cancellation requests use a single action menu entry instead of separate buttons. The change also makes key JSON files easier to find by keeping SII data with its related document and showing Veri*Factu JSON attachments in document lists.
Original PR description
During the l10n_es [EDI rework](https://www.odoo.com/odoo/project/967/tasks/6175455), the PO introduced some tweaks that would be nice to have. As those tweaks have nothing to do with the rework, we have decided to add them in a different PR: - Replace the direct SII/TicketBAI cancel buttons with a server action bound to the form's action menu, so cancellation goes through a single "Request Cancellation" entry instead of a dedicated button. - Expose the raw JSON attachment on Veri*Factu documents in the list view. - Attach the SII JSON payload to the SII document record itself instead of the invoice, so it travels with the document it belongs to. Here is the edi-rework [PR](https://github.com/odoo/odoo/pull/267440) task-6523345 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The calendar event 'Linked to' field now suggests only relevant business records, making it easier for users to choose the right related item. The reference selector also supports searching and filtering, reducing long lists and speeding up selection.
Original PR description
Improve the models suggested in the "Linked to" field on the "calendar.event" model. We should only have business models. Also improving the reference field to use a select menu instead of the regular select element. This allows to search for a model and reduce the selection depending on the search term. Task-6563387 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#287832
Users can now open the same quick action menu from the members panel and messaging menu items, not just individual messages. This makes common mail and Discuss actions easier to access and reduces accidental text selection when using long-press on iOS Safari.
Original PR description
Before this PR, we could right-click on messages to show the message actions. This feature was only limited to messages. This PR adds the same feature in member panel and messaging menu item. Also drastically reduces chances for long-press on iOS Safari to trigger accidental text selection. Task-6463002 Forward-Port-Of: odoo/odoo#273806
The AI Agent interface now makes it easier to choose which subagents are allowed to help with a task. This improves clarity for users configuring AI agents and should reduce selection mistakes.
Original PR description
task-[6578342](https://www.odoo.com/odoo/2366/tasks/6578342) Forward-Port-Of: odoo/enterprise#131951
Studio users can now choose an end date field when configuring date filters in search views. This helps businesses create more accurate date-range filters, especially for records that span a start and end date.
Original PR description
This commit is the counter part of odoo/odoo#288280 and allows to select an end_date field on date filters in studio. Task-6546268
Belgian payroll now supports transport reimbursement for each separate home-work trip when JC302 employees or workers have interrupted shifts on the same day. This helps ensure private car and bicycle reimbursements match the actual number of qualifying commutes, while public transport season-ticket handling stays unchanged.
Original PR description
1. Employees and workers under JC302 with interrupted shifts can receive transport reimbursement for each home-work trip, based on separate work entries on the same day. 2. Public-transport season-ticket handling remains unchanged. Task-6532008
Knowledge article editors can now opt in to a whiteboard embedding command from the editor menu. This makes it easier for teams to add visual collaboration content directly into Knowledge articles when needed.
Original PR description
See https://github.com/odoo/odoo/pull/262181 This commit make the knowledge article editor opt-in to the whiteboard embedding powerbox command. task-6102610 Forward-Port-Of: odoo/enterprise#116588
Payroll dashboard warnings now load more efficiently by reducing expensive background processing and making warning actions more flexible. This should improve dashboard responsiveness for payroll teams, especially where many warning checks are displayed.
Original PR description
Prior to this commit, the payroll dashboard could experience high latency during load times. This was primarily caused by the heavy use of `safe_eval` to compute Python-based warning actions…
Prior to this commit, the payroll dashboard could experience high latency during load times. This was primarily caused by the heavy use of `safe_eval` to compute Python-based warning actions (`evaluation_code`), as well as the rigidity of the `button_action` (Many2one) field which limited dynamic UI routing. This commit refactors the warning action system to fix these performance issues and clear technical debt: - Replaced the relational `button_action` field with a generic JSON-driven `action_config` text field on `hr.payroll.warning`. - Migrated all existing data records in `hr_payroll`, `l10n_be_hr_payroll`, and `l10n_be_hr_payroll_account` to use `action_config`. - Converted several Python-evaluated warnings into pure `domain` warnings by moving static dictionary generation and `group_by` clauses into the JSON configuration, allowing the backend to skip `safe_eval` entirely. - Improved the separation of concerns: the backend strictly handles ORM detection (`warning_domain`), while `action_config` handles UI routing. - Set up the infrastructure to support volatile frontend context keys (e.g., for autofocusing fields) without polluting the ORM context.
Studio now handles AI options directly on regular fields, so users no longer need a separate AI-field drop action. The update also improves wording and the Studio promotion dialog, making the AI field experience clearer and more consistent.
Original PR description
Forward-Port-Of: odoo/enterprise#131981
Appointment booking screens and emails are clearer and more consistent. Users get better default reminders, more readable appointment cards, improved popover layout, and confirmation emails that greet the right attendee when bookings are made internally.
Original PR description
This commit improves the UI of appointment: * To improve the readability of the "total_capacity_reserved" field in the records of the kanban view, its background color is lightened. * The default…
This commit improves the UI of appointment: * To improve the readability of the "total_capacity_reserved" field in the records of the kanban view, its background color is lightened. * The default alarm of the appointment type is set to 1 day. * The name of the appointment booker is no longer used for the greeting in the confirmation email since it is not set if the appointment is done from the backend. Instead of it, the first of the attendees is used and if they do not exist, the organizer is used. * The popover is improved. Margins are placed between the buttons in the footer, to avoid having them stuck to each other. A maximal width is set for the videocall_location field, this way it does not impact the size of the popover. * To avoid a technical label, the default falsy label of CalendarEvent.resource_ids, which is "Undefined Attendee", is replaced by "Unassigned". Community PR: https://github.com/odoo/odoo/pull/288801 Task-6566639 Forward-Port-Of: odoo/enterprise#131840
Belgian payroll now automatically sets a disabled spouse's fiscal status to "without income" so the correct withholding tax scale is applied. This helps ensure employees with a disabled spouse receive the appropriate tax treatment and reduces manual payroll errors.
Original PR description
When an employee has a disabled spouse, the employee automatically qualifies for withholding tax scale 2 (Bareme II), so the spouse fiscal status must be "without income". If Disabled Spouse is set to True, the spouse fiscal status is now automatically set to "without_income". In Disabled Spouse is not selected, the fiscal status can be manually changed to any other option. task-6542651
Belgian payroll users now get clearer warnings when employees are missing time off allocations, helping HR teams spot and resolve issues before payroll or leave planning is affected. The employee and time off dashboards also make it easier for managers to grant the needed allocations directly from the relevant screens.
Original PR description
mock- https://link.excalidraw.com/l/65VNwvy7c4X/1BoTHSkGUpa - Added new warning on payroll dashboard for employees not having allocation. Doesn't apply to employees without contract and salary…
mock- https://link.excalidraw.com/l/65VNwvy7c4X/1BoTHSkGUpa
- Added new warning on payroll dashboard for employees not having allocation.
Doesn't apply to employees without contract and salary structure dirigeant.
- Added "Not defined" on employee form o-stat-value when there is no allocation.
- Added new button "Grant Time off" on employee Time Off dashboard only visible
to time off managers and when no allocation. On click-> new allocation request
form opens.
- Updated fields for New Allocation request form from Time Off Dasboard:-
- Removed name field.
- Rename "Ask for" to "Duration" for `number_of_day_display` field.
- Change time type field to widget time type tags
- New button "Grant" in place of "Submit Request" directly approves requests.
- Updated the popover form for Time off gantt:
- Warning message on no allocation shows is in alert-danger class.
- If there is no enough allocation then "Allocate & Add" appears which
creates required allocations with leaves.
task-6476239Resolved issues and error corrections
Fixed an issue where online shop and product configurator pages could show an incorrect reference price when a product was sold in a different unit of measure than its base unit. This ensures customers see consistent price-per-unit information, reducing confusion and pricing errors.
Original PR description
Current behavior: On /shop and in the product configurator, the reference price (price per UoM, e.g. $/kg) shown next to a product's price is wrong when the website only sells that product in a uom…
Current behavior: On /shop and in the product configurator, the reference price (price per UoM, e.g. $/kg) shown next to a product's price is wrong when the website only sells that product in a uom other than its base uom. Steps to reproduce: - Enable "Units of Measure" and "Product Reference Price" - Create a product priced per g, with base_unit_count set for kg - Add kg as a packaging and restrict the base uom (g) on the website - Open /shop, or the product configurator, for that product Expected behavior: The reference price matches the "Price Per Unit" shown on the product's own form. Cause of the issue: `_get_sales_prices` and `_get_basic_product_information` divided the price directly by base_unit_count, without converting it from the uom used to compute that price (the main/pricing uom) to the product's base uom. When both differ, the result is off by their ratio. Fix: Convert the price to the base uom before dividing it by base_unit_count, in both the shop listing and the product configurator. opw-6566849 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288079
Features or functions removed from Odoo
This update removes outdated internal functions across Odoo's core framework and several built-in apps. It helps keep the platform cleaner and easier to maintain, with limited direct impact for everyday users but possible adjustments needed for custom integrations relying on deprecated behavior.
Original PR description
Remove most of deprecated functions. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Point of Sale online payments no longer include individual customer details in batched transfers. This prevents closing issues when a POS session contains payments from multiple customers, making end-of-day processing more reliable.
Original PR description
We were adding the partner of the payment in a batched transfer, which caused issue with the closing when several partner were involved in the same session. We're now avoiding adding the partner in the batched transfer. Forward-Port-Of: odoo/odoo#289156
Opening records with search panel filters linked to archived or restricted items no longer causes a crash. The system now ignores hidden filter groups that users cannot access, improving reliability in affected views such as Social Marketing posts.
Original PR description
Opening a view whose search panel has a `select="multi"` field of type many2many crashes when a record is only linked to comodel records the user cannot see: File "/addons/web/models/models.py", line…
Opening a view whose search panel has a `select="multi"` field of type many2many crashes when a record is only linked to comodel records the user cannot see: File "/addons/web/models/models.py", line 1496, in _search_panel_domain_image id_, display_name = group_id_name(group[field_name]) TypeError: cannot unpack non-iterable bool object Steps to reproduce: - On a db with Social Marketing installed, social accounts are automatically created per website - Open Social Marketing > Posts - Create a new Post with an social account - Open Settings > Social Accounts > the chosen social account - Archive it - Get back to the Post created above => crash `_search_panel_domain_image` restricts the domain with `(field_name, '!=', False)`, which is evaluated on the comodel with sudo and active_test=False, while the group by of the same query joins the comodel through `_search`, which applies record rules and active_test. A record whose values are all archived or hidden by a record rule therefore passes the condition but ends up in a group with no value, which the image loop unpacks as a tuple. This commit skips those groups: their values have no place in the range anyway. Seen on runbot, where `runbot.build.error.trigger_ids` points at `runbot.trigger` records that are archived or restricted by the project group rule. Forward-Port-Of: odoo/odoo#288731 Forward-Port-Of: odoo/odoo#288503
This fixes an issue where creating or editing a technical view could fail even when the selected model and view layout were valid. Odoo now applies the chosen model before validating the view layout, preventing false validation errors and making view setup more reliable.
Original PR description
**Problem:** Creating a view from Settings > Technical > User Interface > Views fails with a validation error, even though both the model and the architecture are valid. **Steps to reproduce:** 1. Go…
**Problem:**
Creating a view from Settings > Technical > User Interface > Views fails with a validation error, even though both the model and the architecture are valid.
**Steps to reproduce:**
1. Go to Settings > Technical > User Interface > Views
2. Click New
3. Set the View Name and pick a model in "Model of the view"
4. Set the View Architecture to `<list><field name="name"/></list>`
5. Save
**Current behavior:**
The record cannot be saved:
Error while validating view near:
<list __validate__="1"><field name="name"/></list>
Model not found: False
Duplicating an existing view and editing the copy works, so the issue only shows up on brand new records.
**Expected behavior:**
The view is saved, and validated against the model that was picked.
**Cause of the issue:**
The form edits `model_id`, a non-stored `Many2one` whose inverse `_inverse_compute_model_id` is what actually fills the stored `model` field. The architecture is edited through `arch_base`, whose inverse chain ends in `_inverse_arch` writing `arch_db`; `write` then runs `_check_xml`, which validates the architecture against `view.model`.
Both values therefore reach the record through inverse methods, so the order those run in decides whether `model` is set by the time the architecture is validated. That order became deterministic in this version: the ORM sorts inverse groups by `(field.write_sequence, field index)`. Both fields keep the default `write_sequence` of 0, which leaves the declaration order to break the tie, and `arch` is declared well before `model_id` — so the architecture is validated while `model` is still empty.
The same ordering also breaks a plain edit: changing the model and the architecture of an existing view in a single save validates the new architecture against the previous model.
**Fix:**
`write_sequence` is the ORM's hook for exactly this kind of ordering dependency, so a negative one on `model_id` states the real constraint — the model has to be known before anything is validated against it — rather than leaving it to where the fields happen to sit in the class. It also covers the write path, which a create-only workaround would miss.
opw-6467991
Forward-Port-Of: odoo/odoo#284143This fixes an issue when marking several manufacturing orders as done at the same time. The system now applies completion updates to the correct order, preventing the wrong stock moves from being marked as picked.
Original PR description
The lambda should refer to `p.qty_producing`. Since `button_mark_done` already has a `for production in self:` loop earlier, `production` is filtered, and the lambda evaluates the `qty_producing` of the last MO for all of them. When marking multiple MOs as done, it sets `picked=True` on the wrong moves. Introduced in bb56600bfc81. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288632
Fixes a Point of Sale issue where opening a company's orders could show a blank screen if one related delivery address had no name. Orders for those unnamed address contacts now display using the parent company name, keeping staff able to review customer orders without interruption.
Original PR description
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click…
Steps to reproduce: - Create a company contact with a delivery address that has no name - Make a PoS order for the delivery address and pay it - Open the PoS, open the customer list, and click "Orders" on the company Issue: The screen goes blank. Cause: The ticket screen is opened with the company name as search term. The server domain searches on `partner_id.complete_name`, so the orders of the address contact are returned too. They are then fuzzy matched on `getPartnerName()`, which returns `false` for a partner without a name, and `fuzzyLookup` calls a string method on it. The error is raised during rendering, so the whole app is destroyed. Fix: Make `getPartnerName()` always return a string, and fall back on the parent name, like the complete name does. This way the orders of the address contact are still listed when searching on the company name, instead of being filtered out by an empty name. opw-6578541 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288880
Installing Stock Fleet now correctly enables the required batch transfer setting so its features remain available after changing settings. This prevents the app from being accidentally marked for uninstall when users save configuration changes. Point of Sale stock permissions are also aligned so users can work with batch-related transfers consistently.
Original PR description
Steps to reproduce: - Install stock, stock_fleet - Open the settings - Enable packages (or do any other change) Previously, `stock_fleet` had `stock_picking_batch` as its dependency. However, since the merge of that module into `stock`, we still consider that this group should be enabled to be able to use `stock_fleet`'s features. However, when `stock_fleet` is installed through the apps/-i, the group won't be set, and when opening the settings, since the group isn't set the `onchange` triggers to set the `module_stock_fleet` to False, prompting its uninstall when saving the settings. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288352
Administrators can now view and create API keys for other users, making it easier to manage automated or bot accounts that rely on API keys instead of passwords. This helps teams centrally maintain script and integration access without logging in as each user.
Original PR description
This make it possible for an admin to list and also create API keys for other users. This is useful to manage "bot" users, i.e. accounts with no password that are only used with API keys in scripts.
Fixed an error that could prevent users from duplicating rental products when pricing rules were attached to a variant without variant-defining attributes. This keeps product setup workflows reliable and avoids interruptions when copying products such as rental listings.
Original PR description
Issue: Duplicating a product raises `ValueError: False is not in list` when the product has a pricelist rule set on its variant and that variant holds no attribute value. Steps to reproduce: 1.…
Issue: Duplicating a product raises `ValueError: False is not in list` when the product has a pricelist rule set on its variant and that variant holds no attribute value. Steps to reproduce: 1. Install `website_sale_renting` with the demo data. 2. Open the `Luxury Room` product, whose only attribute, `Breakfast`, doesn't create variants, and which has a pricelist rule set on its variant. 3. Duplicate it. Cause: `get_combination_key` builds the combination of a variant through `product_template_variant_value_ids.mapped(...)`. On an empty recordset, `mapped` calls the given function once with that empty recordset instead of not calling it at all, so `attribute_line_id.id` is `False` and `attribute_line_ids.index()` raises. A variant holds no attribute value when its template has no attribute line, or only lines whose attribute doesn't create variants. Fix: Iterate `product_template_variant_value_ids` instead of mapping over it, so that a variant without attribute value gets an empty combination key. Version:20 (bugfix) Forward-Port-Of: odoo/odoo#288252
Fixed an issue where manufactured lot-tracked products could be placed in the default stock location after components were unreserved and re-reserved. Putaway rules are now applied after production, helping ensure finished goods are stored in their intended warehouse locations.
Original PR description
Currently, when the user re-reserves a component and produces an MO for a lot-tracked product, putaway rules stop working. ## Steps to produce: - Install the Manufacturing application. - Go to…
Currently, when the user re-reserves a component and produces an MO for a lot-tracked product, putaway rules stop working. ## Steps to produce: - Install the Manufacturing application. - Go to Settings and enable Lots & Serial Numbers and Storage Locations. - Create a product named 'Laptop' and set it to be tracked by Lots. - Create a product named 'Graphics card' with some quantity on hand. - Create a Bill of Materials (BoM) for the Laptop with Graphics card as a component. - Create a location named 'Laptop Shelf' with 'WH/Stock' as its parent location. - Create a putaway rule: - When a product arrives in: WH/Stock - Product: Laptop - Store to: WH/Stock/Laptop Shelf - Create and confirm a Manufacturing Order (MO) for the Laptop. - Unreserve the components, open Details, and re-add the Graphics card. - Click Produce All and check the on-hand quantity list view of the Laptop. ## Issue: The Laptop is stored in WH/Stock instead of WH/Stock/Laptop Shelf. ## Root cause: When the user presses the unreserve button the finished move is passed to `_do_unreserve` https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L2407 which unlinks all the move lines on finished product moves if they are not picked: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L1043 Now when the user presses the Produce All button, `button_mark_done` is called, which calls `_post_inventory` at: https://github.com/odoo/odoo/blob/a51ca77c825d2dba326dd540e2b4c04ef6c2385d/addons/mrp/models/mrp_production.py#L2236 `_post_inventory` assigns `lot_ids` to the finished product moves at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L1928-L1930 This calls `_set_lot_ids` which creates a move line with quantity 1 at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L679-L683 After that, `_post_inventory` sets the quantity for the finished product moves at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/mrp/models/mrp_production.py#L1935 Which calls `_set_quantity` and the delta quantity is now zero at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L477-L483 Because `_set_lot_ids` has already created a move line that satisfies the move quantity, `_process_increase` is not called. Since `_process_increase` is not called, `_set_quantity_done` is never called at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L463-L465 Since `_set_quantity_done` is not called, `_apply_putaway_strategy` is never called within that function at: https://github.com/odoo/odoo/blob/157874aad3aebef5bc9268de6e17530641107e31/addons/stock/models/stock_move.py#L2565 As a result, the laptop ends up in stock instead of on the shelf. This issue did not occur in lower versions because the feature to produce multiple lots was introduced in version 19.0 by the following commit: https://github.com/odoo/odoo/commit/4bb4e08066449177f89382718ceadd840ce90d0e Before this commit, only `_set_quantity` was called. Since no move line was created because lot_ids were not set in `_post_inventory`, `_process_increase` was called, which then called `_apply_putaway_strategy`, so the putaway rules were applied correctly. ## Solution: Apply putaway rules in the post-inventory function after the user manufactures a product, ensuring the items are placed in the correct locations and preventing products from being misplaced even when putaway rules are defined. opw-6517324 Forward-Port-Of: odoo/odoo#288739 Forward-Port-Of: odoo/odoo#287390
This fixes a crash that could occur when users fetched the processing status of Romanian e-Factura documents from SPV. It ensures signature files are saved correctly, allowing businesses to continue invoice status checks without interruption.
Original PR description
Steps to reproduce: - Send an invoice to the SPV (Romanian e-Factura). - Once the SPV finished processing it, click "Fetch Status" on the e-Factura document. - Odoo crashes with binascii.Error:…
Steps to reproduce: - Send an invoice to the SPV (Romanian e-Factura). - Once the SPV finished processing it, click "Fetch Status" on the e-Factura document. - Odoo crashes with binascii.Error: Incorrect padding Cause of the issue: _request_ciusro_download_answer extracts the signature file from the SPV zip answer and re-serializes it with etree.tostring(...), which gives plain XML bytes, not base64 But _l10n_ro_edi_fetch_invoice_sent_documents and the 3 other callers that persist that signature still ran it through base64.b64decode() before wrapping it in BinaryBytes (which already expects raw bytes, not base64). Decoding real XML as base64 only "works" by accident when the XML happens to contain a number of base64-alphabet characters that's a multiple of 4 after the invalid ones get silently stripped - otherwise it blows up with "Incorrect padding". This got introduced by 41fe2ebdb9cc (fields.Binary return BinaryValue), which flipped a base64.b64encode() call to base64.b64decode() at these 4 spots instead of just dropping the base64 call entirely. A previous fix (0750ee145ae2) already fixed the sibling issue on the invoice's own attachment_raw but missed these. Solution: Drop the erroneous base64.b64decode around the signature's attachment_raw at the 4 call sites, and fix the tests that were mocking attachment_raw as base64-encoded instead of raw bytes opw-6562123 Forward-Port-Of: odoo/odoo#288140
Sales order previews and reports now show the correct section quantity, unit of measure, and unit price. This prevents customers and sales teams from seeing misleading quantities or pricing, especially on mobile previews and sections with hidden composition.
Original PR description
The section quantity and pricing displayed in the sale order preview and report are inconsistent in some cases. After https://github.com/odoo/odoo/commit/34c47e5493bc36af338efbb54050e060b04f8ea3, the section quantity and UoM are dynamically displayed in the preview, but the mobile preview still shows the static `1 Units`. Additionally, when a section has `hide_composition` enabled, the unit price is incorrectly displayed as the section's total price instead of being based on the section quantity. This PR fixes both issues by: - Displaying the dynamic section quantity and UoM in the mobile preview. - Computing the section unit price from its total price and quantity when the composition is hidden. Forward-Port-Of: odoo/odoo#288743
Changing the pricelist on a saved quotation now correctly updates prices for combo product items. This prevents quotes from showing outdated combo item prices and helps ensure customers receive pricing that matches the selected pricelist.
Original PR description
Versions -------- - 20 Steps ----- 1. Create and save a quotation with a combo product; 2. change the pricelist for one giving a different price on that combo product. Issue ----- The combo item…
Versions -------- - 20 Steps ----- 1. Create and save a quotation with a combo product; 2. change the pricelist for one giving a different price on that combo product. Issue ----- The combo item lines keep the prices given by the previous pricelist. Cause ----- Commit e78f729d removed the `Update Prices` button and moved `_recompute_prices` to the `pricelist_id` onchange. The lines are therefore in memory records now, whereas they were stored ones when the recompute was triggered by the button. `_get_combo_item_display_price` prorates the price of the combo product, computed on the line returned by `_get_linked_line`. As of commit f1e68cc, that method returns `linked_line_id` whenever it is set, i.e. the stored line, whose `order_id` is the stored order, hence still the previous pricelist. Solution -------- When the line is an in memory record, look its linked line up among the order lines, by origin, so that the in memory version is returned. opw-testing-day-v20 Forward-Port-Of: odoo/odoo#288543
Changing an employee's working schedule no longer shifts the dates on existing time off requests for employees in certain time zones. The request dates remain unchanged while the system still recalculates the time off duration based on the new schedule.
Original PR description
## Description Changing an employee version's working schedule recomputes the internal dates of subsequent time off requests. The `hr.leave` write override then mirrors these recomputed UTC values…
## Description Changing an employee version's working schedule recomputes the internal dates of subsequent time off requests. The `hr.leave` write override then mirrors these recomputed UTC values back to the request dates. For timezones ahead of UTC, midnight on a non-working day can therefore shift the requested date to the previous day. This fix marks the schedule-triggered re-computation as an internal fast update, ensuring that the dates selected on the time off request remain the source of truth while still allowing the leave duration to be recomputed according to the new working schedule. Regression coverage is added to ensure that changing an employee's working schedule: * preserves the dates selected on existing time off requests; * correctly recomputes the leave duration according to the new schedule. ## Steps to reproduce 1. Create an employee with timezone Europe/Brussels and a Monday–Friday working schedule. Before creating any leave, set the employment record’s effective date and contract start date to 3 March 2025. 2. Create a full-day time off request from 23 to 26 February 2026. Its duration is initially four working days. 3. Change only the employee’s working schedule to a duration-based Tuesday–Friday schedule, with 7.6 hours per working day and Monday off. Leave the effective date and contract dates unchanged, then save. **Before the fix:** the request’s start date incorrectly shifts to 22 February. **After the fix:** the dates remain 23–26 February, while the duration correctly decreases to three working days. opw-6456107 Forward-Port-Of: odoo/odoo#281385
The email plugin can now keep users signed in for longer, with a default token duration of seven days. Administrators can adjust this duration through a system setting, reducing the need for daily logins while keeping access limited to the Outlook plugin endpoints.
Original PR description
Purpose ======= Allow customizing the expiration time of tokens, so users don't need to login everyday in the plugin. This is customized with a system parameter, with a default of 7 days. Those tokens are limited to endpoints `auth="outlook"`. Task-6466253 Forward-Port-Of: odoo/odoo#288572 Forward-Port-Of: odoo/odoo#287767
Connecting bank synchronization no longer removes payment options that users previously configured on bank journals. This preserves existing incoming and outgoing payment settings while still adding any missing default options, reducing reconfiguration work after syncing a bank account.
Original PR description
**Steps to reproduce:** - Install Accounting - Configure "Bank" journal: * Add several incoming payments * Add several outgoing payments - From Accounting dashboard, connect Bank (e.g. Odoo Bank Sync Demo) **Issue:** After bank sync, all the incoming/outgoing payments added on the Bank journal are removed. Only the default ones are re-created. **Solution:** Keep all the existing incoming/outgoing payments (i.e. payment method lines) and only create the default ones for the payment method types that don't exist. opw-6424407 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#281658
The Attendance app calendar now greys out holidays and other unavailable days, making it easier for users to understand attendance context at a glance. This aligns the calendar experience with the Time Off app and reduces confusion when reviewing schedules.
Original PR description
The calendar view on Attendance app should look similar to the one on Time Off app, with holidays greyed out. task-6576916 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
This update prevents users from being sent back to the login screen after signing in while a websocket-based service such as live chat is active. It ensures the existing session is saved without overwriting the newly logged-in session in the browser, improving login reliability.
Original PR description
Before this commit, signing in from a page holding a websocket connection could land the user back on the login form. This happens because the websocket handshake marks the session dirty to get it written on disk, and _save_session sends a "session_id" cookie back for every dirty session. A handshake answered between the login response and the request that follows it therefore puts the pre-login session back in the browser. Note that the failure was seen on master, where a livechat tour signs in with the bus connected, but every version since 17.0 answers the handshake the same way. This commit saves the session from the handshake itself, so that the response carries no session cookie. https://runbot.odoo.com/odoo/error/947173 Forward-Port-Of: odoo/odoo#288667 Forward-Port-Of: odoo/odoo#287960
This update fixes cases where Odoo could silently use desktop-style behavior on small screens or hide debug-only interface elements because outdated internal flags were still being read. It also adds stronger checks so similar mistakes are caught during testing instead of reaching users.
Original PR description
Removes depracated `env.isSmall` and `env.debug` and make it throw if used (to prevent forward port mistakes) Enterprise PR: https://github.com/odoo/enterprise/pull/130698 Forward-Port-Of: odoo/odoo#288843
Payslip corrections now use the appropriate accounting dates for both the refund and the corrected payslip. This helps payroll and accounting records reflect the correct timing, reducing confusion during financial reconciliation.
Original PR description
-The refund payslip's accounting date is set to the day of the correction (since it's put as paid directly). -The accounting date of the correction payslip us set to the day of its validation Forward-Port-Of: odoo/enterprise#131902
Payslips now calculate attendance-based work entries using paid working time only, excluding any recorded unpaid break duration. This prevents employees from being paid for break time when attendance records include unpaid breaks.
Original PR description
Steps to Reproduce: 1. Create an attendance from 8AM to 4PM (8 hours) 2. Add 2h of Break Duration -> This reduces the worked time from 8h down to 6h. 3. Generate a payslip for this employee -> 8H of attendance is included, but it should be 6H instead Fix: Exclude `attendance.break_duration` -if there is one set- from attendance work entries' total value. task-6573873
Blog imports now correctly handle multiple blogs with the same name by tracking them with their original external identifiers. This prevents posts from being linked to the wrong blog or failing to match during website generation.
Original PR description
Blogs with the same name was creating a conflict in the mapping of id to created odoo blog, which meant that the blog post matching with the blog itself was failing. This fix introduces external ids for the blogs which is the id of the foreign website, making it much easier to keep track of the mapping between the external records and the created odoo records.
This fixes several AI chat issues so users can again show agent steps, see accurate tool activity status, and get clearer chat names after the first message. It also aligns AI agent name styling with other chat participants for a more consistent experience.
Original PR description
Purpose: --------- #### Show agents steps toggler in composer actions The option to toggle the agents steps is missing in the composer actions. There were 2 ways of adding composer actions for AI…
Purpose: --------- #### Show agents steps toggler in composer actions The option to toggle the agents steps is missing in the composer actions. There were 2 ways of adding composer actions for AI chats (probably after resolving conflicts). #### Update tool status when tools are called The tool status in ai chats is not updated anymore when tools are called. This happens because `_store_session_fields` is called when the tool results are sent to the LLM since commit [1] Therefore, we are sending the tool status which is immediately overridden by this call to `_store_session_fields`. This commit fixes it by removing the direct updates, and let `_store_session_fields` handle this case as well. This PR also changes the font-weight of the agent header in ai chats to match the one of the other users #### Always rename ai chats on first message Since commit 1, AI chats created with a channel title are not renamed anymore. This commit restores the previous logic, which is to always rename AI chats on the first message (to make it easier to find them in the chat history). [1] a7523d2e84edb258f42141b3154493a8a8e12e58 Forward-Port-Of: odoo/enterprise#131681
Asset depreciation is now calculated using exact monthly portions when periods cover full months or years. This avoids small day-count differences, helping accounting results align more consistently with expected depreciation schedules.
Original PR description
Old behavior: Linear depreciation for constant periods used a day-based ratio (days_passed / total_days). New behavior: When evaluating full periods (exact months or years), the engine now bypasses day-based logic and uses strict monthly fractions: Depreciable_Value * (Period_Months / Total_Months).
Payroll warnings for split sick leave are now shown even when a leave request has not yet been saved. Employees and payroll teams can also see the warning directly on the employee form, helping avoid missed payroll issues.
Original PR description
Before this commit, the split warning was searched in the database, so a request not saved yet never showed it. The employee form showed no payroll warning either. After this commit, the warning filters the given records, and the banner is also on the employee form. taskid-6578279
Submitting a draft Denmark VAT report no longer triggers an error caused by missing report context. This ensures Danish VAT filings can proceed reliably without users being blocked at submission time.
Original PR description
Currently, an error occurs when the user submits the draft Denmark VAT report. ``` TypeError: AccountReport.get_options() missing 1 required positional argument: 'previous_options' ``` When the user submits the draft Denmark VAT report, it gets the calculated lines of the current report by calling get_options method. However, get_options() requires the previous_options argument [1]. Since this argument is not passed [2], it raises the error. This commit ensures that an empty dictionary is passed as the previous_options argument when getting the report lines. [1]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/account_reports/models/account_report.py#L2126 [2]- https://github.com/odoo/enterprise/blob/e2aa3073dafa9377654c761e371a660415db1e88/l10n_dk_reports/wizard/tax_report_wizard.py#L216 sentry-7380293202 Forward-Port-Of: odoo/enterprise#130320
This fix prevents the Bulgarian SAF-T setup from recreating deleted standard accounts or taxes with incomplete information. It only updates existing standard records, reducing the risk of setup errors or failed upgrades when customers have customized their accounting data.
Original PR description
If a client deletes a standard account or tax, its XMLID can no longer be resolved during the post-init hook. In that case, _load_data() may create a new record using only the provided data, e.g.:…
If a client deletes a standard account or tax, its XMLID can no longer be resolved during the post-init hook. In that case, _load_data() may create a new record using only the provided data, e.g.:
```
('l10n_bg_691001', {'l10n_bg_saft_account_code': '624'})
```
This can lead to incorrect records.
Only update records whose XMLIDs still exist, avoiding the creation of new records when the corresponding standard record has been deleted.
```
File "/home/odoo/src/enterprise/19.0/l10n_bg_saft/__init__.py", line 10, in _add_account_saft_code
Template._load_data({'account.account': Template._get_bg_saft_account_code()})
File "/tmp/tmpbu1ntr1j/migrations/account/0.0.0/pre-ensure-deferred-accounts.py", line 37, in _load_data
return super()._load_data(data, *args, **kwargs)
File "/home/odoo/src/odoo/19.0/addons/account/models/chart_template.py", line 697, in _load_data
created_records[model] = self.with_context(lang='en_US').env[model]._load_records(all_records_vals)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5196, in _load_records
records = self._load_records_create([data['values'] for data in to_create])
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 5103, in _load_records_create
records = self.create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/addons/account/models/account_account.py", line 1055, in create
)).create(vals_list_for_company)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/addons/mail/models/mail_thread.py", line 329, in create
threads = super(MailThread, self).create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/tmp/tmpbu1ntr1j/migrations/util/orm.py", line 267, in wrapper
return f(*args, **kwargs)
File "/tmp/tmpbu1ntr1j/migrations/base/0.0.0/pre-models-match_uniq.py", line 25, in create
return super().create(vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/decorators.py", line 369, in create
return method(self, vals_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4711, in create
records = self._create(data_list)
File "/home/odoo/src/odoo/19.0/odoo/orm/models.py", line 4887, in _create
cr.execute(SQL(
File "/home/odoo/src/odoo/19.0/odoo/sql_db.py", line 440, in execute
self._obj.execute(query, params)
psycopg2.errors.NotNullViolation: null value in column "account_type" of relation "account_account" violates not-null constraint
DETAIL: Failing row contains (734, null, 1, 1, null, null, null, t, f, f, 2026-08-25 08:18:15.751937, 2026-08-25 08:18:15.751937, no, f, null, null, null, null, 411).
```
upg-4611390
tbg-2902
Forward-Port-Of: odoo/enterprise#129145This fix ensures Belgian 13th month payslips only apply the Special Social Contribution when there is a regular monthly payslip for the same period. It prevents incorrect deductions when the 13th month payment is generated on its own, improving payroll accuracy for Belgian employees.
Original PR description
When we generate a 13th month payslip, we need to check if there is a monthly pay payslip in the same period. If not, the Special Social Contribution will be 0. task-6512347 Forward-Port-Of: odoo/enterprise#131844
Belgian payroll no longer applies private car reimbursement when an employee has no joint committee set. The update also standardizes how compensation thresholds are interpreted, reducing incorrect payroll calculations in edge cases.
Original PR description
Issue: - When no JC (joint committee) was specified for given employee, JC302 rule was applied to compute private car compensation. - Unconsistent format for representing intervals by single key in param rules. With key1 < key2 < key3, associated key values to key2 should be read as belonging to [key2, key3[ in one case, and ]key1, key2] in another case. Solution: - No JC, means no private car compensation. - Associated key value to key2 belongs to interval [key2, key3[ - Fix edge cases (value below min requirement for compensation) task: 6531956
Several Odoo Enterprise screens now use the correct mobile and debug-mode settings after an underlying framework change. This prevents menus and layouts from silently choosing the wrong desktop view on phones and restores debug-only options where relevant.
Original PR description
Removes depracated `env.isSmall` and `env.debug` and make it throw if used (to prevent forward port mistakes) Community PR: https://github.com/odoo/odoo/pull/287002 Forward-Port-Of: odoo/enterprise#131886