Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions orm_forward_compatibility/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
=========================
ORM Forward Compatibility
=========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:69068e4f7be049656b46a19592cfb15d2468bddb3e38f8522adbb63adf8ba386
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/18.0/orm_forward_compatibility
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-orm_forward_compatibility
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module backports a subset of the Odoo 19+ ORM API onto Odoo 18.0.
Backporting a module to 18.0 then requires fewer adaptations.

Backported so far:

- ``Domain``, the domain object introduced in Odoo 19.
- The typed ``ir.config_parameter`` getters ``get_str``, ``get_int``,
``get_float`` and ``get_bool``, which return a typed value or a
default instead of ``False``.

**Table of contents**

.. contents::
:local:

Usage
=====

**Domain**

Replace the Odoo 19 import ``from odoo.fields import Domain`` and leave
the rest untouched:

.. code:: python

from odoo.addons.orm_forward_compatibility import Domain

domain = Domain("partner_id", "=", partner.id) & Domain([("state", "=", "done")])

Not supported yet:

- Relative-date literals
- custom SQL domains
- ``any!`` / ``not any!`` operators

**Typed ir.config_parameter getters**

.. code:: python

limit = self.env["ir.config_parameter"].sudo().get_int("my_module.limit", 20)

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20orm_forward_compatibility%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Camptocamp

Contributors
------------

- `Camptocamp <https://www.camptocamp.com>`__:

- David Gallay <david.gallay@camptocamp.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/18.0/orm_forward_compatibility>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions orm_forward_compatibility/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
from . import models
from .domain import Domain
14 changes: 14 additions & 0 deletions orm_forward_compatibility/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
{
"name": "ORM Forward Compatibility",
"version": "18.0.1.0.0",
"development_status": "Beta",
"summary": "Backport newer-version ORM helpers (Domain) onto Odoo 18.0",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/server-tools",
"license": "LGPL-3",
"category": "Hidden/Dependency",
"depends": ["base"],
"installable": True,
}
78 changes: 78 additions & 0 deletions orm_forward_compatibility/domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
"""Minimal ``Domain`` shim backporting the Odoo 19+ ``odoo.fields.Domain`` API.

Odoo 19 replaced list-domains by a ``Domain`` AST object (``odoo/orm/domains.py``,
~2000 lines). Backported modules written against 19/20 import it as
``from odoo.fields import Domain``. Rather than porting the whole engine, this
shim reimplements *only* the surface those modules use, delegating to 18's
``odoo.osv.expression``.

Because ``Domain`` subclasses ``list`` and normalises itself to a plain 18
list-domain, a shim instance can be passed straight to ``search``/``_search``.

Supported surface (extend as new call sites appear):
- ``Domain([('a', '=', 1), ...])`` and ``Domain('a', '=', 1)`` constructors
- ``&`` / ``|`` / ``~`` operators
- ``Domain.AND(iterable)`` / ``Domain.OR(iterable)``
- ``Domain.TRUE`` / ``Domain.FALSE``
- ``.optimize_full(model)`` -> validates against the model, returns self

NOT supported (keep such call sites hand-adapted on 18):
- relative-date literals in leaves (e.g. ``('date', '<', '-1d')``)
- custom SQL domains, ``any!``/``not any!`` internal operators
"""

from odoo.osv import expression


class Domain(list):
def __init__(self, *args):
if len(args) == 3:
domain = [tuple(args)]
elif len(args) == 1:
arg = args[0]
if isinstance(arg, Domain):
domain = list(arg)
elif arg is True or arg == []:
domain = list(expression.TRUE_DOMAIN)
elif arg is False:
domain = list(expression.FALSE_DOMAIN)
elif isinstance(arg, list | tuple):
domain = expression.normalize_domain(list(arg))
else:
raise TypeError(f"Domain() invalid argument type: {arg!r}")
else:
raise TypeError(f"Domain() invalid arguments: {args!r}")
super().__init__(domain)

def __and__(self, other):
return Domain(expression.AND([list(self), list(Domain(other))]))

__rand__ = __and__

def __or__(self, other):
return Domain(expression.OR([list(self), list(Domain(other))]))

__ror__ = __or__

def __invert__(self):
return Domain(["!"] + list(self))

@staticmethod
def AND(items):
return Domain(expression.AND([list(Domain(item)) for item in items]))

@staticmethod
def OR(items):
return Domain(expression.OR([list(Domain(item)) for item in items]))

def optimize_full(self, model):
"""Validate the domain against ``model`` (raises on unknown fields)."""
model._where_calc(list(self))
return self


# v19/v20 call sites reference ``Domain.TRUE`` / ``Domain.FALSE`` as attributes.
Domain.TRUE = Domain(True)
Domain.FALSE = Domain(False)
3 changes: 3 additions & 0 deletions orm_forward_compatibility/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
from . import ir_config_parameter
34 changes: 34 additions & 0 deletions orm_forward_compatibility/models/ir_config_parameter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
from odoo import models
from odoo.tools import str2bool


class IrConfigParameter(models.Model):
_inherit = "ir.config_parameter"

def get_str(self, key, default=""):
value = self.get_param(key)
return default if value is False else str(value)

def get_int(self, key, default=0):
value = self.get_param(key)
if value is False:
return default
try:
return int(value)
except (TypeError, ValueError):
return default

def get_float(self, key, default=0.0):
value = self.get_param(key)
if value is False:
return default
try:
return float(value)
except (TypeError, ValueError):
return default

def get_bool(self, key, default=False):
value = self.get_param(key)
return default if value is False else str2bool(value, default)
3 changes: 3 additions & 0 deletions orm_forward_compatibility/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions orm_forward_compatibility/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [Camptocamp](https://www.camptocamp.com):
- David Gallay \<<david.gallay@camptocamp.com>\>
8 changes: 8 additions & 0 deletions orm_forward_compatibility/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
This module backports a subset of the Odoo 19+ ORM API onto Odoo 18.0.
Backporting a module to 18.0 then requires fewer adaptations.

Backported so far:

- `Domain`, the domain object introduced in Odoo 19.
- The typed `ir.config_parameter` getters `get_str`, `get_int`, `get_float` and
`get_bool`, which return a typed value or a default instead of `False`.
20 changes: 20 additions & 0 deletions orm_forward_compatibility/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
**Domain**

Replace the Odoo 19 import `from odoo.fields import Domain` and leave the rest untouched:

``` python
from odoo.addons.orm_forward_compatibility import Domain

domain = Domain("partner_id", "=", partner.id) & Domain([("state", "=", "done")])
```

Not supported yet:
- Relative-date literals
- custom SQL domains
- `any!` / `not any!` operators

**Typed ir.config_parameter getters**

``` python
limit = self.env["ir.config_parameter"].sudo().get_int("my_module.limit", 20)
```
Loading
Loading