oca-ocb-core/odoo-bringout-oca-ocb-web/web/tests/test_ir_model.py
Ernad Husremovic 2d3ee4855a 19.0 vanilla
2026-03-09 09:30:27 +01:00

304 lines
14 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
from odoo.tests import tagged
from odoo.tests.common import new_test_user
@tagged("post_install", "-at_install")
class IrModelAccessTest(TransactionCase):
@classmethod
def setUpClass(cls):
super(IrModelAccessTest, cls).setUpClass()
cls.env['ir.model.access'].create({
'name': "read",
'model_id': cls.env['ir.model'].search([("model", "=", "res.company")]).id,
'group_id': cls.env.ref("base.group_public").id,
'perm_read': False,
})
cls.env['ir.model.access'].create({
'name': "read",
'model_id': cls.env['ir.model'].search([("model", "=", "res.company")]).id,
'group_id': cls.env.ref("base.group_portal").id,
'perm_read': True,
})
cls.env['ir.model.access'].create({
'name': "read",
'model_id': cls.env['ir.model'].search([("model", "=", "res.company")]).id,
'group_id': cls.env.ref("base.group_user").id,
'perm_read': True,
})
cls.portal_user = new_test_user(
cls.env, login="portalDude", groups="base.group_portal"
)
cls.public_user = new_test_user(
cls.env, login="publicDude", groups="base.group_public"
)
cls.spreadsheet_user = new_test_user(
cls.env, login="spreadsheetDude", groups="base.group_user"
)
def test_display_name_for(self):
# Internal User with access rights can access the business name
result = self.env['ir.model'].with_user(self.spreadsheet_user).display_name_for(["res.company"])
self.assertEqual(result, [{"display_name": "Companies", "model": "res.company"}])
# external user with access rights cannot access business name
result = self.env['ir.model'].with_user(self.portal_user).display_name_for(["res.company"])
self.assertEqual(result, [{"display_name": "res.company", "model": "res.company"}])
# external user without access rights cannot access business name
result = self.env['ir.model'].with_user(self.public_user).display_name_for(["res.company"])
self.assertEqual(result, [{"display_name": "res.company", "model": "res.company"}])
# admin has all rights
result = self.env['ir.model'].display_name_for(["res.company"])
self.assertEqual(result, [{"display_name": "Companies", "model": "res.company"}])
# non existent model yields same result as a lack of access rights
result = self.env['ir.model'].display_name_for(["unexistent"])
self.assertEqual(result, [{"display_name": "unexistent", "model": "unexistent"}])
# non existent model comes after existent model
result = self.env['ir.model'].display_name_for(["res.company", "unexistent"])
self.assertEqual(result, [{"display_name": "Companies", "model": "res.company"}, {"display_name": "unexistent", "model": "unexistent"}])
# transient models
result = self.env['ir.model'].display_name_for(["res.company", "base.language.export"])
self.assertEqual(result, [{"display_name": "Companies", "model": "res.company"}, {"display_name": "base.language.export", "model": "base.language.export"}])
# do not return results for transient models
result = self.env['ir.model'].get_available_models()
result = {values["model"] for values in result}
self.assertIn("res.company", result)
self.assertNotIn("base.language.export", result)
class TestIrModel(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# The test mode is necessary in this case. After each test, we call
# registry.reset_changes(), which opens a new cursor to retrieve custom
# models and fields. A regular cursor would correspond to the state of
# the database before setUpClass(), which is not correct. Instead, a
# test cursor will correspond to the state of the database of cls.cr at
# that point, i.e., before the call to setUp().
cls.registry_enter_test_mode_cls()
# model and records for banana stages
cls.env['ir.model'].create({
'name': 'Banana Ripeness',
'model': 'x_banana_ripeness',
'field_id': [
Command.create({'name': 'x_name', 'ttype': 'char', 'field_description': 'Name'}),
]
})
# stage values are pairs (id, display_name)
cls.ripeness_green = cls.env['x_banana_ripeness'].name_create('Green')
cls.ripeness_okay = cls.env['x_banana_ripeness'].name_create('Okay, I guess?')
cls.ripeness_gone = cls.env['x_banana_ripeness'].name_create('Walked away on its own')
# model and records for bananas
cls.bananas_model = cls.env['ir.model'].create({
'name': 'Bananas',
'model': 'x_bananas',
'field_id': [
Command.create({'name': 'x_name', 'ttype': 'char', 'field_description': 'Name'}),
Command.create({'name': 'x_length', 'ttype': 'float', 'field_description': 'Length'}),
Command.create({'name': 'x_color', 'ttype': 'integer', 'field_description': 'Color'}),
Command.create({'name': 'x_ripeness_id', 'ttype': 'many2one',
'field_description': 'Ripeness', 'relation': 'x_banana_ripeness',
'group_expand': True}),
]
})
# add non-stored field that is not valid in order
cls.env['ir.model.fields'].create({
'name': 'x_is_yellow',
'field_description': 'Is the banana yellow?',
'ttype': 'boolean',
'model_id': cls.bananas_model.id,
'store': False,
'depends': 'x_color',
'compute': "for banana in self:\n banana['x_is_yellow'] = banana.x_color == 9"
})
# default stage is ripeness_green
cls.env['ir.default'].set('x_bananas', 'x_ripeness_id', cls.ripeness_green[0])
cls.env['x_bananas'].create([{
'x_name': 'Banana #1',
'x_length': 3.14159,
'x_color': 9,
}, {
'x_name': 'Banana #2',
'x_length': 0,
'x_color': 6,
}, {
'x_name': 'Banana #3',
'x_length': 10,
'x_color': 6,
}])
def setUp(self):
# this cleanup is necessary after each test, and must be done last
self.addCleanup(self.registry.reset_changes)
super().setUp()
def test_model_order_constraint(self):
"""Check that the order constraint is properly enforced."""
VALID_ORDERS = ['id', 'id desc', 'id asc, x_length', 'x_color, x_length, create_uid']
for order in VALID_ORDERS:
self.bananas_model.order = order
INVALID_ORDERS = ['', 'x_wat', 'id esc', 'create_uid,', 'id, x_is_yellow']
for order in INVALID_ORDERS:
with self.assertRaises(ValidationError):
self.bananas_model.order = order
# check that the constraint is checked at model creation
fields_value = [
Command.create({'name': 'x_name', 'ttype': 'char', 'field_description': 'Name'}),
Command.create({'name': 'x_length', 'ttype': 'float', 'field_description': 'Length'}),
Command.create({'name': 'x_color', 'ttype': 'integer', 'field_description': 'Color'}),
]
self.env['ir.model'].create({
'name': 'MegaBananas',
'model': 'x_mega_bananas',
'order': 'x_name asc, id desc', # valid order
'field_id': fields_value,
})
with self.assertRaises(ValidationError):
self.env['ir.model'].create({
'name': 'GigaBananas',
'model': 'x_giga_bananas',
'order': 'x_name asc, x_wat', # invalid order
'field_id': fields_value,
})
# ensure we can order by a stored field via inherits
user_model = self.env['ir.model'].search([('model', '=', 'res.users')])
user_model._check_order() # must not raise
def test_model_order_search(self):
"""Check that custom orders are applied when querying a model."""
ORDERS = {
'id asc': ['Banana #1', 'Banana #2', 'Banana #3'],
'id desc': ['Banana #3', 'Banana #2', 'Banana #1'],
'x_color asc, id asc': ['Banana #2', 'Banana #3', 'Banana #1'],
'x_color asc, id desc': ['Banana #3', 'Banana #2', 'Banana #1'],
'x_length asc, id': ['Banana #2', 'Banana #1', 'Banana #3'],
}
for order, names in ORDERS.items():
self.bananas_model.order = order
self.assertEqual(self.env['x_bananas']._order, order)
bananas = self.env['x_bananas'].search([])
self.assertEqual(bananas.mapped('x_name'), names, 'failed to order by %s' % order)
def test_model_fold_search(self):
"""Check that custom orders are applied when querying a model."""
self.assertEqual(self.bananas_model.fold_name, False)
self.assertEqual(self.env['x_bananas']._fold_name, None)
self.bananas_model.fold_name = 'x_name'
self.assertEqual(self.env['x_bananas']._fold_name, 'x_name')
def test_group_expansion(self):
"""Check that the basic custom group expansion works."""
model = self.env['x_bananas'].with_context(read_group_expand=True)
groups = model.formatted_read_group([], ['x_ripeness_id'], ['__count'])
expected = [{
'x_ripeness_id': self.ripeness_green,
'__count': 3,
'__extra_domain': [('x_ripeness_id', '=', self.ripeness_green[0])],
}, {
'x_ripeness_id': self.ripeness_okay,
'__count': 0,
'__extra_domain': [('x_ripeness_id', '=', self.ripeness_okay[0])],
}, {
'x_ripeness_id': self.ripeness_gone,
'__count': 0,
'__extra_domain': [('x_ripeness_id', '=', self.ripeness_gone[0])],
}]
self.assertEqual(groups, expected, 'should include 2 empty ripeness stages')
def test_rec_name_deletion(self):
"""Check that deleting 'x_name' does not crash."""
record = self.env['x_bananas'].create({'x_name': "Ifan Ben-Mezd"})
self.assertEqual(record._rec_name, 'x_name')
ClassRecord = self.registry[record._name]
self.assertEqual(self.registry.field_depends[ClassRecord.display_name], ('x_name',))
self.assertEqual(record.display_name, "Ifan Ben-Mezd")
# unlinking x_name should fixup _rec_name and display_name
self.env['ir.model.fields']._get('x_bananas', 'x_name').unlink()
record = self.env['x_bananas'].browse(record.id)
self.assertEqual(record._rec_name, None)
self.assertEqual(self.registry.field_depends[ClassRecord.display_name], ())
self.assertEqual(record.display_name, f"x_bananas,{record.id}")
def test_monetary_currency_field(self):
fields_value = [
Command.create({'name': 'x_monetary', 'ttype': 'monetary', 'field_description': 'Monetary', 'currency_field': 'test'}),
]
with self.assertRaises(ValidationError):
self.env['ir.model'].create({
'name': 'Paper Company Model',
'model': 'x_paper_model',
'field_id': fields_value,
})
fields_value = [
Command.create({'name': 'x_monetary', 'ttype': 'monetary', 'field_description': 'Monetary', 'currency_field': 'x_falsy_currency'}),
Command.create({'name': 'x_falsy_currency', 'ttype': 'one2many', 'field_description': 'Currency', 'relation': 'res.currency'}),
]
with self.assertRaises(ValidationError):
self.env['ir.model'].create({
'name': 'Paper Company Model',
'model': 'x_paper_model',
'field_id': fields_value,
})
fields_value = [
Command.create({'name': 'x_monetary', 'ttype': 'monetary', 'field_description': 'Monetary', 'currency_field': 'x_falsy_currency'}),
Command.create({'name': 'x_falsy_currency', 'ttype': 'many2one', 'field_description': 'Currency', 'relation': 'res.partner'}),
]
with self.assertRaises(ValidationError):
self.env['ir.model'].create({
'name': 'Paper Company Model',
'model': 'x_paper_model',
'field_id': fields_value,
})
fields_value = [
Command.create({'name': 'x_monetary', 'ttype': 'monetary', 'field_description': 'Monetary', 'currency_field': 'x_good_currency'}),
Command.create({'name': 'x_good_currency', 'ttype': 'many2one', 'field_description': 'Currency', 'relation': 'res.currency'}),
]
model = self.env['ir.model'].create({
'name': 'Paper Company Model',
'model': 'x_paper_model',
'field_id': fields_value,
})
monetary_field = model.field_id.search([['name', 'ilike', 'x_monetary']])
self.assertEqual(len(monetary_field), 1,
"Should have the monetary field in the created ir.model")
self.assertEqual(monetary_field.currency_field, "x_good_currency",
"The currency field in monetary should have x_good_currency as name")
def test_invalid_field_domain(self):
"""Ensure assigning an invalid domain raises ValidationError."""
field_ripeness_id = self.env['ir.model.fields']._get('x_bananas', 'x_ripeness_id')
valid_domain = "[('x_name', '=', 'Green')]"
invalid_domain = "[('x_name', '=', Green)]" # Green without quotes
field_ripeness_id.domain = valid_domain
with self.assertRaises(ValidationError) as error:
field_ripeness_id.domain = invalid_domain
self.assertIn('An error occurred while evaluating the domain', str(error.exception))
self.assertEqual(field_ripeness_id.domain, valid_domain)