m2ms are internally represented as "6" commands, however in domains it's possible to compare an m2m value to a list of ids (to investigate: whether this is an artifact of internal webclient repr or part of the real contract). Add a workaround in SSF modifier computation to convert the m2m command storage to a simple ids list. A better fix would probably have been to represent the m2m as a list of ids internally (and only convert on load / save) however it not completely trivial as it has to be done recursively in order to properly handle an m2m inside an o2m. So it's a complete change of the internal data model (which should probably go alongide more fundamental changes e.g. properly handling parent refs, etc...) Also add very minor support for widgets (mostly so it's possible to set widget=many2many on an o2m field). closes odoo/odoo#39467 X-original-commit: 927979beff9e5d4f6c88078862779eaa88f4e07d Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
282 lines
8.0 KiB
Python
282 lines
8.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import division
|
|
|
|
from itertools import count, zip_longest
|
|
|
|
from odoo import api, fields, models
|
|
|
|
class A(models.Model):
|
|
_name = 'test_testing_utilities.a'
|
|
_description = 'Testing Utilities A'
|
|
|
|
f1 = fields.Char(required=True)
|
|
f2 = fields.Integer(default=42)
|
|
f3 = fields.Integer()
|
|
f4 = fields.Integer(compute='_compute_f4')
|
|
f5 = fields.Integer()
|
|
f6 = fields.Integer()
|
|
|
|
@api.onchange('f2')
|
|
def _on_change_f2(self):
|
|
self.f3 = int(self.f2 / 2)
|
|
self.f5 = self.f2
|
|
self.f6 = self.f2
|
|
|
|
@api.depends('f1', 'f2')
|
|
def _compute_f4(self):
|
|
for r in self:
|
|
r.f4 = r.f2 / (int(r.f1) or 1)
|
|
|
|
class B(models.Model):
|
|
_name = 'test_testing_utilities.readonly'
|
|
_description = 'Testing Utilities Readonly'
|
|
|
|
f1 = fields.Integer(default=1, readonly=True)
|
|
f2 = fields.Integer(compute='_compute_f2')
|
|
|
|
@api.depends('f1')
|
|
def _compute_f2(self):
|
|
for r in self:
|
|
r.f2 = 2 * r.f1
|
|
|
|
class C(models.Model):
|
|
_name = 'test_testing_utilities.c'
|
|
_description = 'Testing Utilities C'
|
|
|
|
name = fields.Char("name", required=True)
|
|
f2 = fields.Many2one('test_testing_utilities.m2o')
|
|
|
|
@api.onchange('f2')
|
|
def _on_change_f2(self):
|
|
self.name = self.f2.name
|
|
|
|
class M2O(models.Model):
|
|
_name = 'test_testing_utilities.m2o'
|
|
_description = 'Testing Utilities Many To One'
|
|
|
|
name = fields.Char(required=True)
|
|
|
|
class M2Onchange(models.Model):
|
|
_name = 'test_testing_utilities.d'
|
|
_description = 'Testing Utilities D'
|
|
|
|
# used to check that defaults & onchange to m2o work
|
|
f = fields.Many2one(
|
|
'test_testing_utilities.m2o',
|
|
required=True,
|
|
default=lambda self: self.env['test_testing_utilities.m2o'].search(
|
|
[], limit=1
|
|
)
|
|
)
|
|
f2 = fields.Char()
|
|
|
|
@api.onchange('f2')
|
|
def _on_change_f2(self):
|
|
self.f = self.env['test_testing_utilities.m2o'].search([
|
|
('name', 'ilike', self.f2),
|
|
], limit=1) if self.f2 else False
|
|
|
|
class M2MChange(models.Model):
|
|
_name = 'test_testing_utilities.e'
|
|
_description = 'Testing Utilities E'
|
|
|
|
m2m = fields.Many2many('test_testing_utilities.sub2')
|
|
count = fields.Integer(compute='_m2m_count', inverse='_set_count')
|
|
|
|
@api.depends('m2m')
|
|
def _m2m_count(self):
|
|
for r in self:
|
|
r.count = len(r.m2m)
|
|
|
|
def _set_count(self):
|
|
for r in self:
|
|
r.write({
|
|
'm2m': [
|
|
(0, False, {'name': str(n)})
|
|
for n, v in zip_longest(range(r.count), r.m2m or [])
|
|
if v is None
|
|
]
|
|
})
|
|
|
|
class M2MSub(models.Model):
|
|
_name = 'test_testing_utilities.sub2'
|
|
_description = 'Testing Utilities Subtraction 2'
|
|
|
|
name = fields.Char()
|
|
|
|
class M2MChange2(models.Model):
|
|
_name = 'test_testing_utilities.f'
|
|
_description = 'Testing Utilities F'
|
|
|
|
def _get_some(self):
|
|
r = self.env['test_testing_utilities.sub2'].search([], limit=2)
|
|
return r
|
|
|
|
m2m = fields.Many2many(
|
|
'test_testing_utilities.sub2',
|
|
default=_get_some,
|
|
)
|
|
m2o = fields.Many2one('test_testing_utilities.sub2')
|
|
|
|
@api.onchange('m2o')
|
|
def _on_change_m2o(self):
|
|
self.m2m = self.m2m | self.m2o
|
|
|
|
class M2MReadonly(models.Model):
|
|
_name = 'test_testing_utilities.g'
|
|
_description = 'Testing Utilities G'
|
|
|
|
m2m = fields.Many2many('test_testing_utilities.sub3', readonly=True)
|
|
|
|
class M2MSub3(models.Model):
|
|
_name = 'test_testing_utilities.sub3'
|
|
_description = 'Testing Utilities Subtraction 3'
|
|
|
|
name = fields.Char()
|
|
|
|
class O2MChange(models.Model):
|
|
_name = 'test_testing_utilities.parent'
|
|
_description = 'Testing Utilities Parent'
|
|
|
|
value = fields.Integer(default=1)
|
|
v = fields.Integer()
|
|
subs = fields.One2many('test_testing_utilities.sub', 'parent_id')
|
|
|
|
@api.onchange('value', 'subs')
|
|
def _onchange_values(self):
|
|
self.v = self.value + sum(s.value for s in self.subs)
|
|
|
|
class O2MSub(models.Model):
|
|
_name = 'test_testing_utilities.sub'
|
|
_description = 'Testing Utilities Subtraction'
|
|
|
|
name = fields.Char(compute='_compute_name')
|
|
value = fields.Integer(default=2)
|
|
v = fields.Integer()
|
|
parent_id = fields.Many2one('test_testing_utilities.parent')
|
|
has_parent = fields.Boolean()
|
|
|
|
@api.onchange('value')
|
|
def _onchange_value(self):
|
|
self.v = self.value
|
|
|
|
@api.depends('v')
|
|
def _compute_name(self):
|
|
for r in self:
|
|
r.name = str(r.v)
|
|
|
|
@api.onchange('has_parent')
|
|
def _onchange_has_parent(self):
|
|
if self.has_parent:
|
|
self.value = self.parent_id.value
|
|
|
|
class O2MDefault(models.Model):
|
|
_name = 'test_testing_utilities.default'
|
|
_description = 'Testing Utilities Default'
|
|
|
|
def _default_subs(self):
|
|
return [
|
|
(0, 0, {'v': 5})
|
|
]
|
|
value = fields.Integer(default=1)
|
|
v = fields.Integer()
|
|
subs = fields.One2many('test_testing_utilities.sub3', 'parent_id', default=_default_subs)
|
|
|
|
class O2MSub3(models.Model):
|
|
_name = 'test_testing_utilities.sub3'
|
|
_description = 'Testing Utilities Subtraction 3'
|
|
|
|
name = fields.Char(compute='_compute_name')
|
|
value = fields.Integer(default=2)
|
|
v = fields.Integer(default=6)
|
|
parent_id = fields.Many2one('test_testing_utilities.default')
|
|
|
|
@api.onchange('value')
|
|
def _onchange_value(self):
|
|
self.v = self.value
|
|
|
|
@api.depends('v')
|
|
def _compute_name(self):
|
|
for r in self:
|
|
r.name = str(r.v)
|
|
|
|
|
|
class O2MRecursive(models.Model):
|
|
_name = _description = 'test_testing_utilities.recursive'
|
|
|
|
one_to_many_id = fields.Many2one('test_testing_utilities.recursive', readonly=True)
|
|
many_to_one_ids = fields.One2many('test_testing_utilities.recursive', 'one_to_many_id', readonly=True)
|
|
|
|
|
|
class O2MOnchangeParent(models.Model):
|
|
_name = 'test_testing_utilities.onchange_parent'
|
|
_description = 'Testing Utilities Onchange Parent'
|
|
|
|
line_ids = fields.One2many('test_testing_utilities.onchange_line', 'parent')
|
|
|
|
@api.onchange('line_ids')
|
|
def _onchange_line_ids(self):
|
|
for line in self.line_ids.filtered(lambda l: l.flag):
|
|
self.env['test_testing_utilities.onchange_line'].new({'parent': self.id})
|
|
|
|
|
|
class M2OOnchangeLine(models.Model):
|
|
_name = 'test_testing_utilities.onchange_line'
|
|
_description = 'Testing Utilities Onchange Line'
|
|
|
|
parent = fields.Many2one('test_testing_utilities.onchange_parent')
|
|
dummy = fields.Float()
|
|
flag = fields.Boolean(store=False)
|
|
|
|
@api.onchange('dummy')
|
|
def _onchange_flag(self):
|
|
self.flag = True
|
|
|
|
class O2MChangeCount(models.Model):
|
|
_name = 'test_testing_utilities.onchange_count'
|
|
_description = _name
|
|
|
|
count = fields.Integer()
|
|
line_ids = fields.One2many('test_testing_utilities.onchange_count_sub', 'parent')
|
|
|
|
@api.onchange('count')
|
|
def _onchange_count(self):
|
|
Sub = self.env['test_testing_utilities.onchange_count_sub']
|
|
recs = Sub
|
|
for i in range(self.count):
|
|
recs |= Sub.new({'name': str(i)})
|
|
self.line_ids = recs
|
|
|
|
class O2MChangeSub(models.Model):
|
|
_name = 'test_testing_utilities.onchange_count_sub'
|
|
_description = _name
|
|
|
|
parent = fields.Many2one('test_testing_utilities.onchange_count')
|
|
name = fields.Char()
|
|
|
|
class O2MReadonlySubfield(models.Model):
|
|
_name = 'o2m_readonly_subfield_parent'
|
|
_description = _name
|
|
|
|
line_ids = fields.One2many('o2m_readonly_subfield_child', 'parent_id')
|
|
|
|
class O2MReadonlySubfieldChild(models.Model):
|
|
_name = _description = 'o2m_readonly_subfield_child'
|
|
|
|
name = fields.Char()
|
|
parent_id = fields.Many2one('o2m_readonly_subfield_parent')
|
|
f = fields.Integer(compute='_compute_f', inverse='_inverse_f', readonly=True)
|
|
|
|
@api.depends('name')
|
|
def _compute_f(self):
|
|
for r in self:
|
|
r.f = len(r.name) if r.name else 0
|
|
|
|
def _inverse_f(self):
|
|
raise AssertionError("Inverse of f should not be called")
|
|
|
|
class ReqBool(models.Model):
|
|
_name = _description = 'test_testing_utilities.req_bool'
|
|
|
|
f_bool = fields.Boolean(required=True)
|