Thursday, September 17, 2026
115 changes · master
New functionality added to Odoo
This update introduces Owl 3 and starts adapting Odoo's Hoot testing tools to work with it. It matters because it prepares the web platform and its automated tests for the next generation of the user interface framework, helping future development move forward more reliably.
Original PR description
### [ADD] Owl 3 ### [WIP] convert Hoot to Owl 3 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Enhancements to existing features
Vendor invoice imports now better recognize reverse charge taxes even when electronic invoice files list them as 0%. This helps keep imported tax and analytic information more accurate, reducing manual correction work for accounting teams.
Original PR description
Reverse charge taxes can be reported as 0% in the XML, meaning that we wouldn't be able to predict them even if e already set it right on a previous invoice for the same partner.Description of the issue/feature this PR addresses: Forward-Port-Of: odoo/odoo#287476
Resolved issues and error corrections
This fixes an issue where saved filter settings could fail to load when their stored text included leading or trailing spaces. The change makes filter handling more tolerant, helping users avoid errors caused by harmless formatting differences.
Original PR description
task-6578010 Forward-Port-Of: odoo/odoo#288643 Forward-Port-Of: odoo/odoo#288528
Code cleanup and technical improvements
This change removes obsolete mail code that was no longer used after chat windows were limited to channels. It reduces maintenance overhead without changing how users interact with the system.
Original PR description
Added in 55381b54fe68 to get `display_name` for ChatHub chat windows restored from local storage. f8c07b76f65c restricted ChatHub to channels only, removing the sole caller.
This update makes the Ecuador localization fully functional for Odoo 14, including accounting setup, tax rules, withholding taxes, document types, and bank account support. It helps Ecuador-based businesses comply with local tax requirements and use a more complete, multi-company-ready accounting configuration.
Original PR description
Purpose ======= The purpose of this commit is to provide a fully functional Ecuadorian localization tested for incoming v14, in compliance with current coding guidelines and practices, it takes into consideration minimum ifrs compliance, new tributary resolutions (from jan-2020 and mar-2020), document types, vat taxes, withhold taxes, and other taxes (ICE, IRBPNR), compatibility with further electronic documents, and several other requirements as detailed in file __manifest__.py Here a short description showcasing this module https://www.youtube.com/watch?v=kLI5c2xORWs&feature=youtu.be 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
Installing the Colombian electronic invoicing module is made faster for companies with large accounting histories. The change avoids time-consuming recalculation of existing invoice data during setup while keeping normal behavior for new and updated records.
Original PR description
- Use the `init_storage` field attribute to initialize the stored computed column `l10n_co_edi_type`, `l10n_co_edi_cufe_cude_ref`, `l10n_co_edi_state`, `l10n_co_edi_operation_type`,…
- Use the `init_storage` field attribute to initialize the stored computed column `l10n_co_edi_type`, `l10n_co_edi_cufe_cude_ref`, `l10n_co_edi_state`, `l10n_co_edi_operation_type`, `l10n_co_edi_commercial_state` directly in the database. - This prevents Odoo from computing and writing the field for all existing `account.move` records when installing `l10n_co_edi`. - This is particularly important for large databases with a high volume of Colombian accounting moves, where the initial computation can take too long and cause the module installation to hit the time limit. - Keep the compute method unchanged so the field continues to be computed normally for subsequent record creation or dependency changes. - Remove the `default` from `l10n_co_edi_commercial_state` since the `default` sets the field to `pending` while the compute sets it to `False` when no accepted EDI document exists. Move `pending` to `default_get` to preserve the current behavior while keeping the initialization consistent with the compute. **opw-6451331** Forward-Port-Of: odoo/enterprise#130808 Forward-Port-Of: odoo/enterprise#129507
Payroll now shows warnings about employee profile changes more broadly, including on the payroll dashboard and employee form. This helps payroll teams notice when an employee's data changed after a payslip was created, reducing the risk of processing outdated payroll information.
Original PR description
When you update an employee profile or create a new version covering a period of time already covered by a payslip, if you re-open the payslip, you'll see this warning: The employee's data has been updated since this payslip was created. -> Making it visible from the payroll dashboard and the employee's form. Warnings on `hr.employee` and having `warning_type == 'python'` were grouped by versions. Making them visible only if the right version was selected. Updating `_compute_issues()` so that if model is `hr.employee`, the warning is visible no matter the selected version. Task: 6435032
Time off entries now display as full visual bars for half-day and full-day absences in monthly and quarterly Gantt views. This makes leave schedules easier to read at a glance and reduces confusion when reviewing team availability.
Original PR description
**What:** - Use precision to show full pill for half-day events in gantt view for month and quarter view task-6361123
The point of sale receipt popup now uses standard notifications to show sending progress, success, or errors instead of messages inside the popup. The dialog also has a clearer title, making it easier for cashiers to understand and act on receipt-sending steps.
Original PR description
Previously, the receipt popup displayed its status (success, error, loading) using inline text elements directly inside the dialog. This commit replaces these inline text notices with standard Odoo toaster notifications. Additionally, a title was added to the dialog to make its purpose immediately clear. Task-6496757
Studio now supports configuring AI behavior directly on existing fields, making AI setup more consistent and easier to manage. The update also cleans up related prompts and dialog text so users get clearer guidance when working with AI fields and Studio promotion messages.
The employee kanban view in Payroll has been adjusted to resolve usability issues. This makes employee information easier to review and helps HR teams work more smoothly from the employee overview.
Original PR description
task-id: 6530918 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Employee kanban cards now show an issues tag when payroll-related problems need attention, helping payroll teams identify cases that require follow-up more quickly. The update also removes outdated employee issue fields and filters, and improves Belgian payroll warning text handling for more accurate messages.
Original PR description
task-id: 6530918
Half-day time off entries now appear as full, easy-to-see blocks in monthly and quarterly Gantt planning views. This makes leave schedules clearer for managers and employees, reducing the chance of overlooking shorter absences.
Original PR description
**What:** - Use precision to show full pill for half-day events in gantt view for month and quarter view task-6361123
This update strengthens checks around spreadsheet shortcut handling in Documents, helping prevent shortcut issues that could disrupt key document workflows. It improves reliability where shortcuts are important for day-to-day functionality.
Original PR description
*Note: only the last commit really belongs to this PR.* Increase integrity of shortcuts where it is critical for functionality. Task-4266789
The web testing tools now match form values exactly by default, reducing false matches in automated checks. Teams can still use flexible pattern matching when partial or case-insensitive matching is needed.
Original PR description
This commit changes the ':value' pseudo-selector to only match the exact value of the matching nodes, instead of only matching a substring. The motivation behind this change is that, contrary to ':contains' wich evaluates the text content of a node (which can itself be affected by other nodes which are not taken account for), ':value' only evaluates the "value" property of the node, which consists of a single string. As such, in most cases, the entire value should already known in advance, and having an exact match would then make sense as a default. In edge cases where this is not wanted, ':value' can still be given a regular expression (surrounded by //), which then accepts any kind of match (partial, exact, case (in)sensitive, etc.). --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Payslip validation errors now show more helpful details, including both the error title and description when available. When several issues are found, they are displayed as a numbered list so payroll users can understand and resolve them more easily.
Original PR description
Validating a payslip for an employee can raise errors (regarding salary configuration for example.) The validation message providing insights to the user used to be a list of the titles of errors. Some of these validation errors may have a description as well, which would be useful for the user. The errors message has been adapted to display title and description if any. If more than one error is shown, the message format is also adapted as a numbered list. task-6526610
Product variant names are now generated more efficiently by avoiding unnecessary checks across all attribute values. This can significantly speed up loading large product catalogs, especially in Point of Sale, while keeping the displayed names unchanged.
Original PR description
Issue --> `product.product's` display_name appends the variant's combination name, which `_get_combination_name` builds by dropping the values that come from single value lines. The check behind that, `_is_from_single_value_line`, only needs to know whether the line holds exactly one active value, but it filtered the line's entire set of values through `_only_active()` to find out. That check runs once per attribute value of every variant being named, so its cost follows the number of values on the template rather than the number of lines. Solution --> Stop at the second active value instead: finding two is enough to know the line is not single valued. The archived path (only_active=False) only measures the line's length and is unchanged, as is the returned name in both cases. Benchmark -> Reading display_name for the 23989 variants on the related database loads in the Point of Sale from approximately 200s to 70s. opw-6530735 Forward-Port-Of: odoo/odoo#288183
Belgian payroll now includes a dedicated work entry type for employees returning on a reduced schedule after more than 12 months of illness. This helps classify the absence correctly as unassimilated for payroll processing.
Original PR description
In Belgian payroll, when an employee returns to work on a reduced working schedule due to illness for more than 12 months, the absence becomes unassimilated. Add the work entry type 'Illness half-time>12m - workers initiative' (LEAVE12305) to support unassimilated partial illness after 12 months. Task-ID: 6484739 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Payroll users can now update Premium Pay options for several existing leave records at once from the Gantt view, instead of opening each leave individually. This speeds up payroll preparation while ensuring options are only applied where the leave type supports them, and leaves with active options are easier to spot via the currency symbol shown on the schedule.
Original PR description
Allow users to apply Premium Pay options across multiple leave records at once in the Gantt view instead of configuring them individually. An "Options" action is added to the Gantt multi-selection button bar when selecting cells with existing leaves. Clicking this action displays applicable m2m options inside a popover view using a form layout with an inline `add_circle` icon indicator. When updated, selected options are executed on the server side and applied strictly to leave types whose underlying work entry types support them. Additionally, leaves with active options now render the company currency symbol directly on their Gantt pills. task-6542141
Belgian payroll now warns when a payslip uses the adapted work schedule leave code after an employee has been continuously sick for more than 12 months or had related long-term illness leave. This helps payroll teams apply the correct leave code and avoid incorrect salary assimilation calculations.
Original PR description
When an employee has been continuously sick for more than 12 months, returning on an adapted working schedule must use LEAVE12305 instead of LEAVE281, as the absence is no longer assimilated. Add a Python payroll warning to flag payslips using LEAVE281 when the employee's continuous illness exceeds 12 months or includes LEAVE280, and update assimilation computations accordingly. Task-ID: 6484739
Payroll structure types are now handled more automatically, reducing the need for users to manage them directly. This makes payroll configuration clearer and helps prevent inconsistent pay schedule settings when creating or updating Belgian payroll records.
Original PR description
- made `type_id` hidden in the payroll structure form view and made it computed so it takes a value when the user creates a new type or change the county of an existing one. - made `schedule_pay` not related to structure type - removed the menu for structure types task-6569952
The AI app now offers a clearer interface for choosing which subagents an AI agent can use, making setup easier and reducing configuration mistakes. The update also prevents unnecessary warning messages when website styling refreshes successfully.
Original PR description
task-[6578342](https://www.odoo.com/odoo/2366/tasks/6578342)
This change improves Odoo's internal test infrastructure by allowing repeated test data to be created once and reused safely across related tests. It should reduce time spent running large test suites, especially for accounting tests, helping developers validate changes faster without changing end-user functionality.
Original PR description
With the abundance of tests we are accumulating, common test classes have become the go to way to make test data readily available for all of your module's tests and/or their dependent modules.…
With the abundance of tests we are accumulating, common test classes
have become the go to way to make test data readily available for all of
your module's tests and/or their dependent modules.
Unfortunately the cost of some of the `setUpClass` implementation has
also been rising as tests require more and more data.
As an example `AccountTestInvoicingCommon` which, with it's override of
`BaseCommon`'s `setUpClass` methods, is called 170 times during the
split from `account` to `auth_signup`.
With each call taking multiple seconds. This is a significant time loss
as (most of the time) the data generated is exactly the same from one
run to another.
(stats collected using `master-profile-setupclass-wbr` branch on runbot)
We realized the following:
- We can run the whole test suite within a singular transaction without
it being much more costly.
- We could provide a way for common classes to define data that could
be shared between test classes that share the same properties.
The changes only concern `odoo.tests.common.TransactionCase` and their
children.
### Transaction Strategy
Previously `TransactionCase`'s transactions would look like this:
(each box representing a transaction, with nested boxes being savepoints)
```
┌─ TestCaseA.setUpClass ─────────────────────────────────┐
│┌─ TestCaseA.setUp (test_foo) ─────────────────────────┐│
││ test_foo() ││
│└──────────────────────────────────────────────────────┘│
│┌─ TestCaseA.setUp (test_bar) ─────────────────────────┐│
││ test_bar() ││
│└──────────────────────────────────────────────────────┘│
└────────────────────────────────────────────────────────┘
┌─ TestCaseB.setUpClass ─────────────────────────────────┐
│┌─ TestCaseB.setUp (test_baz) ─────────────────────────┐│
││ test_baz() ││
│└──────────────────────────────────────────────────────┘│
└────────────────────────────────────────────────────────┘
```
This will also be the case if no `setUpCommonData` is defined on your test class.
However, if `setUpCommonData` IS defined on your class, or a parent class, the
following plan will be executed instead:
Sample python code
```python
from odoo.tests.common import TransactionCase
class ModuleCommon(TransactionCase):
@classmethod
def setUpCommonData(cls):
cls.data = cls.env['module.model'].create({'foo': 'bar'})
class ModuleTestA(ModuleCommon):
def test_bar(self):
self.assertEqual(self.data.foo, 'bar')
class ModuleTestB(ModuleCommon):
def test_foo(self):
self.data.foo = 'foo'
self.assertEqual(self.data.foo, 'foo')
class ModuleCommonExtended(ModuleCommon):
@classmethod
def setUpCommonData(cls):
cls.data_2 = cls.env['module.model'].create({'foo': 'baz'})
class ModuleTestC(ModuleCommonExtended):
def test_baz(self):
self.assertEqual(self.data.foo, 'bar')
self.assertEqual(self.data_2.foo, 'baz')
```
```
┌─ ModuleCommon.setUpCommonData ───────────────────────────┐
│ │
│┌─ ModuleTestA.setUpClass ───────────────────────────────┐│
││┌─ ModuleTestA.setUp (test_bar) ───────────────────────┐││
│││ test_bar() │││
││└──────────────────────────────────────────────────────┘││
│└────────────────────────────────────────────────────────┘│
│ │
│┌─ ModuleTestB.setUpClass ───────────────────────────────┐│
││┌─ ModuleTestB.setUp (test_foo) ───────────────────────┐││
│││ test_foo() │││
││└──────────────────────────────────────────────────────┘││
│└────────────────────────────────────────────────────────┘│
│ │
│┌─ ModuleCommonExtended.setUpCommonData ─────────────────┐│
││┌─ TestCaseC.setUpClass ───────────────────────────────┐││
│││┌─ TestCaseC.setUp (test_baz) ───────────────────────┐│││
││││ test_baz() ││││
│││└────────────────────────────────────────────────────┘│││
││└──────────────────────────────────────────────────────┘││
│└────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────┘
```
### Class selection and dependency strategy
One of our goals for `setUpCommonData` is that it could pretty much be a
drop-in replacement for `setUpClass`.
As indicated by the schema above however, it is not really compatible with our
new system, we want to be able to single out `setUpCommonData` for tests that
rely on that singular common class, which is why calling `super` is NOT allowed
within `setUpCommonData` (or at least calling `super().setUpCommonData`).
We take care of picking the class on which to call `setUpCommonData` and for
you, while the order is defined by the test class's `__mro__`.
Some common classes also provide methods that can be overwritten to change the
data generated by `setUpClass`, for which we provide the decorator
`odoo.tests.common.data_depends`.
`data_depends` takes a list of strings that are used as attributes to generate
a hash for our class; these can be regular attributes or methods (note that we
do not call the method, we simply store the reference to it).
The hash key for a class is defined as follows:
```python
tuple(
tuple(
Type[TransactionCase], # The class that defined `setUpCommonData`
Tuple[Any], # The dependency data
Type[TransactionCase], # The class that will be used to call `setUpCommonData`
),
...
)
```
The dependency data is built by iterating over the `__mro__` of said class and
adding each new definition for each attribute defined by `@data_depends`.
The class to install is the first class in the `__mro__` which has different
dependency data, with the added condition that it must be a subclass of the
previous installed class if there is one.
Consider the following:
```python
from odoo.tests.common import TransactionCase, data_depends
class ModuleCommon(TransactionCase):
_name = 'foo'
@data_depends('_name')
@classmethod
def setUpCommonData(cls):
cls.data = cls.env['module.model'].create({'name': cls._name})
class ModuleTestA(ModuleCommon):
def test_data_name(self):
self.assertEqual(self.data.name, self._name)
class ModuleTestB(ModuleCommon):
_name = 'bar'
def test_data_name(self):
self.assertEqual(self.data.name, self._name)
class ModuleTestC(ModuleCommon):
_name = 'bar'
def test_data_name(self):
self.assertEqual(self.data.name, self._name)
```
Whilst all classes share the exact same `__mro__`, their dependencies differ.
This will of course cause a split as all test can not be ran within the same
transaction/savepoint.
```
┌─ ModuleCommon.setUpCommonData (_name = 'foo') ───────────────────────────────┐
│ │
│┌─ ModuleTestA.setUpClass ───────────────────────────────────────────────────┐│
││┌─ ModuleTestA.setUp (test_data_name) ─────────────────────────────────────┐││
│││ test_data_name() │││
││└──────────────────────────────────────────────────────────────────────────┘││
│└────────────────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────────┘
┌─ ModuleCommon.setUpCommonData (_name = 'bar', install on ModuleTestB) ───────┐
│ │
│┌─ ModuleTestB.setUpClass ───────────────────────────────────────────────────┐│
││┌─ ModuleTestB.setUp (test_data_name) ─────────────────────────────────────┐││
│││ test_data_name() │││
││└──────────────────────────────────────────────────────────────────────────┘││
│└────────────────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────────┘
┌─ ModuleCommon.setUpCommonData (_name = 'bar', install on ModuleTestC) ───────┐
│ │
│┌─ ModuleTestC.setUpClass ───────────────────────────────────────────────────┐│
││┌─ ModuleTestC.setUp (test_data_name) ─────────────────────────────────────┐││
│││ test_data_name() │││
││└──────────────────────────────────────────────────────────────────────────┘││
│└────────────────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────────┘
```
The system takes care of organizing and ordering tests in a way to reuse data as
much as possible (according to the dependencies defined on the tests).
NOTE: While we do re-order tests we still follow `test_sequence` if defined.
#### Edge cases
A)
Consider the following:
```python
from odoo.tests.common import TransactionCase
class ModuleACommon(TransactionCase):
@classmethod
def setUpCommonData(cls):
cls.module_a_data = cls.env['module_a.model'].create({'name': 'foo'})
class ModuleBCommon(TransactionCase):
@classmethod
def setUpCommonData(cls):
cls.env = cls.env(context={'some_context': 'bar'}) # This could be creating+changing company
cls.module_b_data = cls.env['module_b.model'].create({'name': 'bar'})
class ModuleCTestA(ModuleACommon, ModuleBCommon):
def test_data(self):
self.assertEqual(module_a_data.name, 'foo')
class ModuleCTestB(ModuleACommon, ModuleBCommon):
def test_data(self):
self.assertEqual(module_b_data.name, 'bar')
```
`ModuleCTestA` and `ModuleCTestB` actually share the same `__mro__` _AND_
dependencies.
In this case we can also note that `ModuleBCommon` will have an impact on
`ModuleACommon` due to changing the environment (according to `__mro__`).
This is the reason we include the install class in our hash key, the
dependencies would otherwise be the exact same between our test cases, but we
in fact need to know which class the data was installed upon to make sure the
data is available.
```
┌─ ModuleBCommon.setUpCommonData (install on ModuleCTestA) ───────┐
│┌─ ModuleACommon.setUpCommonData (install on ModuleCTestA) ─────┐│
││ ││
││┌─ ModuleCTestA.setUpClass ───────────────────────────────────┐││
│││┌─ ModuleCTestA.setUp (test_data) ──────────────────────────┐│││
││││ test_data() ││││
│││└───────────────────────────────────────────────────────────┘│││
││└─────────────────────────────────────────────────────────────┘││
│└───────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
┌─ ModuleBCommon.setUpCommonData (install on ModuleCTestB) ───────┐
│┌─ ModuleACommon.setUpCommonData (install on ModuleCTestB) ─────┐│
││ ││
││┌─ ModuleCTestB.setUpClass ───────────────────────────────────┐││
│││┌─ ModuleCTestB.setUp (test_data) ──────────────────────────┐│││
││││ test_data() ││││
│││└───────────────────────────────────────────────────────────┘│││
││└─────────────────────────────────────────────────────────────┘││
│└───────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```
B)
Consider the following:
```python
from odoo.tests.common import TransactionCase
class ModuleCommon(TransactionCase):
@classmethod
def setUpCommonData(cls):
cls.data = {
'company': cls.env['res.company'].create({'name': 'test'})
}
class ModuleTestA(ModuleCommon):
test_sequence = 0
def test_foo(self):
self.data['company'] = self.env['res.company'].create({'name': 'test_2'})
class ModuleTestB(ModuleCommon):
test_sequence = 1
def test_bar(self):
self.assertEqual(self.data['company'].name, 'test')
```
The above code will actually result in a cache error; the company referenced
within `cls.data` does not exist anymore.
It is also not possible to add a dependency on `setUpCommonData` as `data` does
not exist outside of running the tests.
It is not recommended to use dictionaries within `setUpCommonData` (or use
frozen ones) as they can not be rollbacked, one should stick to orm records.Meetings held in Discuss can now be automatically recorded on the related document when they were planned from that document, so teams no longer need to manually note that a call happened. The log includes the call duration and links back to the call, with indicators for recordings and transcripts when available.
Original PR description
A meeting held in Discuss left no trace outside of its own channel: whoever just spent an hour on a call had to write that fact down by hand on the lead or the task it was about. A…
A meeting held in Discuss left no trace outside of its own channel: whoever just spent an hour on a call had to write that fact down by hand on the lead or the task it was about. A discuss.call.history can now be tied to a mail.activity, the way a voip.call already is. It happens on its own when the meeting was planned from a document: the call taking place in it is logged on the activity that planned it, so that marking that activity done posts "Meeting done (1h 23m 45s)" in the chatter of that document, linking back to the call and telling whether it was recorded and transcribed. A call nobody planned is logged by hand from the Call History, through the wizard voip contributed and mail now owns. The duration wording shared by both call models moves to mail.tools.call, and ai contributes the transcript icon to that message the way it already does for a voip.call. Join Meeting opens the Discuss channel of the meeting for an attendee who is already a member of it, instead of navigating away to the invitation link. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
The web debug dialog now includes a one-click option to copy the fully computed view structure. This makes it easier for support teams, consultants, and developers to diagnose view issues, share details in tickets, and compare environments without error-prone manual selection.
Original PR description
Description of the issue/feature this PR addresses: The "Computed Arch" debug dialog displays the fully computed view architecture, after inheritance, xpaths and studio customizations have been…
Description of the issue/feature this PR addresses: The "Computed Arch" debug dialog displays the fully computed view architecture, after inheritance, xpaths and studio customizations have been applied, in a read-only pre block. Extracting that text previously required manually selecting it in the browser, which is unreliable for long or deeply indented XML. Being able to copy this text to the clipboard helps with several common scenarios: - Debugging inherited views: when a view looks wrong and it is not clear which of several inheriting modules caused it, the computed arch can be diffed against the base view's arch to see exactly what changed. - Writing new xpath inherit rules: the exact final structure (attribute values, node ordering) is needed to write a correct expr="//field[@name='x']", which is easier to do in a scratch file or editor with search than by eyeballing a read-only dialog. - Bug reports and support tickets: the computed arch can be pasted into a Slack message, GitHub issue or support ticket so others can inspect the view without needing access to the database. - Studio customizations: consultants often need the underlying computed arch to understand why a Studio-added field appears in an unexpected position. - Comparing environments: the arch from a customer's staging instance can be diffed against production to spot config drift, e.g. a view customization made directly on prod. Current behavior before PR: In the "Computed Arch" debug dialog, the only way to get the arch text out of the browser is to manually click-drag to select it inside the pre block, then copy it. This is error-prone for long or deeply indented XML, where whitespace and line breaks are easy to select incorrectly or truncate. Desired behavior after PR is merged: A "Copy Arch" button is added next to "Close" in the dialog's footer. Clicking it copies the full computed arch to the clipboard in one click, with a brief "Copied" confirmation, removing the need to manually select the text. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286894 Forward-Port-Of: odoo/odoo#286619
Meetings held in Discuss can now be automatically recorded in the chatter of the related document when they were planned from that document, including duration, recording, and transcript details. This gives teams a clear history of customer or project conversations without manually writing notes after each call.
Original PR description
A meeting held in Discuss left no trace outside of its own channel: whoever just spent an hour on a call had to write that fact down by hand on the lead or the task it was about. A…
A meeting held in Discuss left no trace outside of its own channel: whoever just spent an hour on a call had to write that fact down by hand on the lead or the task it was about. A discuss.call.history can now be tied to a mail.activity, the way a voip.call already is. It happens on its own when the meeting was planned from a document: the call taking place in it is logged on the activity that planned it, so that marking that activity done posts "Meeting done (1h 23m 45s)" in the chatter of that document, linking back to the call and telling whether it was recorded and transcribed. A call nobody planned is logged by hand from the Call History, through the wizard voip contributed and mail now owns. The duration wording shared by both call models moves to mail.tools.call, and ai contributes the transcript icon to that message the way it already does for a voip.call. Join Meeting opens the Discuss channel of the meeting for an attendee who is already a member of it, instead of navigating away to the invitation link. https://github.com/odoo/odoo/pull/285269
This update improves the online shopping experience by adding clearer country-related options and better handling of currency and pricelist choices. It helps shoppers see the right regional settings while avoiding unexpected price changes in the cart.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Customer lists now show reminder levels and include filters for overdue accounts and reminder status. This helps teams quickly identify which customers need follow-up and prioritize collection activities.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/enterprise#130440 task-6501327 Forward-Port-Of: odoo/odoo#286688
Users can now add a separate personal message when sending survey invitations, so their notes are preserved even if recipients are changed. Invalid email addresses are shown directly below the email field, making invitation errors easier to understand and fix.
Original PR description
Purpose ======== On the survey invitation wizard, when a user tries to put comments on the mail body, it recomputes and erases the user message when the recipient field is altered. Specification ============== - It adds a new field to allow users to send additional messages when inviting users to survey. - It displays an error message below the email field for invalid emails instead of UserError. Task-3753800
Event badges now better support folded A4 printing by keeping key instructions and QR codes visible for easier check-in. Event session planning was also simplified with clearer wording, fewer unnecessary notifications, and improved wishlist access, making the attendee experience smoother.
Original PR description
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
This update adds guided sandbox answer tools for Belgian DRS and Flexi@Work payroll-related declarations. It helps teams test and validate Belgian payroll workflows more reliably before using them in real processes.
Original PR description
…andbox answer wizards Task: 6558927
Users can now see reminder levels directly in partner lists and filter customers by overdue status or reminder level. This helps finance teams prioritize follow-ups and find accounts needing attention more quickly.
Original PR description
- add reminder level column in partner list views - add overdue filter in partner search view - add reminder level in custom filters see odoo/odoo#286688 task-6501327 Forward-Port-Of: odoo/enterprise#130440
Appointment screens and messages have been polished to make booking details clearer for users. Confirmation emails now use a more reliable greeting, default reminders are set one day before appointments, and calendar labels and popovers are cleaner and less confusing.
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 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/288517 Task-6566639
Calendar now includes a reusable default reminder that can support appointment types without extra setup. Attendee fields also show a clearer “Unassigned” label instead of the more technical “Undefined Attendee,” making calendar screens easier to understand.
Original PR description
This PR adds a calendar.alarm. This one is required as default alarm for the appointment types. To avoid creating a new data file and updating the manifest, this change is not done appointment but in calendar as it can already be useful in that app. To avoid a technical label, this PR replaces the default falsy label of CalendarEvent.partner_ids, which is "Undefined Attendee", by "Unassigned". Enterprise PR: https://github.com/odoo/enterprise/pull/131714 Task-6576867
This update improves small on-screen interactions to make the product feel more responsive and polished. These refinements help users better understand actions and feedback while working, without changing core workflows.
Original PR description
commu-PR: https://github.com/odoo/odoo/pull/286205
This update advances the master branch release version to 20.1 alpha. It helps prepare the platform for the next development and testing cycle without changing day-to-day business functionality.
Odoo Studio users can now rearrange pages and groups in form layouts by dragging and dropping them. This makes it easier to customize forms visually and organize business information without relying on technical changes.
The payment popover now validates the information it receives more strictly and includes a new automated test to confirm payment details display correctly. This reduces the risk of display issues in accounting screens and improves confidence in future changes.
Original PR description
Introduces a strict `useProps` schema to the popover and adds a hoot test to verify the popover shows the data correctly. no task-id
Users can now create serial or lot records directly from the Customers smart button on a customer profile. This streamlines field service stock workflows by reducing navigation and making equipment tracking setup faster.
Original PR description
After this commit, users can create serial/lot records directly from the Customers smart button on the partner form view. task-6417443
Serial and lot records can now have their linked customers updated manually, making it easier to reflect real-world service relationships such as separate installer and maintenance customers. Customer links are also kept consistent with company ownership by automatically removing mismatched associations.
Original PR description
Before this commit, the customers linked to a serial/lot were computed and could not be edited. This prevented handling cases where the installer and maintenance customer differ, as no sales order exists to link the serial number to the maintenance customer. After this commit: - `partner_ids` on serial/lot records is editable. - If a partner's company is changed and no longer matches the company of the serial/lot, the link between the partner and the serial/lot is automatically removed (and vice versa). task-6417443
Sales teams can now use Studio to edit the list view for sales order lines without running into an error. This restores the ability to customize columns and add fields on sales orders, reducing disruption for users configuring their sales workflow.
Original PR description
Before this change: When opening Studio on a Sales Order with existing line items, clicking "Edit List View" on the order lines component triggered a JavaScript error. This prevented users from customizing columns or adding custom fields to the sale order line list view. To reproduce: 1. Open the Sales app and create a new Sales Order with at least one product line. 2. Toggle Studio on. 3. Select the Sale Order Lines list component and click "Edit List View". After this change: Clicking "Edit List View" on sale order lines in Studio works as intended without throwing errors, allowing standard view customizations. opw-6545970 Forward-Port-Of: odoo/odoo#288171
Cancelling an Adyen card payment from the Point of Sale now sends the cancellation to the correct payment request. This prevents payment terminals from continuing to wait for a customer payment after the cashier has cancelled it in Odoo.
Original PR description
Steps to reproduce: - Configure a POS with an Adyen payment terminal - Open the POS, add a product and go to the payment screen - Select the Adyen payment method and click Send - Wait at least 5 seconds - Cancel the payment from the POS - => The terminal keep waiting for the payment (it's not canceled) `_adyenCancel` read `most_recent_service_id` but this value is overwritten by the `_adyenCheckPaymentStatus` polling after 5s, so we try to cancel the wrong payment. We now read the ServiceID from the payment line instead of using `most_recent_service_id`. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288874
Vendor bill lists in Accounting now load quickly again after a recent change caused severe slowdowns. The database lookup used to detect duplicate bills was updated so it also covers vendor receipts, restoring the expected performance for users viewing bills.
Original PR description
commit https://github.com/odoo-dev/odoo/commit/391278237dc4fdbf48039bb269f1377d83bbc54d introduced a performance regression.
Go to Accounting > Vendors > Bills
On odoo.com:
| | Time | Query plan |
|--------|--------|--------|
| Before | ~600ms | https://explain.dalibo.com/plan/3ge1afa2aa632be9 |
| Now | 44s | https://explain.dalibo.com/plan/99e847h8d1c38dfd |
After this commit, we're back with the same query plan :)
The reason is the condition `.move_type in ('in_invoice', 'in_refund')`
which was changed to include 'in_receipt':
`.move_type in ('in_invoice', 'in_refund', 'in_receipt')`
Because of that, the query can no longer use the partial index
`_duplicate_bills_idx` because it doesn't include 'in_receipt'.
This commit adapts the index to include moves with type 'in_receipt'.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#288481Fixes French PDP partner lookup so it works when a company endpoint uses a personalized SIREN or SIRET with an added suffix. This helps affected businesses find and validate partners reliably instead of being blocked by overly strict identifier length checks.
Original PR description
In PDP the lookup didn't work if the endpoint is of any length other than 9 or 14 characters, however personalised SIREN/SIRET can have a higher length, this pr fixes that by allowing a SIREN/SIRET suffix in the lookup following the format 'SIRET_SUFFIX' no task id Forward-Port-Of: odoo/odoo#288509
Changing a light user's notification preference to receive messages in Odoo no longer upgrades them to a regular user. This preserves intended access limits while still allowing users to choose their preferred notification method.
Original PR description
Changing the notification preference silently promoted a light user to a regular user, granting them access rights they were never meant to have. Steps to reproduce: - log in as a light user…
Changing the notification preference silently promoted a light user to a regular user, granting them access rights they were never meant to have. Steps to reproduce: - log in as a light user (light/light) - open My Profile - in Preferences, switch "Notification" from "Email" to "Odoo" - log back in as an administrator and open that user's form - the Role field now reads "User" instead of "Light" The notification preference is not a plain field: choosing "Odoo" puts the user in a dedicated technical group. That group grants no access right of its own, but a user is considered light only as long as every group they hold is explicitly known to be a light one. Since this group was never declared as such, it was treated like any ordinary access group, which was enough to have the user reclassified as regular. This commit declares the group as a light one, so the preference stays a preference and no longer affects the user's role. A regression test covers both directions of the toggle for a light and a regular user, checking the role, the underlying group and the role search filter. Note that existing databases need a `mail` upgrade to clear the stale link. Task-6575413 Forward-Port-Of: odoo/odoo#288646
This fixes an error that could appear when users reopened an attendance record from the calendar view. The attendance popover now loads the information it needs to show warnings properly, preventing a disruptive traceback in My Attendances.
Original PR description
Steps to reproduce:- 1. Create an attendance record on calendar view and save it 2. Now try to open that record, and you get traceback `Error: Name 'source_stale' is not defined` Cause:- The "My Attendances" calendar popover shows a stale-source warning banner using invisible="not source_stale" and invisible="source_attendance_id". Both fields were declared at the <calendar> level only, not inside <popover>, so the popover's Card renderer never fetched them, raising error. Fix:- Declare the two fields as direct children of <popover> so their values are included in the popover's record data. task-6577810 Forward-Port-Of: odoo/odoo#288524
This fixes an issue where status steps could incorrectly collapse into a single “More” dropdown when users viewed records at certain browser zoom or display scaling settings. Users on high-resolution screens should now see the full statusbar when there is enough space, making record stages easier to read and navigate.
Original PR description
`areItemsWrapping` decides whether the statusbar buttons fit on one line by comparing `getBoundingClientRect()` heights. Those rects are rounded to physical pixels, so depending on the zoom/DPI ratio…
`areItemsWrapping` decides whether the statusbar buttons fit on one line by comparing `getBoundingClientRect()` heights. Those rects are rounded to physical pixels, so depending on the zoom/DPI ratio a single-line height can come out a fraction of a pixel taller than the reference button height, even though nothing actually wraps. That false positive made the whole statusbar collapse into a single dropdown at some (but not all) browser zoom levels, most noticeable on high-DPI screens where OS scaling and browser zoom combine into non-integer ratios. Steps to reproduce: 1. On a high-DPI screen (e.g. 4K) with OS display scaling enabled. 2. Open any record with a statusbar field (e.g. a CRM opportunity). 3. Set the browser zoom to a value close to 100%. 4. The statusbar collapses into a single "More" dropdown instead of showing every stage inline, even though there is enough room. Round both heights before comparing to ignore that sub-pixel noise while still detecting genuine wrapping. Regression introduced by 7e77b99d2845. task-6545990 Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288194
The Discuss thread header now avoids showing a selection highlight when no action is currently active. This prevents items such as Attachments from appearing selected by mistake, reducing confusion for users.
Original PR description
The `o_switcher` style, shared with the control panel view switcher, renders its sliding background unconditionally and places it from `--SwitchButtons-activeIndex`, which is only set when one of its buttons is active. An action group can have no active action at all: the variable stays unset, `left` falls back to `auto` and the background sits on the first action, making it look selected. This was visible on `Attachments` in the Discuss thread header. Keep the generic switcher style as is and hide that background in ActionList when none of its actions is active. <img width="944" height="534" alt="switcher-before-after" src="https://github.com/user-attachments/assets/914cc215-4ef1-4974-a3e2-432f6be9356c" /> Forward-Port-Of: odoo/odoo#288060
The messaging menu search now uses the same search field design as other Discuss areas. This gives users a clearer focus indicator and more consistent visual feedback when searching messages.
Original PR description
The messaging menu search rolled its own markup, with the border on a wrapper and none on the input, so it had no focus contour or background unlike every other Discuss search. Render SearchInput instead. Its term stays owned by MessagingMenuUIState, whose signal is handed to useSearch(), so there is a single term and nothing to keep in sync. <img width="700" height="548" alt="discuss-search-focus-before-after" src="https://github.com/user-attachments/assets/9150d1ef-dd13-4e66-8cc4-c6dabc140f08" /> Forward-Port-Of: odoo/odoo#288391
This fix prevents sale orders from failing when a user saves immediately after reordering order lines. Odoo now pauses saving while the line reorder action finishes, making the Sales workflow more reliable for quotations and orders with multiple lines.
Original PR description
When we save a sale order just after reordering its sale order lines, there's a chance that an error is thrown Steps to reproduce: 1. Install Sales 2. Go to Sales and open any quotation that has…
When we save a sale order just after reordering its sale order lines, there's a chance that an error is thrown Steps to reproduce: 1. Install Sales 2. Go to Sales and open any quotation that has multiple sale order lines 3. Reorder a sale order line and immediately save (there's more chance to reproduce if you have a lot of sol and if you use the shortcut `ALT + S`) 4. An error is thrown Issue: It is possible that the save takes place during the execution of super.sortDrop. This reassigns the id of all the records in this.props.list.records Therefore, calling `_handleQuantityAdjustment` with the recordMap computed before the save uses an id that has disappeard from this.props.list.records so we cannot find it at https://github.com/odoo/odoo/blob/4973903252865a6a7a2da235bc3a01675dbbff4d/addons/sale_management/static/src/fields/sale_order_line_field/sale_order_line_field.js#L263 which eventually throws an error Solution: Suspend any save mechanism when sortDrop starts and resume it when sortDrop has finished opw-6483206 Forward-Port-Of: odoo/odoo#288370 Forward-Port-Of: odoo/odoo#286787
This fixes an access error that could stop warehouse users from creating backorders during multi-step deliveries when the original sales order belonged to another user. It restores the expected workflow so inventory teams can validate transfers and create backorders without unnecessary sales order permissions.
Original PR description
Multi-step deliveries backorders may create a new picking when processed by a user who only has access to their own sales orders. But, `_key_assign_picking` reads from the related sales order that they don’t have access to. Previously, these moves were confirmed with superuser rights, so this read did not trigger the sales order record rule. Since 19.2, the `sudo()` was removed. The fix would be to add this back in so the access rights error would not be raised. Steps to reproduce on Runbot: 1. Log in as User A (Admin) Turn on Multi-Step Routes Set the warehouse to use 2-step delivery (Pick then Deliver) 2. Create a Sales Order for a product with demand more than stock available and confirm it. 3. Log in as User B with (Demo): Sales: User: Own Documents Only Inventory: User 4. Open the picking transfer generated from User A’s Sales Order. 5. Click Validate and choose Create Backorder. Related: opw-6509032 Forward-Port-Of: odoo/odoo#285778
The website builder now stops a warning timer once style updates finish loading successfully. This prevents misleading warning messages during normal AI website editing and helps keep diagnostics focused on real issues.
Original PR description
Commit [1] made the promise of reloading css bundles race with a timeout promise that logs a warning if CSS reload is not confirmed. However, even if the bundles reloaded on time, the warning was still logged as the timeout was never canceled. [1]: 769ebc02784d2bdebbfb3189e87426994fd87c8f Forward-Port-Of: odoo/enterprise#131722
The marketing automation menu is now hidden when a user is viewing a record that has not been saved yet. This prevents users from trying to add incomplete records to campaigns, reducing confusion and avoiding invalid actions.
Original PR description
This commit fixes an issue with the marketing_automation's new cogMenu. If the form view we are opening is not yet created we have a resId set to False. This is not a normal behavior to try and add a not fully created record to a campaign. Thus we hide the dropdown if there's no resId on the currently opened record. task-6559148 Forward-Port-Of: odoo/enterprise#131828
This fixes where users are sent after archiving or deleting an AI agent. The redirect now uses the correct AI Agent app reference, helping users return to the right place without navigation errors.
Original PR description
Use the ai_agentic namespace when redirecting after archiving or deleting an agent. task-id-6497307 Forward-Port-Of: odoo/enterprise#131833
Field service auto-planning no longer fails when an employee has multiple neighboring shifts at the same time. The system now checks travel time against the furthest relevant previous or next shift, helping schedules generate reliably in more complex planning situations.
Original PR description
This commit fixes a traceback occuring when travel times were checked with the neighboring shifts in the auto-plan. It may be possible that a resource has multiple shifts at the same time, in which case we should use the travel time with the further previous/next shift. task-6579763 Forward-Port-Of: odoo/enterprise#131845
Fixes a crash when exporting the Peruvian "Inventory and Balance" General Ledger report. The report now generates successfully while preserving the required SUNAT file format, preventing disruption for users preparing compliance reports.
Original PR description
### Description of the issue/feature this PR addresses: This PR fixes a server crash in the Peruvian localization (l10n_pe_reports_lib) that occurs when generating the "Inventory and Balance" General…
### Description of the issue/feature this PR addresses: This PR fixes a server crash in the Peruvian localization (l10n_pe_reports_lib) that occurs when generating the "Inventory and Balance" General Ledger report. The crash is triggered by strict validation rules within Python's csv module, which rejects the custom line terminator used to fulfill the SUNAT PLE formatting requirements. ### Current behavior before PR: When a user attempts to generate and export the "Inventory and Balance" report, the server crashes with a ValueError: bad delimiter or lineterminator value. This happens because the csv.DictWriter is initialized with lineterminator='|\n' to ensure every row ends with a pipe. Python's underlying csv implementation rejects this, as it expects standard line endings (\r, \n, or \r\n) and throws an error if the delimiter character (|) is included in the terminator string. ### Desired behavior after PR is merged: The "Inventory and Balance" report generates successfully without server errors. The code now uses the standard lineterminator='\n' to satisfy Python's validation rules. To maintain the mandatory trailing pipe (|) at the end of each row required by SUNAT, a dummy empty column (['']) is appended to the field names with restval=''. This prompts the writer to naturally append the final pipe as a column delimiter before the newline, resulting in the exact |\n output format required, safely and reliably. opw-6509674 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#131302 Forward-Port-Of: odoo/enterprise#129636
This fixes naming and selection issues in marketing automation views introduced by a recent revamp. Users should no longer see crashes or be sent to the wrong screen when opening server actions or trigger forms in campaign flows.
Original PR description
This commit fixes issues that were introduced with the new marketing automation revamp. Some views were either wrongly renamed or no longer used to display server actions or trigger form views in the flow view. Which is an issue as this would either create a crash or open the wrong view entirely. Now the views are correctly renamed and used. task-6559148
Fixed an issue where one-time purchases of recurring products were treated like ongoing subscriptions in stock forecasts. This prevents misleading future demand and helps replenishment planning reflect actual sales commitments.
Original PR description
When a recurring product (with `allow_one_time_sale = True`) is sold as a one-time purchase (no subscription plan), the stock forecast report and replenishment logic incorrectly treat it as an active…
When a recurring product (with `allow_one_time_sale = True`) is sold as a one-time purchase (no subscription plan), the stock forecast report and replenishment logic incorrectly treat it as an active subscription. This results in infinite projected future outgoing moves for standard sales. This occurs because the logic only checks if `recurring_invoice` is True on the product, ignoring whether the parent order actually has a `plan_id`. This commit fixes the issue by: 1. Updating `_get_stock_subscription_lines` in `sale.order.line` to filter out lines using `_subscription_is_one_time_sale()`. 2. Updating the domains in `stock.forecasted_product_product` to require `order_id.plan_id != False` for subscription forecasts, while correctly routing one-time sales (`order_id.plan_id == False`) back to the standard sale domain. 3. Adapting existing tests to verify that one-time sales do not generate future subscription stock forecasts. Task-6193648 Forward-Port-Of: odoo/enterprise#128018 Forward-Port-Of: odoo/enterprise#116889
Vendor bills now keep the manually selected recipient bank account when using Auto-Complete, as long as the bill currency has not actually changed. This prevents accidental payment detail changes for vendors with multiple bank accounts.
Original PR description
### Issue: When a bank account is manually set on a bill for a partner with multiple bank accounts, using the Auto-Complete feature could reset the manual selection even without a currency change ###…
### Issue: When a bank account is manually set on a bill for a partner with multiple bank accounts, using the Auto-Complete feature could reset the manual selection even without a currency change ### Cause: When `invoice_vendor_bill_id` is set, an onchange assigns `currency_id` unconditionally, even when it is the same value This triggers `_compute_partner_bank_id`, which always recomputes the best matching bank account from scratch without considering the currently set value If the currency did not change, this recompute is unnecessary and silently overrides the manual selection ### Steps to reproduce: - Install `account` - Create a Vendor with 2 bank accounts (keep default values to have equal priority on all accounts) - Create and post a Bill for this vendor with at least one line - Create a new Bill for the same vendor - Set the Recipient Bank to the second account in the list - In Auto-Complete, select the first Bill Before the fix, the Recipient Bank is reset to the first account opw-6210414 Forward-Port-Of: odoo/odoo#288386 Forward-Port-Of: odoo/odoo#283479
Fixes a crash in Odoo Studio when users open the report settings from the cog icon in debug mode. This makes it possible to access the related report configuration form reliably, reducing disruption for users editing reports.
Original PR description
On the report editor in debug mode, click on the cog to open the ir.action.report form view Before this commit, there was a crash After this commit, there is no crash task-6531172
This update improves how text from Odoo templates is prepared for translation by excluding punctuation, icons, and standalone symbols that should not be translated. This helps translators focus on meaningful text, improving translation quality and consistency across several apps without changing core business workflows.
Original PR description
## [FIX] *: better translations in templates This commit aims to provide better translatable strings from static XML templates. To do so, it includes the following changes: - isolated strings that are not meaningful to a translation, such as punctuation or special characters (e.g. "?" for field tooltip marker, "-" to separate tips, or a "x" button to close a panel); - add `t-translation="off"` to strings that are not supposed to end up in translatable strings, such as the aforementioned characters; - a few simplifications for the affected templates have also been applied to the changed nodes when possible, to enforce semantics or to improve readability. - Enterprise: https://github.com/odoo/enterprise/pull/100330 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update improves how text in interface templates is prepared for translation by excluding punctuation, symbols, and other non-meaningful characters. It helps translators focus on useful text, improving translated screens without changing business workflows.
Original PR description
## [FIX] *: better translations in templates This commit aims to provide better translatable strings from static XML templates. To do so, it includes the following changes: - isolated strings that are not meaningful to a translation, such as punctuation or special characters (e.g. "?" for field tooltip marker, "-" to separate tips, or a "x" button to close a panel); - add `t-translation="off"` to strings that are not supposed to end up in translatable strings, such as the aforementioned characters; - a few simplifications for the affected templates have also been applied to the changed nodes when possible, to enforce semantics or to improve readability. - Community: https://github.com/odoo/odoo/pull/236100
The wording for a French e-reporting configuration option was restored because the previous change made its meaning too narrow. This helps users understand that the setting also covers choosing not to send data to the public invoicing portal, reducing confusion during setup.
Original PR description
When we removed the pilot phase setting from the view, we changed that setting to only mean Enable e-reporting. But that's a mistake. In fact people are also choosing not to send to the PPF, so the previous sentence was still right. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288518
This fix ensures that when a past point-of-sale order is later invoiced, the cancellation sent to the Spanish tax authority targets the original simplified receipt instead of the newly created full invoice. This prevents incorrect electronic tax records and helps businesses keep Verifactu POS reporting accurate.
Original PR description
When creating an invoice for a previous POS order, the data sent to AEAT cancels the full invoice instead of the order's simplified invoice. Steps to reproduce: - Open a POS and make a sale without invoicing it; - In the POS, go to the Order tab; - Select the order and fully invoice it. Issue: The AEAT cancellation line added to the pos order actually cancels the full invoice just created [opw-6471927](https://www.odoo.com/odoo/project/49/tasks/6471927) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#284277
Fixed a mobile Point of Sale issue where scrolling the product list could accidentally open the product information popup. This prevents interruptions during touchscreen use and makes browsing products smoother for cashiers.
Original PR description
Steps to reproduce: - Open the PoS in mobile mode (touch device) - Swipe the product list up and down a few times Issue: The product info popup sometimes opens while scrolling, as if a product had been long pressed. Cause: Since the product card long press listens to pointerdown/pointerup, a touch that turns into a scroll ends with a pointercancel and never a pointerup, so the long press timer survives the gesture. The debounced onScroll handler is the only other canceller, but when the list is still scrolling from a previous swipe the debounce is already armed, its leading call is skipped and the continuous scroll events keep delaying the trailing call past the long press duration. Fix: Cancel the long press on pointercancel, like pointerup. opw-6514079 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288135 Forward-Port-Of: odoo/odoo#287835
Users can now find and select both service and goods taxes when searching for taxes on product sales or purchase tax fields. This prevents valid tax choices from being hidden and gives businesses more flexibility when configuring product taxes.
Original PR description
With this commit:- - We remove the tax-scope filter from the Search more taxes in the product page's tax fields (Sales taxes and Purchase taxes). - The purpose of doing so is that we should not restrict the user from using service taxes in goods and vice versa. task-6527424 Forward-Port-Of: odoo/odoo#288187
The Mail app now only shows followers who are actually subscribed to receive messages in the recipient list. This prevents users from seeing people listed as recipients when they will not receive the message, reducing confusion in chatter communications.
Original PR description
Before this commit, a follower not subscribed to "Messages" message subtype would still show up in the recipients list (the `To: ` line). This happens since [1] introduced the followers badge on the recipient list, without taking into account the subscription of the followers. This commit fixes the issue by using the `recipientsCount` field, which holds the count of followers subscribed to the "Messages" subtype (except self). [1] https://github.com/odoo/odoo/pull/255498 task-6545679 Forward-Port-Of: odoo/odoo#287214
Colombian Point of Sale orders no longer fail when a company has several DIAN obligation types configured. Receipts can now be generated and printed correctly by listing all applicable obligation descriptions instead of assuming only one exists.
Original PR description
Steps to reproduce: - Colombian company with DIAN electronic invoicing enabled in PoS - On the company contact, set more than one obligation type (e.g. O-13, O-15 and O-23) - Open a PoS session, sell…
Steps to reproduce: - Colombian company with DIAN electronic invoicing enabled in PoS - On the company contact, set more than one obligation type (e.g. O-13, O-15 and O-23) - Open a PoS session, sell a product and pay Issue: The order fails to sync with "Expected singleton: l10n_co_edi.type_code(x, y, z)" as soon as the DIAN document is accepted, and the receipt cannot be printed. On 18.0 to saas-19.1 the same crash happens when the receipt data is generated for printing. Cause: `_compute_l10n_co_edi_pos_receipt_data` fills `obligation_type_description` by reading `description` directly on `l10n_co_edi_obligation_type_ids`, which is a many2many. The read only works when the company carries exactly one obligation type, while a Colombian company commonly has several (they are all sent to the DIAN in `TaxLevelCode`). Fix: Join the descriptions of all obligation types, the same way the DIAN invoice PDF report already does. opw-6576523 Forward-Port-Of: odoo/enterprise#131747
Long bill reference text in outstanding credit or debit sections is now shortened visually so it stays within the page layout. This keeps Credit Notes and Vendor Bills easier to read and prevents confusing display issues when references are unusually long.
Original PR description
The "Outstanding credits" or "Outstanding debits" sections of a Credit Note or Vendor Bill will overflow when the "Bill Reference" is too long. We resolve this by applying the text-truncate class.…
The "Outstanding credits" or "Outstanding debits" sections of a Credit Note or Vendor Bill will overflow when the "Bill Reference" is too long. We resolve this by applying the text-truncate class. Steps to Reproduce: 1. Create a new 19.0 db and load demo data. 2. Accounting -> Vendors -> Refunds -> RBILL/2026/09/0001 3. Click the entry in the Outstanding credits section. 4. Enter a long "Bill Reference" value, e.g. asdf asdfasfasdfasdfasdfasdfasdfasdfasdfasdfasdf. 5. Go back to the Credit Note and observe the text overflow. opw-6558982 <img width="1254" height="1162" alt="bill_reference_long" src="https://github.com/user-attachments/assets/8c762b50-65a8-44bf-9a28-bd34887d5928" /> <img width="1620" height="1294" alt="overflow" src="https://github.com/user-attachments/assets/f5c47404-fb7f-4715-8815-cabe5dda3805" /> <img width="1586" height="1159" alt="truncated" src="https://github.com/user-attachments/assets/2a80f656-0a12-4805-9c1d-fd20379655ce" /> Forward-Port-Of: odoo/odoo#288389
GCC Arabic-English invoice PDFs no longer fail when an invoice includes section or note lines. This prevents internal server errors during invoice preview or printing, making invoice generation more reliable for affected businesses.
Original PR description
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not…
## Problem When rendering PDF invoices that contain **section** or **note** lines, the `l10n_gcc_invoice.arabic_english_invoice` QWeb template raises: ``` TypeError: argument of type 'bool' is not iterable ``` This happens because `account.move.line` records of type `line_section` or `line_note` have `name = False`. The template evaluates `arabic_name not in line.name` (and the same for `english_name`), which fails because Python cannot apply the `in` operator on a boolean value. ## Fix Add a `line.name and` guard before each `not in` check: ```xml <!-- Before --> <span t-if="arabic_name not in line.name" .../> <span t-if="(english_name != arabic_name) and (english_name not in line.name)" .../> <!-- After --> <span t-if="line.name and arabic_name not in line.name" .../> <span t-if="line.name and (english_name != arabic_name) and (english_name not in line.name)" .../> ``` ## Steps to reproduce 1. Install `l10n_gcc_invoice` on an Odoo 16.0 instance. 2. Create a customer invoice and add a **Section** line. 3. Print/preview the invoice PDF. 4. Observe `Internal Server Error` / `TypeError: argument of type 'bool' is not iterable`. Forward-Port-Of: odoo/odoo#278887 Forward-Port-Of: odoo/odoo#267147
This fix prevents Argentina electronic invoices from sending zero-value VAT details when advance payments fully offset the invoice. It helps $0 final invoices pass ARCA validation instead of being rejected, supporting the standard 100% down payment workflow.
Original PR description
## Description of the issue When an Argentinian invoice contains advance-payment deduction lines that exactly cancel the product lines (net taxable base = 0 for a given VAT aliquot), the invoice is…
## Description of the issue
When an Argentinian invoice contains advance-payment deduction lines that exactly cancel the product lines (net taxable base = 0 for a given VAT aliquot), the invoice is rejected by the ARCA (AFIP) WSFE web service with:
> **Error 10018**: "Si ImpIva es igual a 0 el objeto Iva y AlicIva son obligatorios. Id iva = 3 (iva 0)"
This is the standard "100% down payment" flow: the customer is invoiced an advance for the full amount, and the final invoice deducts that advance, resulting in a $0 invoice that must still be validated against ARCA.
This is a forward-port to 19.0 of #270846 (same fix, targeted at 18.0, closed unmerged). The bug is still present in 19.0: `_get_vat()` in `addons/l10n_ar/models/account_move.py` evaluates its filter on the raw unrounded aggregated floats.
## Steps to reproduce
1. On a company with the Argentinian localization (`l10n_ar_edi`) configured for electronic invoicing (WSFE), create a sale order with one or more product lines taxed at IVA 21% (e.g. total $121,000).
2. Create a **down payment invoice for 100%** of the order and validate it against ARCA (this one succeeds).
3. Create the final invoice from the sale order: it contains the product lines (positive) and the down-payment deduction line (negative), both at IVA 21%. Total to pay: **$0.00**.
4. Confirm the invoice and send it to ARCA.
5. **Current behavior (bug):** ARCA rejects the request with error 10018. Inspecting the generated WSFE request shows `ImpNeto=0.0`, `ImpIVA=0.0`, `ImpTotal=0.0` and an `Iva` block containing an all-zero aliquot, e.g. `{'AlicIva': [{'Id': '5', 'BaseImp': 0.0, 'Importe': 0.0}]}` — instead of `Iva: null`.
6. **Expected behavior (after fix):** no zero-amount aliquot is sent (`Iva` is `null`) and ARCA approves the $0 invoice.
## Root cause
In `_get_vat()`, the positive product lines and the negative down-payment deduction line share the same VAT aliquot, so the aggregation by `vat_afip_code` nets the group to zero. However, floating-point accumulation in the aggregated tax details leaves a tiny residual (~1e-12) in `base_amount_currency` / `tax_amount_currency`. The filter condition checks the **raw unrounded** values:
```python
if grouping_key['vat_afip_code'] not in (False, '0', '1', '2') and (values['base_amount_currency'] or values['tax_amount_currency']):
```
The ~1e-12 residual is truthy in Python, so the aliquot entry is kept — even though both `BaseImp` and `Importe` are rounded to `0.00` two lines below when building the entry. The WSFE request therefore carries a non-null `Iva` block with all-zero amounts, which ARCA rejects with error 10018.
## Fix
Round `BaseImp` and `Importe` to 2 decimals **before** evaluating the filter condition, so an aliquot whose amounts cancel out is excluded from the `Iva` array (the same rounded values are then reused when building the entry, keeping the sent amounts unchanged for every other case):
```python
base_imp = float_round(amount_sign * values['base_amount_currency'], precision_digits=2)
importe = float_round(amount_sign * values['tax_amount_currency'], precision_digits=2)
if grouping_key['vat_afip_code'] not in (False, '0', '1', '2') and (base_imp or importe):
```
Behavior is unchanged for every invoice whose aliquots round to a non-zero base or tax amount.
---
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Forward-Port-Of: odoo/odoo#286005Vehicle imports now better understand the expected brand/model format when creating vehicle model records. This helps manually created fleet vehicles import more reliably and reduces avoidable errors during data migration or updates.
Original PR description
The name has a specific format `<brand>/<model>`, hence the fix overwrites the `name_create` function called when the import tries to create new model records, so that the name passed to that function can be correctly interpreted. Errors will occur for vehicles from the demo data (because of their external ID) but should work for any vehicle created manually. task-6578351
Fixes an issue where editing certain Kanban cards in Odoo Studio, such as Project tasks, could fail when adding or removing fields. This lets users customize those views reliably without save errors, while leaving other views unchanged.
Original PR description
Issue: Editing the Project task Kanban with Studio fails with an "element cannot be located in parent view" error. This affects Kanban views whose card architecture is provided through `card_id`.…
Issue: Editing the Project task Kanban with Studio fails with an "element cannot be located in parent view" error. This affects Kanban views whose card architecture is provided through `card_id`. Adding or removing a field produces an XPath targeting the inlined `<card>`, but that node cannot be found when the Studio customization is saved. Steps to reproduce: * Open Project > Tasks > My Tasks in Kanban view. * Open Studio. * Add or remove a field from the card. Cause: The client receives a postprocessed Kanban architecture in which the view referenced by `card_id` has already been appended as a `<card>` node: https://github.com/odoo/odoo/blob/1aa1f1967c7b9c8fd2c941fbc0c7b4c563698e46/odoo/addons/base/models/ir_ui_view.py#L3138-L3140 Studio therefore generates paths containing `/kanban/card`. However, Studio normalization applies those paths to the pre-postprocessed architecture, which still contains only the `card_id` attribute: https://github.com/odoo/enterprise/blob/42aa8fafdef159476d708f91467cba6f7fb2c6d3/web_studio/models/ir_ui_view.py#L667-L671 As `<card>` does not exist in that source tree, the inheritance engine cannot locate the target. Solution: We need to inline the referenced card before applying or normalizing Studio customization specifications. This makes Studio operate on the same architecture shape that was presented to the client. The `card_id` attribute is then removed from that temporary source to prevent regular post processing from appending the card a second time. The behavior is limited to Studio customization views and is a no op for Kanban views without `card_id`, preserving the existing inheritance flow for other views. opw-6445398 Forward-Port-Of: odoo/enterprise#127393
This change restores the previous way Mexican electronic payment complements calculate amounts, following updated guidance from the certification provider after consultation with the government. It helps reduce compliance and validation issues by using the maximum allowed decimal precision for payment amounts.
Original PR description
Quadrum reverted their changes because > Derived from a consultation with the government we reverted to our previous behavior, we recommend using the maximum number of decimals allowed Reverts commit https://github.com/odoo-dev/enterprise/commit/f13d204dacf9eae98c78de26e9f2e54387a0eaee as well opw-6561617 Forward-Port-Of: odoo/enterprise#131640 Forward-Port-Of: odoo/enterprise#131581
Payroll version pages now display the full details for employee-related warnings instead of omitting them. This helps payroll teams understand and resolve employee issues without missing important context.
Original PR description
`_compute_issues()` looked up `warning_details` using `version` keys for warnings keyed by `hr.employee`, dropping the issue details. Use `version.employee_id` as the lookup key when `warning.model_name` is `'hr.employee'`. Task: 6483048
Fixes a VoIP call flow editor issue that prevented users from moving an existing connection to a new destination. This helps teams update call routing flows without losing connectors or being blocked by connection limits.
Original PR description
An output port that already has a connection couldn't be rewired to a new target: grabbing it looked for another output port instead of an input port, so the old connector was removed and never replaced. Dropping a new connection onto an output port that was already at its connection limit was rejected outright instead of replacing the existing connection.
This update adjusts HR forms so structure type information is not hidden where it is still needed. It helps preserve clarity for users managing employees and contract templates while avoiding an unintended form change.
Original PR description
- hide `structure_type_id` on employee and contract template form views task-6569952 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This fix makes Belgian payroll time off allocations more consistent and accurate, especially when employees have multiple contract changes or working schedule updates. It also improves the schedule change process and adds clearer explanations in the interface so payroll teams can better understand allocation values.
Original PR description
Time off allocation fixes:
- Fixed rounding of time off allocations: some parts of the code rounded to full days, while others rounded to half-days.
- Fixed the calculation of time off from holiday attestations, which was done differently in different parts of the code.
- Fixed allocation calculations for employees with multiple contract versions, ensuring that each version is only taken into account for its actual effective period.
- Fixed the Working Schedule Change wizard:
-- allocations were not found when the work entry types had the same code but different records (generic vs. Belgian);
-- the calculated allocation was not correctly displayed in the wizard;
-- fixed the allocation update for working schedule changes starting in the future.
- Added a tooltip to explain the time off allocation values in the UI.
task: 6452166This update fixes cases where some screens could behave as if the user was not on a small device, which could affect mobile layouts in areas like accounting, imports, mail, and navigation. It also adds safeguards so outdated internal references fail visibly during testing instead of causing quiet display issues later.
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
EU OSS sales are now reported correctly in French PDP Flow 10 by excluding destination-country VAT from French VAT rate checks. This keeps the invoice and accounting VAT intact while reporting the taxable amount in the appropriate non-French VAT category, reducing reporting rejections.
Original PR description
OSS sales are taxed in the customer's Member State but are not subject to French VAT. They must therefore be reported under TNT1, while the Flow 10 Schematron only accepts French VAT rates. Identify taxes generated for the EU OSS scheme through their OSS tag. Keep the destination VAT on the invoice and in accounting, but report the taxable base under TNT1 with a zero tax rate and amount. Continue rejecting unsupported rates for regular taxes and invalid OSS rates. no task id Forward-Port-Of: odoo/odoo#288013
This update removes redundant code in the web test mock server that had no effect on behavior. It helps keep the codebase cleaner and easier to maintain without changing any user-facing functionality.
Original PR description
`record[fieldName] = record[fieldName]` does nothing. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Spreadsheet actions now preserve approved rich formatting in help messages instead of showing it as escaped plain text. This ensures users see action guidance as intended while keeping existing safeguards for missing action details.
Original PR description
Current behavior before PR: - `navigateTo` cleans up the action description using `JSON.parse(JSON.stringify(...))`. - This removes Owl's `markup()` wrapper from the `help` field. - As a result, the trusted HTML is converted to a plain string and gets escaped instead of being rendered. Desired behavior after PR is merged: - Pass the action description directly to doAction. - `_preprocessAction` already provides defensive fallbacks for individual fields, such as `action.domain || [] and action.display_name || action.name || "".` - Therefore, undefined fields or missing keys are handled safely without the need for the JSON round-trip. - This preserves the trusted markup in the help field and renders the HTML correctly. Task: [6428217](https://www.odoo.com/odoo/project/2328/tasks/6428217) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#286191
This fix prevents an error from appearing when a user clicks a shared Sign document link while still editing a website page. It improves the website editing experience by safely handling pages that do not have the usual editable record information.
Original PR description
# How to reproduce - Go to Sign > Templates - Upload a PDF & click on Share - Copy the link - Go to a page with a EditInBackend systray item (e.g. a Product or Event page) - In the editor, add the…
# How to reproduce - Go to Sign > Templates - Upload a PDF & click on Share - Copy the link - Go to a page with a EditInBackend systray item (e.g. a Product or Event page) - In the editor, add the copied link to a button or some text - Save - While still in editor mode, click the link # The issue A traceback is shown # Cause The traceback is caused by the call to this function : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/services/website_service.js#L336 In our case, `this.currentWebsite.metadata.mainObject` is undefined, so trying to access `.model` throws. `mainObject` is undefined because the sign link opens an XML file, which does not have any metadata associated to it : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/services/website_service.js#L150-L155 The call to `getUserModelName` is done by the `EditInBackendSystrayItem` component which subscribes to the "CONTENT-UPDATED" event : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/client_actions/website_preview/edit_in_backend.js#L33 However, this method should not be called by the bus because the sign page does not have this systray item. The issue is, the display of the component is managed by the `WebsiteSystrayItem`, based on the `hasEditableRecordInBackend` getter : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/client_actions/website_preview/website_systray_item.js#L10 https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/client_actions/website_preview/website_systray_item.xml#L9 And the re-render of that component that would remove the `EditInBackendSystrayItem` component is triggered by a call to `renderAndAdapt`, which is also subscribed to "CONTENT-UPDATED" : https://github.com/odoo/odoo/blob/f8c29412e71af098b2949f485a8011b01b64b368/addons/website/static/src/components/navbar/navbar.js#L48 So the destroy of the `EditInBackendSystrayItem`, which should remove the listener that calls `getUserModelName` is trigerred AFTER the call to that function is already done opw-6443794 Forward-Port-Of: odoo/odoo#281763
This fixes an issue where clearing an embedded code snippet on a website page did not persist after saving, causing the old embed to reappear on reload. Empty embeds are now properly removed, so editors see the expected empty-content message instead of stale content.
Original PR description
Scenario: - drop embedded code snippet - edit it to add a value - edit it to put it empty - save Result: on reload the embed is not removed and the old value is restored In 18.2, setting it empty would work and you would see an info alert: "Your Embed Code snippet doesn't have anything to display. Click on Edit to modify it." Fix: delete the embed field if it is saved empty. opw-5454289 Forward-Port-Of: odoo/odoo#251537
This fixes an intermittent issue in Point of Sale automated checks where receipt printing failure scenarios could move ahead too quickly on busy systems. The change makes these checks more stable, reducing false failures in validation pipelines without changing customer-facing POS behavior.
Original PR description
The fast-payment tours with automatic receipt printing configure an unreachable printer to exercise the printing-failure path. Once the failed print settles, FeedbackScreen arms a 1500ms timer that auto-navigates to the next order (iface_print_auto). The tour needs to detect the resulting error dialog, confirm it, then click the validation button, all before that timer fires. On a fast machine this comfortably fits, but on a loaded CI runner the sequence can take longer than 1500ms, so the auto-navigation happens first, unmounting the feedback screen before the tour can click ".button.validation" and causing a step timeout that could not be reproduced locally. Click on the feedback screen background right after confirming the dialog to call stopAutomaticSkip() and cancel the pending timer, removing the race entirely. This mirrors the pattern already used in test_automatic_receipt_printing. runbot-946191 Forward-Port-Of: odoo/odoo#288593 Forward-Port-Of: odoo/odoo#284690
This update fixes screens and menus that could show the wrong layout on phones or hide certain options because they were checking outdated system values. It improves reliability for mobile users and restores expected menu behavior in apps such as Accounting, Documents, Appointments, Spreadsheets, ESG, and Time Off.
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
Belgian payroll now applies the correct train pass reimbursement rate for employees under Joint Committee 302 from February 2026. This prevents employees from being under-reimbursed because the sector table values already include the legally required calculation.
Original PR description
Previously, the system was applying an additional 80% reduction ratio on top of the CP 302 train allowance values. However, the rates listed in the official CP 302 sectoral table already incorporate the legally applicable 71.8% calculation. Applying an extra 80% factor resulted in an under-reimbursement of train transport costs for employees under Joint Committee 302. **What:** - Added the missing rule parameter value record _`rule_parameter_cp302_train_reimbursement_ratio_2026`_ setting the ratio factor to 1 (100%) effective from February 1, 2026. task-6532036 Forward-Port-Of: odoo/enterprise#131583 Forward-Port-Of: odoo/enterprise#130291
This fix prevents users from opening additional shop floor menu dialogs while a manufacturing order is already loading. It avoids an error that could appear on slower connections, making shop floor navigation more reliable for manufacturing users.
Original PR description
**Steps to reproduce:** * Install the **Manufacturing** module with **Work Orders** enabled. * Create and confirm a Manufacturing Order with at least one Work Order. * Open the **Shop Floor** view. *…
**Steps to reproduce:** * Install the **Manufacturing** module with **Work Orders** enabled. * Create and confirm a Manufacturing Order with at least one Work Order. * Open the **Shop Floor** view. * On a work order card, click the **gear** icon to open the menu dialog. * Click **Open Manufacturing Order** on a slow network connection. * Before the MO form view finishes loading, quickly click the **gear** icon again and open another dialog (e.g. Log Note). * The MO form view loads, destroying the shop floor component. * Close the Log Note dialog. **Observed behavior:** * An `UncaughtPromiseError: Component is destroyed` error is thrown because the dialog tries to interact with the shop floor component that has already been destroyed by the navigation to the MO form view. **Cause:** * When the user clicks "Open Manufacturing Order", `doAction` is called to navigate to the MO form view, and `props.close()` immediately closes the menu dialog. However, the shop floor component is still visible while the new view is loading. * During this gap, the gear button remains clickable. If the user opens another dialog (e.g. Log Note), that dialog holds a reference to the shop floor component. When the MO form view finishes mounting, the shop floor is destroyed, and closing the stale dialog triggers operations on the destroyed component. **Fix:** * Add an `actionPending` state flag to `MrpDisplayRecord`. When the user selects "Open Manufacturing Order" from the menu dialog, an `onSelect` callback sets `actionPending` to `true`, which disables the gear button and prevents any new dialog from being opened. * The flag is only set for `openMO` (which navigates away and destroys the component), not for other menu actions like Scrap, Add Component, or Log Note which open wizard dialogs and return to the shop floor. opw-6107579 Forward-Port-Of: odoo/enterprise#131538 Forward-Port-Of: odoo/enterprise#123490
The Norwegian eVAT report now lists tax code details in a consistent order every time it is generated. This prevents random test failures and helps ensure the report output remains reliable and predictable.
Original PR description
The Norwegian tax report is built from ordered elements, but the summary detail per tax code is appended from a list that is quasi-directly calculated straight from PostgreSQL. The query does not request a specific result order causing indeterminism (it depends on the query plan chosen: hash vs. sort aggregate, parallel workers) when the whole XML tree is compared against a golden copy in tests. An explicit ORDER BY clause is added to the taxes query. The chosen key is the tax_code, because these can be casted for integer natural sort. The produced XML tree can be compared in its entirety without random failures. REF Runbot; https://runbot.odoo.com/odoo/error/939532 Forward-Port-Of: odoo/enterprise#131702 Forward-Port-Of: odoo/enterprise#131307
This fix ensures rental orders keep track of serial numbers when pickups and returns are handled through stock transfers. It prevents the return wizard from opening without available serial numbers after a partial return, allowing staff to complete subsequent rental returns reliably.
Original PR description
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return…
**Issue** When rental transfers are enabled and rental pickups/returns are processed through stock pickings, it may become impossible to perform a subsequent rental return through the rental return wizard. **Steps to reproduce** - Activate "Rental Transfers" in the settings - Create a rental product P, tracked by serial number - Create two serial numbers for P - Create and confirm a rental order for 2 units of P - Validate the pickup transfer - Partially validate the return transfer without creating a backorder - Open the rental order and click on "Return" -> The return wizard opens without any available serial number and validation fails with a serial number-related error. **Cause** When clicking on "Return", if there is no pending pickup/return transfer: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/models/sale_order.py#L62-L68 the rental return wizard is opened directly: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/models/sale_order.py#L316 No serial number is prefilled in the wizard because `returned_lot_ids` is empty: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L122-L124 This is because `returnable_lot_ids` is empty as well. `returnable_lot_ids` is computed while generating the wizard lines: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L38 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_renting/wizard/rental_processing.py#L47-L48 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L99-L106 and `returnable_lots` is empty because both `pickedup_lots` and `returned_lots` are. Those fields are currently only populated through the rental wizard flow: https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L42-L43 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L160-L161 https://github.com/odoo/enterprise/blob/d1ba2417affb81c4351ab9f86bc0a6ad5ceb8caf/sale_stock_renting/wizard/rental_processing.py#L166-L167 Since this flow uses stock pickings instead of the rental wizard, those fields are never updated, preventing the wizard from determining any returnable serial number. opw-6150305 Forward-Port-Of: odoo/enterprise#127590 Forward-Port-Of: odoo/enterprise#119257
Swiss payroll users can now save draft hourly payslips without losing a manually entered wage factor. This prevents incorrect resets to zero and reduces the need to re-enter payroll data.
Original PR description
**Steps to reproduce:** 1. Create a draft Swiss ELM payslip for an hourly-paid employee with no automatic hourly work-entry/input 2. In the Wages tab, manually update the `Factor` (`rate`) field of the `Hourly Salary` line 3. Save the payslip **Issue:** The manually entered `Factor` is reset to `0.00` **Cause:** - `l10n_ch_swiss_wage_ids` is a stored computed field without explicit `readonly=False`, causing manual modifications to the line values to be discarded during field recomputation on save. opw-6536243 Forward-Port-Of: odoo/enterprise#131636 Forward-Port-Of: odoo/enterprise#130824
Updates to employee working schedules now create clearer leave allocation messages. The message correctly shows the related contract and includes the details of what changed, helping HR teams understand and audit schedule updates more easily.
Original PR description
When modifying an employee's working schedule via the wizard, the logged chatter message on the leave allocation incorrectly evaluated the contract name to "False" and lacked details on the changes made. This PR fixes the problem by calculating and logging the necessary details regarding the changes.
Belgian payroll now correctly handles the Special Social Contribution when generating a 13th month payslip. If there is no regular monthly payslip for the same period, the contribution is set to zero, helping avoid incorrect payroll deductions.
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
The Nilvera e-invoice integration now keeps the amount-in-words note fully in Turkish when invoices use a foreign currency. This avoids mixed-language wording such as English currency subunits appearing in Turkish e-invoices, helping keep documents compliant and clear.
Original PR description
### Issue before this commit: When generating a Nilvera e-invoice in a foreign currency (e.g., EUR), the amount written in words inside the <cbc:Note> tag contained a mix of languages. While the…
### Issue before this commit: When generating a Nilvera e-invoice in a foreign currency (e.g., EUR), the amount written in words inside the <cbc:Note> tag contained a mix of languages. While the numbers were correctly translated to Turkish, the currency subunit label was fetched using the customer's language. This resulted in a partially translated string (like "... SIFIR CENTS") instead of the expected fully Turkish text (like "... SIFIR SENT"). ### Steps to reproduce the issue: 1. Download Accounting and l10n_tr_nilvera_einvoice 2. Create an API KEY: https://docs.google.com/document/d/1EUzvTBnSm9-VwIfBsX299MHGXIVys-uijnsJ1fpz7vI/edit?tab=t.0#heading=h.e6i8a29lff5t 3. Go to a turkish client on the Accounting tab and click on 'Verify' for the Nilvera status 4. Go to currencies and activate EUR (be sure there is also the translation for currency subunit) 5. Go to invoices, create one for the Turkish customer you already verified setting the currency as EUR and send it with Nilvera 6. See that current output is <cbc:Note>YALNIZ : BEŞYÜZDÖRT EUR SIFIR CENTS</cbc:Note> (Turkish numbers with English subunit) but the expected output is <cbc:Note>YALNIZ : BEŞYÜZDÖRT EUR SIFIR SENT</cbc:Note> (Fully Turkish text) ### Cause of the issue: The issue occurs because the currency_subunit_label field is not translated but fetched using the language of the customer in the invoice. ### Reason to introduce the fix: To ensure that the amount in words inside the <cbc:Note> tag is completely formatted in Turkish, complying with Nilvera and local e-invoicing requirements, regardless of the customer language. opw-6523794 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#288122 Forward-Port-Of: odoo/odoo#287630
Italian point-of-sale refunds can now be processed from a different trusted POS without receipt printing failures. The system keeps the original printer details with the order, so refund receipts use the correct fiscal printer information.
Original PR description
Steps to reproduce: - Set up two POS configurations and add each to "Trusted POS"; - Connect a different fiscal printer to each POS configuration; - Process an order on POS A; - Refund that order on POS B. **Issue**: The refund receipt fails to print. The system currently transmits the serial number of the active POS configuration's printer instead of the printer that processed the original order. As a result, POS B's printer receives its own serial number alongside order identifiers that do not exist in its local fiscal memory. **Solution**: Store the processing printer's serial number directly on the `pos_order model` to ensure the correct serial number is transmitted during cross-POS refunds. [opw-6499079](https://www.odoo.com/odoo/project/49/tasks/6499079)
The add-to-cart notification now correctly shows loyalty reward progress again when shoppers add eligible products to their cart. This helps customers see their discount or loyalty progress at the right moment, reducing confusion during checkout.
Original PR description
Versions -------- - master Steps ----- 1. Install eCommerce and Discounts & Loyalty (demo data). 2. On the shop, add a Furniture product to the cart. Issue ----- No progress bar in the add to cart notification. Cause ----- The patch on `ItemAddedNotification` was not migrated along bb606bbe6330 and still declares its prop through the static `props`, which Owl no longer reads. Solution -------- Declare the prop through its own `useProps` call in a patched `setup`, and read it from `this.loyaltyProps` in the template. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Rounded point-of-sale payments are now recorded correctly even when the stock app is not installed. This prevents accounting imbalances when closing PoS sessions, reducing disruption for retailers using cash rounding.
Original PR description
Before this commit: = - The rounding move line creation was moved from point_of_sale to pos_stock while removing the dependency of stock on point_of_sale. - As a result, when pos_stock was not installed, no rounding move lines were created for rounded PoS payments, leading to unbalanced journal entries during session closing. After this commit: = - Restored the rounding move line creation in point_of_sale so that rounded payments are correctly handled. task-6214240 runbot-error-242920 Forward-Port-Of: odoo/odoo#264288
Long product names on smaller printed labels can now wrap onto a new line instead of being cut off with an ellipsis. This makes printed labels easier to read and keeps label behavior consistent with the previous version.
Original PR description
In 19.0, long names wrap to a newline, while in masters everything is clipped to one line with ellipsis. This commit will increase the height for title fields on smaller labels so they behave the same as on 19.0 and can still wrap --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Installing Fleet Stock directly now also enables the required batch transfer access for internal users. This prevents the settings page from accidentally triggering a Fleet Stock uninstallation prompt, making setup safer and smoother.
Original PR description
If stock_fleet is installed directly without enabling batch transfers beforehand, stock.group_stock_picking_batch is not enabled for internal users. When opening the settings, the onchange on group_stock_picking_batch unchecks module_stock_fleet, and saving triggers the uninstallation wizard of stock_fleet. This commit fixes the issue by enabling stock.group_stock_picking_batch for base.group_user in the post_init_hook of stock_fleet. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
AI agents now manage their URL-based attachments more reliably, so each agent keeps the right linked content separate from others. This helps prevent mix-ups between agents and supports smoother background processing of URL content.
Original PR description
Many2one URL attachments' fixes to ensure each agent has its own URL attachments.
Point of Sale now avoids errors when opening a session if the browser has old cached data for models that were renamed or removed. This helps users resume POS work without needing a manual data reload after module changes or upgrades.
Original PR description
In PR-#[225341](https://github.com/odoo/odoo/pull/225341) we started passing all the models which are cached on the front end directly to the back end, but if the database existed before and the front end cached models which no longer exist, either because the model name changes (such as pos.product.template.snooze -> pos.snooze), or because a module was uninstalled (removing pos_restaurant_appointment), the front end would ask the back end for models which no longer exist, and that would error. When we do the reload data, all the local cache would be deleted and then we could launch the POS. To fix it, now the back end will check whether the model exists before trying to filter on it, and just ignore it if it doesn't exist Task-[6562705](https://www.odoo.com/odoo/project/1737/tasks/6562705) --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Inter-company purchase receipts are no longer marked as already picked when they are created from a confirmed sales order. This lets warehouse teams process the receipt normally in the Barcode app and avoids confusion or skipped handling steps.
Original PR description
Issue ----- When confirming a SO, the corresponding PO picking should not have its' MLs set as `picked` to allow treating the transfer in the barcode app. Steps to reproduce ----- - Create 2 companies A & B - Settings > Inter-Company Transactions - Create Purchase Orders - Set the created PO to be "Validated" by default - Create a SO from company A to B - Validate the OUT picking in company A - Open the PO in company B - Go to its' picking and open it in barcode > The line is already picked ----- Ticket: opw-6481828 Forward-Port-Of: odoo/enterprise#131391
The Austrian point-of-sale setup test data now uses the updated currency reference. This keeps automated checks aligned with the latest shared currency data and helps prevent false failures during validation.
Original PR description
In this commit: ------- - Update the currency ID to 2 when creating the PoS configuration for the Austrian localization, reflecting the corresponding currency data change in the related PR. Task-6576886
After archiving or deleting an AI agent, users are now sent to the correct agent page. This prevents a broken or incorrect navigation path and keeps agent management workflows smooth.
Original PR description
Use the ai_agentic namespace when redirecting after archiving or deleting an agent. task-id-6497307
Odoo no longer crashes when a user clicks an embedded file link whose attachment has since been deleted. Instead, the editor handles the missing file safely, keeping records with HTML fields usable even when old links are stale.
Original PR description
Clicking a stale /web/content/<id> link in an HTML field crashed the client after the related attachment had been deleted. **Steps to reproduce:** 1. Open any record with an HTML field (e.g. Project…
Clicking a stale /web/content/<id> link in an HTML field crashed the client after the related attachment had been deleted. **Steps to reproduce:** 1. Open any record with an HTML field (e.g. Project > Task description). 2. Upload a file into the HTML field to embed an attachment link. 3. Save the record. 4. Delete the uploaded attachment from Chatter > Files, or from Settings > Technical > Attachments. 5. Reopen the record and click the embedded file link. **Client Error:** `TypeError: Cannot destructure property 'mimetype' of '(intermediate value)' as it is undefined at LinkPopover.loadAsyncLinkPreview` `TypeError: Cannot destructure property 'type' of '(intermediate value)' as it is undefined at LinkPopover.updateDocumentState` When the attachment is missing, ormService.read returns an empty array, so fetchAttachmentMetaData returned undefined instead of entering its catch block. LinkPopover then crashed while destructuring that result in loadAsyncLinkPreview and updateDocumentState. Return a safe fallback metadata object when the attachment cannot be found so both code paths keep working without crashing. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/odoo#279281
New Singapore companies will now be created with the correct accounting configuration for inventory purchases. This ensures price differences between product cost and purchase price are posted to the expected account, improving the accuracy of vendor bills and stock valuation.
Original PR description
Singaporean companies didn't have the anglo saxon accounting enabled. Price difference account was then not hit when buying a product with a different price than the one in the product page. Steps to reproduce: ------------------- * Create a product P * Set the costing method to "Standard Price" and the inventory valuation to "Perpetual (automated)" * Make sure a price difference account is set in the product category * Create a RFQ for P and set a different price than the one in the product page * Confirm the RFQ and receive the product * Create a vendor bill for the RFQ and validate it > Observation: If you check the lines in the bill there is no price difference account hit. Why the fix: ------------ We set anglo saxon accounting to True so that all new companies have the correct setup. opw-6525817 Forward-Port-Of: odoo/odoo#288261
This fixes a display issue in Discuss call settings where the selected speaker could be shown with the microphone name when Chrome reports matching default device IDs. Users now see the correct device name in the dropdown, reducing confusion when choosing audio devices for calls.
Original PR description
Steps to reproduce: - Have an input audio device and an output audio device with different names but same device ID. Using Chrome, if the OS only sees one of each, both should have "default" as device ID. (alternatively, modify the code at `updateDevicesList` to simulate having devices of that kind). - Select those devices in the call settings UI, then open the settings UI dropdown again. => The "input" device is properly shown but the "output" device shows the "input" device names (while in fact this is the right one selected in the inner dropdown). This happens since [1]. Before that there were native `<select>` nodes that filtered devices by kind before rendering each option, and the "default" value of Chrome was not really handled. [1]: https://github.com/odoo/odoo/commit/93d0931fba1f467de265200b6a0463cf7edf9534 Related to task-6533808 Forward-Port-Of: odoo/odoo#288537 Forward-Port-Of: odoo/odoo#288285
This update removes an obsolete internal warning in the mail module because message access parameters are already checked earlier in the request flow. It simplifies the code without changing how users interact with the system.
Original PR description
`_get_message_with_access` already filters kwargs against `_get_allowed_access_params` in the http layer before calling the model, and no non-controller caller exists. The model side warning is dead code.
This update modernizes internal component definitions across HR-related, expense, payroll, document extraction, and Knowledge features. It helps keep these areas compatible with the latest interface framework and improves reliability without changing day-to-day user workflows.
Original PR description
See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr Forward-Port-Of: odoo/enterprise#131835
This update replaces an older internal mechanism used by Odoo's web interface with its newer Owl 3 equivalent. It helps keep the platform maintainable during the Owl 3 migration without introducing visible changes for end users.
Original PR description
As part of the Owl 3 migration, this pr aims to replace **onWillUpdateProps** hook with the appropriate Owl 3 alternatives. enterprise: https://github.com/odoo/enterprise/pull/130802 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Documents and Studio were adjusted as part of Odoo's move to the newer Owl 3 interface framework. This keeps these areas aligned with the platform upgrade while preserving existing user-facing behavior.
Original PR description
`* = ['web_studio']` As part of the Owl 3 migration, this pr aims to replace **onWillUpdateProps** hook with the appropriate Owl 3 alternatives. community: https://github.com/odoo/odoo/pull/283124
This update modernizes how many Odoo web interface components define their expected inputs, preparing them for the newer OWL framework version. It is an internal cleanup that helps keep validation consistent and reduces future migration risk, with no intended change to day-to-day user workflows.
Original PR description
See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This refactoring reorganizes the web interface's drag-and-drop code to make it easier to maintain and extend. It should help future interface improvements be delivered more reliably, with limited direct impact for everyday users.
Original PR description
TODO: PR message --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update modernizes how several Odoo interface components handle their settings behind the scenes. It helps keep the system compatible with newer framework standards and improves validation without changing day-to-day user workflows.
Original PR description
See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
This update modernizes internal component definitions across HR, payroll, expense, document extraction and Knowledge features. It helps keep these screens compatible with the latest interface framework and improves reliability without changing day-to-day user workflows.
Original PR description
See commit messages for details. --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr