# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import date from psycopg2 import IntegrityError, ProgrammingError import odoo from odoo.exceptions import UserError, ValidationError, AccessError from odoo.tools import mute_logger from odoo.tests import common from odoo.addons.base.tests.common import TransactionCaseWithUserDemo from odoo import Command class TestServerActionsBase(TransactionCaseWithUserDemo): def setUp(self): super(TestServerActionsBase, self).setUp() # Data on which we will run the server action self.test_country = self.env['res.country'].create({ 'name': 'TestingCountry', 'code': 'TY', 'address_format': 'SuperFormat', }) self.test_partner = self.env['res.partner'].create({ 'city': 'OrigCity', 'country_id': self.test_country.id, 'email': 'test.partner@test.example.com', 'name': 'TestingPartner', }) self.context = { 'active_model': 'res.partner', 'active_id': self.test_partner.id, } # Model data Model = self.env['ir.model'] Fields = self.env['ir.model.fields'] self.comment_html = '
MyComment
' self.res_partner_model = Model.search([('model', '=', 'res.partner')]) self.res_partner_name_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'name')]) self.res_partner_city_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'city')]) self.res_partner_country_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'country_id')]) self.res_partner_parent_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'parent_id')]) self.res_partner_children_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'child_ids')]) self.res_partner_category_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'category_id')]) self.res_partner_latitude_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'partner_latitude')]) self.res_country_model = Model.search([('model', '=', 'res.country')]) self.res_country_name_field = Fields.search([('model', '=', 'res.country'), ('name', '=', 'name')]) self.res_country_code_field = Fields.search([('model', '=', 'res.country'), ('name', '=', 'code')]) self.res_partner_category_model = Model.search([('model', '=', 'res.partner.category')]) self.res_partner_category_name_field = Fields.search([('model', '=', 'res.partner.category'), ('name', '=', 'name')]) # create server action to self.action = self.env['ir.actions.server'].create({ 'name': 'TestAction', 'model_id': self.res_partner_model.id, 'model_name': 'res.partner', 'state': 'code', 'code': 'record.write({"comment": "%s"})' % self.comment_html, }) class TestServerActions(TestServerActionsBase): def test_00_action(self): self.action.with_context(self.context).run() self.assertEqual(self.test_partner.comment, self.comment_html, 'ir_actions_server: invalid condition check') self.test_partner.write({'comment': False}) # Do: create contextual action self.action.create_action() self.assertEqual(self.action.binding_model_id.model, 'res.partner') # Do: remove contextual action self.action.unlink_action() self.assertFalse(self.action.binding_model_id) def test_10_code(self): self.action.write({ 'state': 'code', 'code': ("partner_name = record.name + '_code'\n" "record.env['res.partner'].create({'name': partner_name})"), }) run_res = self.action.with_context(self.context).run() self.assertFalse(run_res, 'ir_actions_server: code server action correctly finished should return False') partners = self.test_partner.search([('name', 'ilike', 'TestingPartner_code')]) self.assertEqual(len(partners), 1, 'ir_actions_server: 1 new partner should have been created') def test_20_crud_create(self): # Do: create a new record in another model self.action.write({ 'state': 'object_create', 'crud_model_id': self.res_country_model.id, 'link_field_id': False, 'fields_lines': [Command.clear(), Command.create({'col1': self.res_country_name_field.id, 'value': 'record.name', 'evaluation_type': 'equation'}), Command.create({'col1': self.res_country_code_field.id, 'value': 'record.name[0:2]', 'evaluation_type': 'equation'})], }) run_res = self.action.with_context(self.context).run() self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False') # Test: new country created country = self.test_country.search([('name', 'ilike', 'TestingPartner')]) self.assertEqual(len(country), 1, 'ir_actions_server: TODO') self.assertEqual(country.code, 'TE', 'ir_actions_server: TODO') def test_20_crud_create_link_many2one(self): _city = 'TestCity' _name = 'TestNew' # Do: create a new record in the same model and link it with a many2one self.action.write({ 'state': 'object_create', 'crud_model_id': self.action.model_id.id, 'link_field_id': self.res_partner_parent_field.id, 'fields_lines': [Command.create({'col1': self.res_partner_name_field.id, 'value': _name}), Command.create({'col1': self.res_partner_city_field.id, 'value': _city})], }) run_res = self.action.with_context(self.context).run() self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False') # Test: new partner created partner = self.test_partner.search([('name', 'ilike', _name)]) self.assertEqual(len(partner), 1, 'ir_actions_server: TODO') self.assertEqual(partner.city, _city, 'ir_actions_server: TODO') # Test: new partner linked self.assertEqual(self.test_partner.parent_id, partner, 'ir_actions_server: TODO') def test_20_crud_create_link_one2many(self): _name = 'TestNew' # Do: create a new record in the same model and link it with a one2many self.action.write({ 'state': 'object_create', 'crud_model_id': self.action.model_id.id, 'link_field_id': self.res_partner_children_field.id, 'fields_lines': [Command.create({'col1': self.res_partner_name_field.id, 'value': _name})], }) run_res = self.action.with_context(self.context).run() self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False') # Test: new partner created partner = self.test_partner.search([('name', 'ilike', _name)]) self.assertEqual(len(partner), 1, 'ir_actions_server: TODO') self.assertEqual(partner.name, _name, 'ir_actions_server: TODO') # Test: new partner linked self.assertIn(partner, self.test_partner.child_ids, 'ir_actions_server: TODO') def test_20_crud_create_link_many2many(self): # Do: create a new record in another model self.action.write({ 'state': 'object_create', 'crud_model_id': self.res_partner_category_model.id, 'link_field_id': self.res_partner_category_field.id, 'fields_lines': [Command.create({'col1': self.res_partner_category_name_field.id, 'value': 'record.name', 'evaluation_type': 'equation'})], }) run_res = self.action.with_context(self.context).run() self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False') # Test: new category created category = self.env['res.partner.category'].search([('name', 'ilike', 'TestingPartner')]) self.assertEqual(len(category), 1, 'ir_actions_server: TODO') self.assertIn(category, self.test_partner.category_id) def test_30_crud_write(self): _name = 'TestNew' # Do: update partner name self.action.write({ 'state': 'object_write', 'fields_lines': [Command.create({'col1': self.res_partner_name_field.id, 'value': _name})], }) run_res = self.action.with_context(self.context).run() self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False') # Test: partner updated partner = self.test_partner.search([('name', 'ilike', _name)]) self.assertEqual(len(partner), 1, 'ir_actions_server: TODO') self.assertEqual(partner.city, 'OrigCity', 'ir_actions_server: TODO') @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') def test_40_multi(self): # Data: 2 server actions that will be nested action1 = self.action.create({ 'name': 'Subaction1', 'sequence': 1, 'model_id': self.res_partner_model.id, 'state': 'code', 'code': 'action = {"type": "ir.actions.act_window"}', }) action2 = self.action.create({ 'name': 'Subaction2', 'sequence': 2, 'model_id': self.res_partner_model.id, 'crud_model_id': self.res_partner_model.id, 'state': 'object_create', 'fields_lines': [Command.create({'col1': self.res_partner_name_field.id, 'value': 'RaoulettePoiluchette'}), Command.create({'col1': self.res_partner_city_field.id, 'value': 'TestingCity'})], }) action3 = self.action.create({ 'name': 'Subaction3', 'sequence': 3, 'model_id': self.res_partner_model.id, 'state': 'code', 'code': 'action = {"type": "ir.actions.act_url"}', }) self.action.write({ 'state': 'multi', 'child_ids': [Command.set([action1.id, action2.id, action3.id])], }) # Do: run the action res = self.action.with_context(self.context).run() # Test: new partner created # currently res_partner overrides default['name'] whatever its value partner = self.test_partner.search([('name', 'ilike', 'RaoulettePoiluchette')]) self.assertEqual(len(partner), 1) # Test: action returned self.assertEqual(res.get('type'), 'ir.actions.act_url') # Test loops with self.assertRaises(ValidationError): self.action.write({ 'child_ids': [Command.set([self.action.id])] }) def test_50_groups(self): """ check the action is returned only for groups dedicated to user """ Actions = self.env['ir.actions.actions'] group0 = self.env['res.groups'].create({'name': 'country group'}) self.context = { 'active_model': 'res.country', 'active_id': self.test_country.id, } # Do: update model and group self.action.write({ 'model_id': self.res_country_model.id, 'binding_model_id': self.res_country_model.id, 'groups_id': [Command.link(group0.id)], 'code': 'record.write({"vat_label": "VatFromTest"})', }) # Test: action is not returned bindings = Actions.get_bindings('res.country') self.assertFalse(bindings) with self.assertRaises(AccessError): self.action.with_context(self.context).run() self.assertFalse(self.test_country.vat_label) # add group to the user, and test again self.env.user.write({'groups_id': [Command.link(group0.id)]}) bindings = Actions.get_bindings('res.country') self.assertItemsEqual(bindings.get('action'), self.action.read(['name', 'sequence', 'binding_view_types'])) self.action.with_context(self.context).run() self.assertEqual(self.test_country.vat_label, 'VatFromTest', 'vat label should be changed to VatFromTest') def test_60_sort(self): """ check the actions sorted by sequence """ Actions = self.env['ir.actions.actions'] # Do: update model self.action.write({ 'model_id': self.res_country_model.id, 'binding_model_id': self.res_country_model.id, }) self.action2 = self.action.copy({'name': 'TestAction2', 'sequence': 1}) # Test: action returned by sequence bindings = Actions.get_bindings('res.country') self.assertEqual([vals.get('name') for vals in bindings['action']], ['TestAction2', 'TestAction']) self.assertEqual([vals.get('sequence') for vals in bindings['action']], [1, 5]) def test_70_copy_action(self): # first check that the base case (reset state) works normally r = self.env['ir.actions.todo'].create({ 'action_id': self.action.id, 'state': 'done', }) self.assertEqual(r.state, 'done') self.assertEqual( r.copy().state, 'open', "by default state should be reset by copy" ) # then check that on server action we've changed that self.assertEqual( self.action.copy().state, 'code', "copying a server action should not reset the state" ) def test_80_permission(self): self.action.write({ 'state': 'code', 'code': """record.write({'date': datetime.date.today()})""", }) user_demo = self.user_demo self_demo = self.action.with_user(user_demo.id) # can write on contact partner self.test_partner.type = "contact" self.test_partner.with_user(user_demo.id).check_access_rule("write") self_demo.with_context(self.context).run() self.assertEqual(self.test_partner.date, date.today()) # but can not write on private address self.test_partner.type = "private" with self.assertRaises(AccessError): self.test_partner.with_user(user_demo.id).check_access_rule("write") # nor execute a server action on it with self.assertRaises(AccessError), mute_logger('odoo.addons.base.models.ir_actions'): self_demo.with_context(self.context).run() def test_90_convert_to_float(self): # make sure eval_value convert the value into float for float-type fields self.action.write({ 'state': 'object_write', 'fields_lines': [Command.create({'col1': self.res_partner_latitude_field.id, 'value': '20.99'})], }) line = self.action.fields_lines[0] self.assertEqual(line.eval_value()[line.id], 20.99) class TestCustomFields(common.TransactionCase): MODEL = 'res.partner' COMODEL = 'res.users' def setUp(self): # check that the registry is properly reset fnames = set(self.registry[self.MODEL]._fields) @self.addCleanup def check_registry(): assert set(self.registry[self.MODEL]._fields) == fnames self.addCleanup(self.registry.reset_changes) self.addCleanup(self.registry.clear_caches) super().setUp() def create_field(self, name, *, field_type='char'): """ create a custom field and return it """ model = self.env['ir.model'].search([('model', '=', self.MODEL)]) field = self.env['ir.model.fields'].create({ 'model_id': model.id, 'name': name, 'field_description': name, 'ttype': field_type, }) self.assertIn(name, self.env[self.MODEL]._fields) return field def create_view(self, name): """ create a view with the given field name """ return self.env['ir.ui.view'].create({ 'name': 'yet another view', 'model': self.MODEL, 'arch': '