743 lines
30 KiB
Python
743 lines
30 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo import api, models, Command
|
||
|
from odoo.addons.base.tests.common import SavepointCaseWithUserDemo
|
||
|
from odoo.tools import mute_logger, unique, lazy
|
||
|
from odoo.exceptions import AccessError
|
||
|
|
||
|
|
||
|
class TestAPI(SavepointCaseWithUserDemo):
|
||
|
""" test the new API of the ORM """
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super(TestAPI, cls).setUpClass()
|
||
|
cls._load_partners_set()
|
||
|
|
||
|
def assertIsRecordset(self, value, model):
|
||
|
self.assertIsInstance(value, models.BaseModel)
|
||
|
self.assertEqual(value._name, model)
|
||
|
|
||
|
def assertIsRecord(self, value, model):
|
||
|
self.assertIsRecordset(value, model)
|
||
|
self.assertTrue(len(value) <= 1)
|
||
|
|
||
|
def assertIsNull(self, value, model):
|
||
|
self.assertIsRecordset(value, model)
|
||
|
self.assertFalse(value)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_00_query(self):
|
||
|
""" Build a recordset, and check its contents. """
|
||
|
domain = [('name', 'ilike', 'j'), ('id', 'in', self.partners.ids)]
|
||
|
partners = self.env['res.partner'].search(domain)
|
||
|
|
||
|
# partners is a collection of browse records
|
||
|
self.assertTrue(partners)
|
||
|
|
||
|
# partners and its contents are instance of the model
|
||
|
self.assertIsRecordset(partners, 'res.partner')
|
||
|
for p in partners:
|
||
|
self.assertIsRecord(p, 'res.partner')
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_01_query_offset(self):
|
||
|
""" Build a recordset with offset, and check equivalence. """
|
||
|
partners1 = self.env['res.partner'].search([('id', 'in', self.partners.ids)], offset=5)
|
||
|
partners2 = self.env['res.partner'].search([('id', 'in', self.partners.ids)])[5:]
|
||
|
self.assertIsRecordset(partners1, 'res.partner')
|
||
|
self.assertIsRecordset(partners2, 'res.partner')
|
||
|
self.assertEqual(list(partners1), list(partners2))
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_02_query_limit(self):
|
||
|
""" Build a recordset with offset, and check equivalence. """
|
||
|
partners1 = self.env['res.partner'].search([('id', 'in', self.partners.ids)], order='id asc', limit=5)
|
||
|
partners2 = self.env['res.partner'].search([('id', 'in', self.partners.ids)], order='id asc')[:5]
|
||
|
self.assertIsRecordset(partners1, 'res.partner')
|
||
|
self.assertIsRecordset(partners2, 'res.partner')
|
||
|
self.assertEqual(list(partners1), list(partners2))
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_03_query_offset_limit(self):
|
||
|
""" Build a recordset with offset and limit, and check equivalence. """
|
||
|
partners1 = self.env['res.partner'].search([('id', 'in', self.partners.ids)], order='id asc', offset=3, limit=7)
|
||
|
partners2 = self.env['res.partner'].search([('id', 'in', self.partners.ids)], order='id asc')[3:10]
|
||
|
self.assertIsRecordset(partners1, 'res.partner')
|
||
|
self.assertIsRecordset(partners2, 'res.partner')
|
||
|
self.assertEqual(list(partners1), list(partners2))
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_04_query_count(self):
|
||
|
""" Test the search_count method. """
|
||
|
self.cr.execute("SELECT COUNT(*) FROM res_partner WHERE active")
|
||
|
count1 = self.cr.fetchone()[0]
|
||
|
count2 = self.env['res.partner'].search_count([])
|
||
|
self.assertIsInstance(count1, int)
|
||
|
self.assertIsInstance(count2, int)
|
||
|
self.assertEqual(count1, count2)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_05_immutable(self):
|
||
|
""" Check that a recordset remains the same, even after updates. """
|
||
|
domain = [('name', 'ilike', 'g'), ('id', 'in', self.partners.ids)]
|
||
|
partners = self.env['res.partner'].search(domain)
|
||
|
self.assertTrue(partners)
|
||
|
ids = partners.ids
|
||
|
|
||
|
# modify those partners, and check that partners has not changed
|
||
|
partners.write({'active': False})
|
||
|
self.assertEqual(ids, partners.ids)
|
||
|
|
||
|
# redo the search, and check that the result is now empty
|
||
|
partners2 = self.env['res.partner'].search(domain)
|
||
|
self.assertFalse(partners2)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_06_fields(self):
|
||
|
""" Check that relation fields return records, recordsets or nulls. """
|
||
|
user = self.env.user
|
||
|
self.assertIsRecord(user, 'res.users')
|
||
|
self.assertIsRecord(user.partner_id, 'res.partner')
|
||
|
self.assertIsRecordset(user.groups_id, 'res.groups')
|
||
|
|
||
|
for name, field in self.partners._fields.items():
|
||
|
if field.type == 'many2one':
|
||
|
for p in self.partners:
|
||
|
self.assertIsRecord(p[name], field.comodel_name)
|
||
|
elif field.type == 'reference':
|
||
|
for p in self.partners:
|
||
|
if p[name]:
|
||
|
self.assertIsRecord(p[name], field.comodel_name)
|
||
|
elif field.type in ('one2many', 'many2many'):
|
||
|
for p in self.partners:
|
||
|
self.assertIsRecordset(p[name], field.comodel_name)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_07_null(self):
|
||
|
""" Check behavior of null instances. """
|
||
|
# select a partner without a parent
|
||
|
partner = self.env['res.partner'].search([('parent_id', '=', False), ('id', 'in', self.partners.ids)])[0]
|
||
|
|
||
|
# check partner and related null instances
|
||
|
self.assertTrue(partner)
|
||
|
self.assertIsRecord(partner, 'res.partner')
|
||
|
|
||
|
self.assertFalse(partner.parent_id)
|
||
|
self.assertIsNull(partner.parent_id, 'res.partner')
|
||
|
|
||
|
self.assertIs(partner.parent_id.id, False)
|
||
|
|
||
|
self.assertFalse(partner.parent_id.user_id)
|
||
|
self.assertIsNull(partner.parent_id.user_id, 'res.users')
|
||
|
|
||
|
self.assertIs(partner.parent_id.user_id.name, False)
|
||
|
|
||
|
self.assertFalse(partner.parent_id.user_id.groups_id)
|
||
|
self.assertIsRecordset(partner.parent_id.user_id.groups_id, 'res.groups')
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_40_new_new(self):
|
||
|
""" Call new-style methods in the new API style. """
|
||
|
partners = self.env['res.partner'].search([('name', 'ilike', 'g'), ('id', 'in', self.partners.ids)])
|
||
|
self.assertTrue(partners)
|
||
|
|
||
|
# call method write on partners itself, and check its effect
|
||
|
partners.write({'active': False})
|
||
|
for p in partners:
|
||
|
self.assertFalse(p.active)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_45_new_new(self):
|
||
|
""" Call new-style methods on records (new API style). """
|
||
|
partners = self.env['res.partner'].search([('name', 'ilike', 'g'), ('id', 'in', self.partners.ids)])
|
||
|
self.assertTrue(partners)
|
||
|
|
||
|
# call method write on partner records, and check its effects
|
||
|
for p in partners:
|
||
|
p.write({'active': False})
|
||
|
for p in partners:
|
||
|
self.assertFalse(p.active)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
@mute_logger('odoo.addons.base.models.ir_model')
|
||
|
def test_50_environment(self):
|
||
|
""" Test environment on records. """
|
||
|
# partners and reachable records are attached to self.env
|
||
|
partners = self.env['res.partner'].search([('name', 'ilike', 'j'), ('id', 'in', self.partners.ids)])
|
||
|
self.assertEqual(partners.env, self.env)
|
||
|
for x in (partners, partners[0], partners[0].company_id):
|
||
|
self.assertEqual(x.env, self.env)
|
||
|
for p in partners:
|
||
|
self.assertEqual(p.env, self.env)
|
||
|
|
||
|
# check that the current user can read and modify company data
|
||
|
partners[0].company_id.name
|
||
|
partners[0].company_id.write({'name': 'Fools'})
|
||
|
|
||
|
# create an environment with a demo user
|
||
|
demo = self.env['res.users'].create({
|
||
|
'name': 'test_environment_demo',
|
||
|
'login': 'test_environment_demo',
|
||
|
'password': 'test_environment_demo',
|
||
|
})
|
||
|
demo_env = self.env(user=demo)
|
||
|
self.assertNotEqual(demo_env, self.env)
|
||
|
|
||
|
# partners and related records are still attached to self.env
|
||
|
self.assertEqual(partners.env, self.env)
|
||
|
for x in (partners, partners[0], partners[0].company_id):
|
||
|
self.assertEqual(x.env, self.env)
|
||
|
for p in partners:
|
||
|
self.assertEqual(p.env, self.env)
|
||
|
|
||
|
# create record instances attached to demo_env
|
||
|
demo_partners = partners.with_user(demo)
|
||
|
self.assertEqual(demo_partners.env, demo_env)
|
||
|
for x in (demo_partners, demo_partners[0], demo_partners[0].company_id):
|
||
|
self.assertEqual(x.env, demo_env)
|
||
|
for p in demo_partners:
|
||
|
self.assertEqual(p.env, demo_env)
|
||
|
|
||
|
# demo user can read but not modify company data
|
||
|
demo_partner = self.env['res.partner'].search([('name', '=', 'Landon Roberts')]).with_user(demo)
|
||
|
self.assertTrue(demo_partner.company_id, 'This partner is supposed to be linked to a company')
|
||
|
demo_partner.company_id.name
|
||
|
with self.assertRaises(AccessError):
|
||
|
demo_partner.company_id.write({'name': 'Pricks'})
|
||
|
|
||
|
# remove demo user from all groups
|
||
|
demo.write({'groups_id': [Command.clear()]})
|
||
|
|
||
|
# demo user can no longer access partner data
|
||
|
with self.assertRaises(AccessError):
|
||
|
demo_partner.company_id.name
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_55_environment_lang(self):
|
||
|
""" Check the record env.lang behavior """
|
||
|
partner = self.partner_demo
|
||
|
self.env['res.lang']._activate_lang('fr_FR')
|
||
|
self.assertEqual(partner.with_context(lang=None).env.lang, None, 'None lang context should have None env.lang')
|
||
|
self.assertEqual(partner.with_context(lang='en_US').env.lang, 'en_US', 'en_US active lang context should have en_US env.lang')
|
||
|
self.assertEqual(partner.with_context(lang='fr_FR').env.lang, 'fr_FR', 'fr_FR active lang context should have fr_FR env.lang')
|
||
|
self.assertEqual(partner.with_context(lang='nl_NL').env.lang, None, 'Inactive lang context lang should have None env.lang')
|
||
|
self.assertEqual(partner.with_context(lang='Dummy').env.lang, None, 'Ilegal lang context should have None env.lang')
|
||
|
|
||
|
def test_56_environment_uid_origin(self):
|
||
|
"""Check the expected behavior of `env.uid_origin`"""
|
||
|
user_demo = self.env.ref('base.user_demo')
|
||
|
user_admin = self.env.ref('base.user_admin')
|
||
|
self.assertEqual(self.env.uid_origin, None)
|
||
|
self.assertEqual(self.env['base'].with_user(user_demo).env.uid_origin, user_demo.id)
|
||
|
self.assertEqual(self.env['base'].with_user(user_demo).with_user(user_admin).env.uid_origin, user_demo.id)
|
||
|
self.assertEqual(self.env['base'].with_user(user_admin).with_user(user_demo).env.uid_origin, user_admin.id)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_60_cache(self):
|
||
|
""" Check the record cache behavior """
|
||
|
Partners = self.env['res.partner']
|
||
|
pids = []
|
||
|
data = {
|
||
|
'partner One': ['Partner One - One', 'Partner One - Two'],
|
||
|
'Partner Two': ['Partner Two - One'],
|
||
|
'Partner Three': ['Partner Three - One'],
|
||
|
}
|
||
|
for p in data:
|
||
|
pids.append(Partners.create({
|
||
|
'name': p,
|
||
|
'child_ids': [Command.create({'name': c}) for c in data[p]],
|
||
|
}).id)
|
||
|
|
||
|
partners = Partners.search([('id', 'in', pids)])
|
||
|
partner1, partner2 = partners[0], partners[1]
|
||
|
children1, children2 = partner1.child_ids, partner2.child_ids
|
||
|
self.assertTrue(children1)
|
||
|
self.assertTrue(children2)
|
||
|
|
||
|
# take a child contact
|
||
|
child = children1[0]
|
||
|
self.assertEqual(child.parent_id, partner1)
|
||
|
self.assertIn(child, partner1.child_ids)
|
||
|
self.assertNotIn(child, partner2.child_ids)
|
||
|
|
||
|
# fetch data in the cache
|
||
|
for p in partners:
|
||
|
p.name, p.company_id.name, p.user_id.name, p.contact_address
|
||
|
self.env.cache.check(self.env)
|
||
|
|
||
|
# change its parent
|
||
|
child.write({'parent_id': partner2.id})
|
||
|
self.env.cache.check(self.env)
|
||
|
|
||
|
# check recordsets
|
||
|
self.assertEqual(child.parent_id, partner2)
|
||
|
self.assertNotIn(child, partner1.child_ids)
|
||
|
self.assertIn(child, partner2.child_ids)
|
||
|
self.assertEqual(set(partner1.child_ids + child), set(children1))
|
||
|
self.assertEqual(set(partner2.child_ids), set(children2 + child))
|
||
|
self.env.cache.check(self.env)
|
||
|
|
||
|
# delete it
|
||
|
child.unlink()
|
||
|
self.env.cache.check(self.env)
|
||
|
|
||
|
# check recordsets
|
||
|
self.assertEqual(set(partner1.child_ids), set(children1) - set([child]))
|
||
|
self.assertEqual(set(partner2.child_ids), set(children2))
|
||
|
self.env.cache.check(self.env)
|
||
|
|
||
|
# convert from the cache format to the write format
|
||
|
partner = partner1
|
||
|
partner.country_id, partner.child_ids
|
||
|
data = partner._convert_to_write(partner._cache)
|
||
|
self.assertEqual(data['country_id'], partner.country_id.id)
|
||
|
self.assertEqual(data['child_ids'], [Command.set(partner.child_ids.ids)])
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_60_prefetch(self):
|
||
|
""" Check the record cache prefetching """
|
||
|
partners = self.env['res.partner'].search([('id', 'in', self.partners.ids)], limit=models.PREFETCH_MAX)
|
||
|
self.assertTrue(len(partners) > 1)
|
||
|
|
||
|
# all the records in partners are ready for prefetching
|
||
|
self.assertItemsEqual(partners.ids, partners._prefetch_ids)
|
||
|
|
||
|
# reading ONE partner should fetch them ALL
|
||
|
for partner in partners:
|
||
|
state = partner.state_id
|
||
|
break
|
||
|
partner_ids_with_field = [partner.id
|
||
|
for partner in partners
|
||
|
if 'state_id' in partner._cache]
|
||
|
self.assertItemsEqual(partner_ids_with_field, partners.ids)
|
||
|
|
||
|
# partners' states are ready for prefetching
|
||
|
state_ids = {
|
||
|
partner._cache['state_id']
|
||
|
for partner in partners
|
||
|
if partner._cache['state_id'] is not None
|
||
|
}
|
||
|
self.assertTrue(len(state_ids) > 1)
|
||
|
self.assertItemsEqual(state_ids, state._prefetch_ids)
|
||
|
|
||
|
# reading ONE partner country should fetch ALL partners' countries
|
||
|
for partner in partners:
|
||
|
if partner.state_id:
|
||
|
partner.state_id.name
|
||
|
break
|
||
|
state_ids_with_field = [st.id for st in partners.state_id if 'name' in st._cache]
|
||
|
self.assertItemsEqual(state_ids_with_field, state_ids)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_60_prefetch_model(self):
|
||
|
""" Check the prefetching model. """
|
||
|
partners = self.env['res.partner'].search([('id', 'in', self.partners.ids)], limit=models.PREFETCH_MAX)
|
||
|
self.assertTrue(partners)
|
||
|
|
||
|
def same_prefetch(a, b):
|
||
|
self.assertEqual(set(a._prefetch_ids), set(b._prefetch_ids))
|
||
|
|
||
|
def diff_prefetch(a, b):
|
||
|
self.assertNotEqual(set(a._prefetch_ids), set(b._prefetch_ids))
|
||
|
|
||
|
# the recordset operations below use different prefetch sets
|
||
|
diff_prefetch(partners, partners.browse())
|
||
|
diff_prefetch(partners, partners[0])
|
||
|
diff_prefetch(partners, partners[:5])
|
||
|
|
||
|
# the recordset operations below share the prefetch set
|
||
|
same_prefetch(partners, partners.browse(partners.ids))
|
||
|
same_prefetch(partners, partners.with_user(self.user_demo))
|
||
|
same_prefetch(partners, partners.with_context(active_test=False))
|
||
|
same_prefetch(partners, partners[:10].with_prefetch(partners._prefetch_ids))
|
||
|
|
||
|
# iteration and relational fields should use the same prefetch set
|
||
|
self.assertEqual(type(partners).country_id.type, 'many2one')
|
||
|
self.assertEqual(type(partners).bank_ids.type, 'one2many')
|
||
|
self.assertEqual(type(partners).category_id.type, 'many2many')
|
||
|
|
||
|
vals0 = {
|
||
|
'name': 'Empty relational fields',
|
||
|
'country_id': False,
|
||
|
'bank_ids': [],
|
||
|
'category_id': [],
|
||
|
}
|
||
|
vals1 = {
|
||
|
'name': 'Non-empty relational fields',
|
||
|
'country_id': self.ref('base.be'),
|
||
|
'bank_ids': [Command.create({'acc_number': 'FOO42'})],
|
||
|
'category_id': [Command.link(self.partner_category.id)],
|
||
|
}
|
||
|
partners = partners.create(vals0) + partners.create(vals1)
|
||
|
for partner in partners:
|
||
|
same_prefetch(partner, partners)
|
||
|
same_prefetch(partner.country_id, partners.country_id)
|
||
|
same_prefetch(partner.bank_ids, partners.bank_ids)
|
||
|
same_prefetch(partner.category_id, partners.category_id)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_60_prefetch_read(self):
|
||
|
""" Check that reading a field computes it on self only. """
|
||
|
Partner = self.env['res.partner']
|
||
|
field = type(Partner).company_type
|
||
|
self.assertTrue(field.compute and not field.store)
|
||
|
|
||
|
partner1 = Partner.create({'name': 'Foo'})
|
||
|
partner2 = Partner.create({'name': 'Bar', 'parent_id': partner1.id})
|
||
|
self.assertEqual(partner1.child_ids, partner2)
|
||
|
|
||
|
# reading partner1 should not prefetch 'company_type' on partner2
|
||
|
self.env.clear()
|
||
|
partner1 = partner1.with_prefetch()
|
||
|
partner1.read(['company_type'])
|
||
|
self.assertIn('company_type', partner1._cache)
|
||
|
self.assertNotIn('company_type', partner2._cache)
|
||
|
|
||
|
# reading partner1 should not prefetch 'company_type' on partner2
|
||
|
self.env.clear()
|
||
|
partner1 = partner1.with_prefetch()
|
||
|
partner1.read(['child_ids', 'company_type'])
|
||
|
self.assertIn('company_type', partner1._cache)
|
||
|
self.assertNotIn('company_type', partner2._cache)
|
||
|
|
||
|
def test_60_reversed(self):
|
||
|
records = self.partners
|
||
|
self.assertGreater(len(records), 1)
|
||
|
|
||
|
# check order
|
||
|
self.assertEqual(list(reversed(records)), list(reversed(list(records))))
|
||
|
|
||
|
first = next(iter(records))
|
||
|
last = next(reversed(records))
|
||
|
self.assertEqual(first, records[0])
|
||
|
self.assertEqual(last, records[-1])
|
||
|
|
||
|
# check prefetching
|
||
|
prefetch_ids = records.ids
|
||
|
reversed_ids = [record.id for record in reversed(records)]
|
||
|
|
||
|
self.assertEqual(list(first._prefetch_ids), prefetch_ids)
|
||
|
self.assertEqual(list(last._prefetch_ids), reversed_ids)
|
||
|
|
||
|
self.assertEqual(list(reversed(first._prefetch_ids)), reversed_ids)
|
||
|
self.assertEqual(list(reversed(last._prefetch_ids)), prefetch_ids)
|
||
|
|
||
|
# check prefetching across many2one field
|
||
|
prefetch_ids = records.state_id.ids
|
||
|
reversed_ids = list(unique(
|
||
|
record.state_id.id
|
||
|
for record in reversed(records)
|
||
|
if record.state_id
|
||
|
))
|
||
|
|
||
|
self.assertEqual(list(first.state_id._prefetch_ids), prefetch_ids)
|
||
|
self.assertEqual(list(last.state_id._prefetch_ids), reversed_ids)
|
||
|
|
||
|
self.assertEqual(list(reversed(first.state_id._prefetch_ids)), reversed_ids)
|
||
|
self.assertEqual(list(reversed(last.state_id._prefetch_ids)), prefetch_ids)
|
||
|
|
||
|
# check prefetching across x2many field
|
||
|
prefetch_ids = records.child_ids.ids
|
||
|
reversed_ids = list(unique(
|
||
|
child.id
|
||
|
for record in reversed(records)
|
||
|
for child in record.child_ids
|
||
|
))
|
||
|
|
||
|
self.assertEqual(list(first.child_ids._prefetch_ids), prefetch_ids)
|
||
|
self.assertEqual(list(last.child_ids._prefetch_ids), reversed_ids)
|
||
|
|
||
|
self.assertEqual(list(reversed(first.child_ids._prefetch_ids)), reversed_ids)
|
||
|
self.assertEqual(list(reversed(last.child_ids._prefetch_ids)), prefetch_ids)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_70_one(self):
|
||
|
""" Check method one(). """
|
||
|
# check with many records
|
||
|
ps = self.env['res.partner'].search([('name', 'ilike', 'a'), ('id', 'in', self.partners.ids)])
|
||
|
self.assertTrue(len(ps) > 1)
|
||
|
with self.assertRaises(ValueError):
|
||
|
ps.ensure_one()
|
||
|
|
||
|
p1 = ps[0]
|
||
|
self.assertEqual(len(p1), 1)
|
||
|
self.assertEqual(p1.ensure_one(), p1)
|
||
|
|
||
|
p0 = self.env['res.partner'].browse()
|
||
|
self.assertEqual(len(p0), 0)
|
||
|
with self.assertRaises(ValueError):
|
||
|
p0.ensure_one()
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_contains(self):
|
||
|
""" Test membership on recordset. """
|
||
|
p1 = self.partners[0]
|
||
|
ps = self.partners
|
||
|
self.assertTrue(p1 in ps)
|
||
|
|
||
|
with self.assertRaisesRegex(TypeError, r"unsupported operand types in: 42 in res\.partner.*"):
|
||
|
42 in ps
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: ir\.ui\.menu.* in res\.partner.*"):
|
||
|
self.env['ir.ui.menu'] in ps
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_lazy_contains(self):
|
||
|
""" Test membership on recordset. """
|
||
|
p1 = lazy(lambda: self.partners[0])
|
||
|
ps = lazy(lambda: self.partners)
|
||
|
self.assertTrue(p1 in ps)
|
||
|
|
||
|
with self.assertRaisesRegex(TypeError, r"unsupported operand types in: 42 in res\.partner.*"):
|
||
|
lazy(lambda: 42) in ps
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: ir\.ui\.menu.* in res\.partner.*"):
|
||
|
lazy(lambda: self.env['ir.ui.menu']) in ps
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_set_operations(self):
|
||
|
""" Check set operations on recordsets. """
|
||
|
pa = self.env['res.partner'].search([('name', 'ilike', 'a'), ('id', 'in', self.partners.ids)])
|
||
|
pb = self.env['res.partner'].search([('name', 'ilike', 'b'), ('id', 'in', self.partners.ids)])
|
||
|
|
||
|
self.assertTrue(pa)
|
||
|
self.assertTrue(pb)
|
||
|
self.assertTrue(set(pa) & set(pb))
|
||
|
|
||
|
concat = pa + pb
|
||
|
self.assertEqual(list(concat), list(pa) + list(pb))
|
||
|
self.assertEqual(len(concat), len(pa) + len(pb))
|
||
|
|
||
|
difference = pa - pb
|
||
|
self.assertEqual(len(difference), len(set(difference)))
|
||
|
self.assertEqual(set(difference), set(pa) - set(pb))
|
||
|
self.assertLessEqual(difference, pa)
|
||
|
|
||
|
intersection = pa & pb
|
||
|
self.assertEqual(len(intersection), len(set(intersection)))
|
||
|
self.assertEqual(set(intersection), set(pa) & set(pb))
|
||
|
self.assertLessEqual(intersection, pa)
|
||
|
self.assertLessEqual(intersection, pb)
|
||
|
|
||
|
union = pa | pb
|
||
|
self.assertEqual(len(union), len(set(union)))
|
||
|
self.assertEqual(set(union), set(pa) | set(pb))
|
||
|
self.assertGreaterEqual(union, pa)
|
||
|
self.assertGreaterEqual(union, pb)
|
||
|
|
||
|
# one cannot mix different models with set operations
|
||
|
ps = pa
|
||
|
ms = self.env['ir.ui.menu'].search([])
|
||
|
self.assertNotEqual(ps._name, ms._name)
|
||
|
self.assertNotEqual(ps, ms)
|
||
|
|
||
|
with self.assertRaisesRegex(TypeError, r"unsupported operand types in: res\.partner.* \+ 'string'"):
|
||
|
ps + 'string'
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* \+ ir\.ui\.menu.*"):
|
||
|
ps + ms
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* - ir\.ui\.menu.*"):
|
||
|
ps - ms
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* & ir\.ui\.menu.*"):
|
||
|
ps & ms
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* \| ir\.ui\.menu.*"):
|
||
|
ps | ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps < ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps <= ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps > ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps >= ms
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_lazy_set_operations(self):
|
||
|
""" Check set operations on recordsets. """
|
||
|
pa = lazy(lambda: self.env['res.partner'].search([('name', 'ilike', 'a'), ('id', 'in', self.partners.ids)]))
|
||
|
pb = lazy(lambda: self.env['res.partner'].search([('name', 'ilike', 'b'), ('id', 'in', self.partners.ids)]))
|
||
|
|
||
|
self.assertTrue(pa)
|
||
|
self.assertTrue(pb)
|
||
|
self.assertTrue(set(pa) & set(pb))
|
||
|
|
||
|
concat = pa + pb
|
||
|
self.assertEqual(list(concat), list(pa) + list(pb))
|
||
|
self.assertEqual(len(concat), len(pa) + len(pb))
|
||
|
|
||
|
difference = pa - pb
|
||
|
self.assertEqual(len(difference), len(set(difference)))
|
||
|
self.assertEqual(set(difference), set(pa) - set(pb))
|
||
|
self.assertLessEqual(difference, pa)
|
||
|
|
||
|
intersection = pa & pb
|
||
|
self.assertEqual(len(intersection), len(set(intersection)))
|
||
|
self.assertEqual(set(intersection), set(pa) & set(pb))
|
||
|
self.assertLessEqual(intersection, pa)
|
||
|
self.assertLessEqual(intersection, pb)
|
||
|
|
||
|
union = pa | pb
|
||
|
self.assertEqual(len(union), len(set(union)))
|
||
|
self.assertEqual(set(union), set(pa) | set(pb))
|
||
|
self.assertGreaterEqual(union, pa)
|
||
|
self.assertGreaterEqual(union, pb)
|
||
|
|
||
|
# one cannot mix different models with set operations
|
||
|
ps = pa
|
||
|
ms = lazy(lambda: self.env['ir.ui.menu'].search([]))
|
||
|
self.assertNotEqual(ps._name, ms._name)
|
||
|
self.assertNotEqual(ps, ms)
|
||
|
|
||
|
with self.assertRaisesRegex(TypeError, r"unsupported operand types in: res\.partner.* \+ 'string'"):
|
||
|
ps + 'string'
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* \+ ir\.ui\.menu.*"):
|
||
|
ps + ms
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* - ir\.ui\.menu.*"):
|
||
|
ps - ms
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* & ir\.ui\.menu.*"):
|
||
|
ps & ms
|
||
|
with self.assertRaisesRegex(TypeError, r"inconsistent models in: res\.partner.* \| ir\.ui\.menu.*"):
|
||
|
ps | ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps < ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps <= ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps > ms
|
||
|
with self.assertRaises(TypeError):
|
||
|
ps >= ms
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_filter(self):
|
||
|
""" Check filter on recordsets. """
|
||
|
ps = self.partners
|
||
|
customers = ps.browse([p.id for p in ps if p.employee])
|
||
|
|
||
|
# filter on a single field
|
||
|
self.assertEqual(ps.filtered(lambda p: p.employee), customers)
|
||
|
self.assertEqual(ps.filtered('employee'), customers)
|
||
|
|
||
|
# filter on a sequence of fields
|
||
|
self.assertEqual(
|
||
|
ps.filtered(lambda p: p.parent_id.employee),
|
||
|
ps.filtered('parent_id.employee')
|
||
|
)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_map(self):
|
||
|
""" Check map on recordsets. """
|
||
|
ps = self.partners
|
||
|
parents = ps.browse()
|
||
|
for p in ps:
|
||
|
parents |= p.parent_id
|
||
|
|
||
|
# map a single field
|
||
|
self.assertEqual(ps.mapped(lambda p: p.parent_id), parents)
|
||
|
self.assertEqual(ps.mapped('parent_id'), parents)
|
||
|
self.assertEqual(ps.parent_id, parents)
|
||
|
|
||
|
# map a sequence of fields
|
||
|
self.assertEqual(
|
||
|
ps.mapped(lambda p: p.parent_id.name),
|
||
|
[p.parent_id.name for p in ps]
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
ps.mapped('parent_id.name'),
|
||
|
[p.name for p in parents]
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
ps.parent_id.mapped('name'),
|
||
|
[p.name for p in parents]
|
||
|
)
|
||
|
|
||
|
# map an empty sequence of fields
|
||
|
self.assertEqual(ps.mapped(''), ps)
|
||
|
|
||
|
@mute_logger('odoo.models')
|
||
|
def test_80_sorted(self):
|
||
|
""" Check sorted on recordsets. """
|
||
|
ps = self.env['res.partner'].search([('id', 'in', self.partners.ids)])
|
||
|
|
||
|
# sort by model order
|
||
|
qs = ps[:len(ps) // 2] + ps[len(ps) // 2:]
|
||
|
self.assertEqual(qs.sorted().ids, ps.ids)
|
||
|
|
||
|
# sort by name, with a function or a field name
|
||
|
by_name_ids = [p.id for p in sorted(ps, key=lambda p: p.name)]
|
||
|
self.assertEqual(ps.sorted(lambda p: p.name).ids, by_name_ids)
|
||
|
self.assertEqual(ps.sorted('name').ids, by_name_ids)
|
||
|
|
||
|
# sort by inverse name, with a field name
|
||
|
by_name_ids = [p.id for p in sorted(ps, key=lambda p: p.name, reverse=True)]
|
||
|
self.assertEqual(ps.sorted('name', reverse=True).ids, by_name_ids)
|
||
|
|
||
|
# sorted doesn't filter out new records but don't sort them either (limitation)
|
||
|
new_p = self.env['res.partner'].new({
|
||
|
'child_ids': [
|
||
|
Command.create({'name': 'z'}),
|
||
|
Command.create({'name': 'a'}),
|
||
|
],
|
||
|
})
|
||
|
self.assertEqual(len(new_p.child_ids.sorted()), 2)
|
||
|
|
||
|
# sorted keeps the _prefetch_ids
|
||
|
partners_with_children = self.env['res.partner'].create([
|
||
|
{
|
||
|
'name': 'required',
|
||
|
'child_ids': [
|
||
|
Command.create({'name': 'z'}),
|
||
|
Command.create({'name': 'a'}),
|
||
|
],
|
||
|
},
|
||
|
{
|
||
|
'name': 'required',
|
||
|
'child_ids': [
|
||
|
Command.create({'name': 'z'}),
|
||
|
Command.create({'name': 'a'}),
|
||
|
],
|
||
|
},
|
||
|
])
|
||
|
partners_with_children.invalidate_model(['name'])
|
||
|
# Only one query to fetch name of children of each partner
|
||
|
with self.assertQueryCount(1):
|
||
|
for partner in partners_with_children:
|
||
|
partner.child_ids.sorted('id').mapped('name')
|
||
|
|
||
|
def test_group_on(self):
|
||
|
p0, p1, p2 = self.env['res.partner'].create([
|
||
|
{'name': "bob", 'function': "guest"},
|
||
|
{'name': "james", 'function': "host"},
|
||
|
{'name': "rhod", 'function': "guest"}
|
||
|
])
|
||
|
pn = self.env['res.partner'].new({'name': 'alex', 'function': "host"})
|
||
|
|
||
|
with self.subTest("Should work with mixes of db and new records"):
|
||
|
self.assertEqual(
|
||
|
(p0 | p1 | p2 | pn).grouped('function'),
|
||
|
{'guest': p0 | p2, 'host': p1 | pn}
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
(p0 | p1 | p2 | pn).grouped(lambda r: len(r.name)),
|
||
|
{3: p0, 4: p2 | pn, 5: p1},
|
||
|
)
|
||
|
|
||
|
with self.subTest("Should allow cross-group prefetching"):
|
||
|
byfn = (p0 | p1 | p2).grouped('function')
|
||
|
self.env.invalidate_all(flush=False)
|
||
|
self.assertFalse(self.env.cache._data, "ensure the cache is empty")
|
||
|
self.assertEqual(byfn['guest'].mapped('name'), ['bob', 'rhod'])
|
||
|
# name should have been prefetched by previous statement (on guest
|
||
|
# group), so should be nothing here
|
||
|
with self.assertQueries([]):
|
||
|
_ = byfn['host'].name
|
||
|
|
||
|
|
||
|
class TestExternalAPI(SavepointCaseWithUserDemo):
|
||
|
|
||
|
def test_call_kw(self):
|
||
|
"""kwargs is not modified by the execution of the call"""
|
||
|
partner = self.env['res.partner'].create({'name': 'MyPartner1'})
|
||
|
args = (partner.ids, ['name'])
|
||
|
kwargs = {'context': {'test': True}}
|
||
|
api.call_kw(self.env['res.partner'], 'read', args, kwargs)
|
||
|
self.assertEqual(kwargs, {'context': {'test': True}})
|