Thursday, September 17, 2026
37 changes · master
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
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.…
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
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.