mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 06:31:59 +02:00
304 lines
14 KiB
Python
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)
|