From 4dbcefb5e5b1878e81fe9be9fe48a785f813334f Mon Sep 17 00:00:00 2001 From: std-odoo Date: Tue, 2 Feb 2021 08:11:50 +0000 Subject: [PATCH] [IMP] utm: ensure the uniqueness of the names of the UTM models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Purpose ======= Ensure that the names of the UTMs models (campaign, medium and source) are unique. If not, generate automatically an unique name. The name field is no more translatable; it makes no sense to translate a technical which will be added in the URL, and can even cause issues (the UTM record is not recognized because of the translation). For the campaign, we keep a "translatable" name which is called "title". For the UTM source, use a mixin to generate automatically the name of the source based on the content (_rec_name) of the record. So we remove the override of create / write / copy in the different models and ensure consistency between those models. Task-2245823 closes odoo/odoo#60501 Related: odoo/enterprise#21882 Related: odoo/upgrade#1863 Signed-off-by: Warnon Aurélien (awa) --- .../hr_recruitment/models/hr_recruitment.py | 3 +- addons/mass_mailing/models/mailing.py | 12 +-- .../tests/test_mailing_internals.py | 46 ++++++++++ addons/mass_mailing_sms/data/mailing_demo.xml | 5 +- addons/utm/models/utm_campaign.py | 25 ++++- addons/utm/models/utm_medium.py | 13 ++- addons/utm/models/utm_mixin.py | 92 +++++++++++++++++-- addons/utm/models/utm_source.py | 78 +++++++++++++++- addons/utm/models/utm_stage.py | 2 +- addons/utm/tests/__init__.py | 1 + addons/utm/tests/test_utm.py | 63 +++++++++++++ addons/utm/tests/test_utm_security.py | 12 +-- addons/utm/views/utm_campaign_views.xml | 13 ++- addons/website_crm/tests/test_website_crm.py | 11 ++- 14 files changed, 338 insertions(+), 38 deletions(-) create mode 100644 addons/utm/tests/test_utm.py diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index 8dd4e0ed81a..94bd7da2624 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -21,9 +21,8 @@ AVAILABLE_PRIORITIES = [ class RecruitmentSource(models.Model): _name = "hr.recruitment.source" _description = "Source of Applicants" - _inherits = {"utm.source": "source_id"} + _inherit = ['utm.source.mixin'] - source_id = fields.Many2one('utm.source', "Source", ondelete='restrict', required=True) email = fields.Char(related='alias_id.display_name', string="Email", readonly=True) job_id = fields.Many2one('hr.job', "Job", ondelete='cascade') alias_id = fields.Many2one('mail.alias', "Alias ID") diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 9014169bc47..dec9a648a00 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -30,9 +30,8 @@ class MassMailing(models.Model): A mass mailing is an occurence of sending emails. """ _name = 'mailing.mailing' _description = 'Mass Mailing' - _inherit = ['mail.thread', 'mail.activity.mixin', 'mail.render.mixin'] + _inherit = ['mail.thread', 'mail.activity.mixin', 'mail.render.mixin', 'utm.source.mixin'] _order = 'calendar_date DESC' - _inherits = {'utm.source': 'source_id'} _rec_name = "subject" @api.model @@ -93,8 +92,6 @@ class MassMailing(models.Model): 'mass_mailing_id', 'attachment_id', string='Attachments') keep_archives = fields.Boolean(string='Keep Archives') campaign_id = fields.Many2one('utm.campaign', string='UTM Campaign', index=True, ondelete='set null') - source_id = fields.Many2one('utm.source', string='Source', required=True, ondelete='restrict', - help="This is the link source, e.g. Search Engine, another domain, or name of email list") medium_id = fields.Many2one( 'utm.medium', string='Medium', compute='_compute_medium_id', readonly=False, store=True, ondelete='restrict', @@ -400,11 +397,8 @@ class MassMailing(models.Model): @api.model_create_multi def create(self, vals_list): - now = fields.Datetime.now() ab_testing_cron = self.env.ref('mass_mailing.ir_cron_mass_mailing_ab_testing').sudo() for values in vals_list: - if values.get('subject') and not values.get('name'): - values['name'] = "%s %s" % (values['subject'], now) if values.get('body_html'): values['body_html'] = self._convert_inline_images_to_urls(values['body_html']) if values.get('ab_testing_schedule_datetime'): @@ -451,9 +445,7 @@ class MassMailing(models.Model): @api.returns('self', lambda value: value.id) def copy(self, default=None): self.ensure_one() - default = dict(default or {}, - name=_('%s (copy)', self.name), - contact_list_ids=self.contact_list_ids.ids) + default = dict(default or {}, contact_list_ids=self.contact_list_ids.ids) return super(MassMailing, self).copy(default=default) def _group_expand_states(self, states, domain, order): diff --git a/addons/mass_mailing/tests/test_mailing_internals.py b/addons/mass_mailing/tests/test_mailing_internals.py index 8eedacd9dc4..cb599aa6245 100644 --- a/addons/mass_mailing/tests/test_mailing_internals.py +++ b/addons/mass_mailing/tests/test_mailing_internals.py @@ -5,10 +5,12 @@ from ast import literal_eval from datetime import datetime from freezegun import freeze_time from psycopg2 import IntegrityError +from unittest.mock import patch from odoo.addons.base.tests.test_ir_cron import CronMixinCase from odoo.addons.mass_mailing.tests.common import MassMailCommon from odoo.exceptions import ValidationError +from odoo.sql_db import Cursor from odoo.tests.common import users, Form from odoo.tools import mute_logger @@ -227,6 +229,50 @@ class TestMassMailValues(MassMailCommon): activity.write({'res_id': 0}) activity.flush() + @freeze_time('2022-01-02') + @patch.object(Cursor, 'now', lambda *args, **kwargs: datetime(2022, 1, 2)) + @users('user_marketing') + def test_mailing_unique_name(self): + """Test that the names are generated and unique for each mailing. + + If the name is missing, it's generated from the subject. Then we should ensure + that this generated name is unique. + """ + mailing_0 = self.env['mailing.mailing'].create({'subject': 'First subject'}) + + mailing_1, mailing_2, mailing_3, mailing_4, mailing_5, mailing_6 = self.env['mailing.mailing'].create([{ + 'subject': 'First subject', + }, { + 'subject': 'First subject', + }, { + 'subject': 'First subject', + 'source_id': self.env['utm.source'].create({'name': 'Custom Source'}).id, + }, { + 'subject': 'First subject', + 'name': 'Mailing', + }, { + 'subject': 'Second subject', + 'name': 'Mailing', + }, { + 'subject': 'Second subject', + }]) + + self.assertEqual(mailing_0.name, 'First subject (Mass Mailing created on 2022-01-02)') + self.assertEqual(mailing_1.name, 'First subject (Mass Mailing created on 2022-01-02) [2]') + self.assertEqual(mailing_2.name, 'First subject (Mass Mailing created on 2022-01-02) [3]') + self.assertEqual(mailing_3.name, 'Custom Source') + self.assertEqual(mailing_4.name, 'Mailing') + self.assertEqual(mailing_5.name, 'Mailing [2]') + self.assertEqual(mailing_6.name, 'Second subject (Mass Mailing created on 2022-01-02)') + + mailing_0.subject = 'First subject' + self.assertEqual(mailing_0.name, 'First subject (Mass Mailing created on 2022-01-02) [4]', + msg='The name must have been re-generated') + + mailing_0.name = 'Second subject (Mass Mailing created on 2022-01-02)' + self.assertEqual(mailing_0.name, 'Second subject (Mass Mailing created on 2022-01-02) [2]', + msg='The name must be unique') + class TestMassMailFeatures(MassMailCommon, CronMixinCase): diff --git a/addons/mass_mailing_sms/data/mailing_demo.xml b/addons/mass_mailing_sms/data/mailing_demo.xml index bcb8a008b5e..bb2e0dab9d3 100644 --- a/addons/mass_mailing_sms/data/mailing_demo.xml +++ b/addons/mass_mailing_sms/data/mailing_demo.xml @@ -121,14 +121,17 @@ + + Mailing Extra Promo + - Extra Promo Extra Promo sms done + Extra promotion for you ! diff --git a/addons/utm/models/utm_campaign.py b/addons/utm/models/utm_campaign.py index fd4cc6a01e9..b4548ba4844 100644 --- a/addons/utm/models/utm_campaign.py +++ b/addons/utm/models/utm_campaign.py @@ -7,8 +7,11 @@ from odoo import fields, models, api, SUPERUSER_ID class UtmCampaign(models.Model): _name = 'utm.campaign' _description = 'UTM Campaign' + _rec_name = 'title' - name = fields.Char(string='Campaign Name', required=True, translate=True) + name = fields.Char(string='Campaign Identifier', required=True, compute='_compute_name', + store=True, readonly=False, precompute=True, translate=False) + title = fields.Char(string='Campaign Name', required=True, translate=True) user_id = fields.Many2one( 'res.users', string='Responsible', @@ -24,6 +27,26 @@ class UtmCampaign(models.Model): is_auto_campaign = fields.Boolean(default=False, string="Automatically Generated Campaign", help="Allows us to filter relevant Campaigns") color = fields.Integer(string='Color Index') + _sql_constraints = [ + ('unique_name', 'UNIQUE(name)', 'The name must be unique'), + ] + + @api.depends('title') + def _compute_name(self): + new_names = self.env['utm.mixin']._get_unique_names(self._name, [c.title for c in self]) + for campaign, new_name in zip(self, new_names): + campaign.name = new_name + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if not vals.get('title') and vals.get('name'): + vals['title'] = vals['name'] + new_names = self.env['utm.mixin']._get_unique_names(self._name, [vals.get('name') for vals in vals_list]) + for vals, new_name in zip(vals_list, new_names): + vals['name'] = new_name + return super().create(vals_list) + @api.model def _group_expand_stage_ids(self, stages, domain, order): """Read group customization in order to display all the stages in the diff --git a/addons/utm/models/utm_medium.py b/addons/utm/models/utm_medium.py index 9bcc9e72280..b8df7745778 100644 --- a/addons/utm/models/utm_medium.py +++ b/addons/utm/models/utm_medium.py @@ -11,9 +11,20 @@ class UtmMedium(models.Model): _description = 'UTM Medium' _order = 'name' - name = fields.Char(string='Medium Name', required=True, translate=True) + name = fields.Char(string='Medium Name', required=True, translate=False) active = fields.Boolean(default=True) + _sql_constraints = [ + ('unique_name', 'UNIQUE(name)', 'The name must be unique'), + ] + + @api.model_create_multi + def create(self, vals_list): + new_names = self.env['utm.mixin']._get_unique_names(self._name, [vals.get('name') for vals in vals_list]) + for vals, new_name in zip(vals_list, new_names): + vals['name'] = new_name + return super().create(vals_list) + @api.ondelete(at_uninstall=False) def _unlink_except_utm_medium_email(self): utm_medium_email = self.env.ref('utm.utm_medium_email', raise_if_not_found=False) diff --git a/addons/utm/models/utm_mixin.py b/addons/utm/models/utm_mixin.py index 67b96b1881b..515c244826c 100644 --- a/addons/utm/models/utm_mixin.py +++ b/addons/utm/models/utm_mixin.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import re +from collections import defaultdict + from odoo import api, fields, models from odoo.http import request +from odoo.osv import expression class UtmMixin(models.AbstractModel): @@ -34,14 +38,8 @@ class UtmMixin(models.AbstractModel): value = request.httprequest.cookies.get(cookie_name) # if we receive a string for a many2one, we search/create the id if field.type == 'many2one' and isinstance(value, str) and value: - Model = self.env[field.comodel_name] - records = Model.search([('name', '=', value)], limit=1) - if not records: - if 'is_auto_campaign' in records._fields: - records = Model.create({'name': value, 'is_auto_campaign': True}) - else: - records = Model.create({'name': value}) - value = records.id + record = self._find_or_create_record(field.comodel_name, value) + value = record.id if value: values[field_name] = value return values @@ -59,3 +57,81 @@ class UtmMixin(models.AbstractModel): ('utm_source', 'source_id', 'odoo_utm_source'), ('utm_medium', 'medium_id', 'odoo_utm_medium'), ] + + def _find_or_create_record(self, model_name, name): + """Based on the model name and on the name of the record, retrieve the corresponding record or create it.""" + Model = self.env[model_name] + + record = Model.search([('name', '=', name)], limit=1) + + if not record: + # No record found, create a new one + record_values = {'name': name} + if 'is_auto_campaign' in record._fields: + record_values['is_auto_campaign'] = True + record = Model.create(record_values) + + return record + + @api.model + def _get_unique_names(self, model_name, names): + """Generate unique names for the given model. + + Take a list of names and return for each names, the new names to set + in the same order (with a counter added if needed). + + E.G. + The name "test" already exists in database + Input: ['test', 'test [3]', 'bob', 'test', 'test'] + Output: ['test [2]', 'test [3]', 'bob', 'test [4]', 'test [5]'] + + :param model_name: name of the model for which we will generate unique names + :param names: list of names, we will ensure that each name will be unique + :return: a list of new values for each name, in the same order + """ + def _split_name_and_count(name): + """ + Return the name part and the counter based on the given name. + + e.g. + "Medium" -> "Medium", 1 + "Medium [1234]" -> "Medium", 1234 + """ + name = name or '' + name_counter_re = r'(.*)\s+\[([0-9]+)\]' + match = re.match(name_counter_re, name) + if match: + return match.group(1), int(match.group(2) or '1') + return name, 1 + + # Remove potential counter part in each names + names_without_counter = {_split_name_and_count(name)[0] for name in names} + + # Retrieve existing similar names + seach_domain = expression.OR([[('name', 'ilike', name)] for name in names_without_counter]) + existing_names = {vals['name'] for vals in self.env[model_name].search_read(seach_domain, ['name'])} + + # Count for each names, based on the names list given in argument + # and the record names in database + count_per_names = defaultdict(lambda: 0) + count_per_names.update({ + name: max(( + _split_name_and_count(existing_name)[1] + 1 + for existing_name in existing_names + if existing_name == name or existing_name.startswith(f'{name} [') + ), default=1) + for name in names_without_counter + }) + + result = [] + for name in names: + if not name: + result.append(False) + continue + + name_without_counter = _split_name_and_count(name)[0] + counter = count_per_names[name_without_counter] + result.append(f'{name_without_counter} [{counter}]' if counter > 1 else name) + count_per_names[name_without_counter] += 1 + + return result diff --git a/addons/utm/models/utm_source.py b/addons/utm/models/utm_source.py index b02e2750944..321f935e5ac 100644 --- a/addons/utm/models/utm_source.py +++ b/addons/utm/models/utm_source.py @@ -2,11 +2,85 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models +from odoo import _, api, fields, models, tools class UtmSource(models.Model): _name = 'utm.source' _description = 'UTM Source' - name = fields.Char(string='Source Name', required=True, translate=True) + name = fields.Char(string='Source Name', required=True) + + _sql_constraints = [ + ('unique_name', 'UNIQUE(name)', 'The name must be unique'), + ] + + @api.model_create_multi + def create(self, vals_list): + new_names = self.env['utm.mixin']._get_unique_names(self._name, [vals.get('name') for vals in vals_list]) + for vals, new_name in zip(vals_list, new_names): + vals['name'] = new_name + return super().create(vals_list) + + def _generate_name(self, record, content): + """Generate the UTM source name based on the content of the source.""" + if not content: + return False + + content = content.replace('\n', ' ') + if len(content) >= 24: + content = f'{content[:20]}...' + + create_date = record.create_date or fields.date.today() + create_date = fields.date.strftime(create_date, tools.DEFAULT_SERVER_DATE_FORMAT) + model_description = self.env['ir.model']._get(record._name).name + return _( + '%(content)s (%(model_description)s created on %(create_date)s)', + content=content, model_description=model_description, create_date=create_date, + ) + + +class UtmSourceMixin(models.AbstractModel): + """Mixin responsible of generating the name of the source based on the content + (field defined by _rec_name) of the record (mailing, social post,...). + """ + _name = 'utm.source.mixin' + _description = 'UTM Source Mixin' + + name = fields.Char('Name', related='source_id.name', readonly=False) + source_id = fields.Many2one('utm.source', string='Source', required=True, ondelete='restrict', copy=False) + + @api.model_create_multi + def create(self, vals_list): + """Create the UTM sources if necessary, generate the name based on the content in batch.""" + # Create all required + utm_sources = self.env['utm.source'].create([ + {'name': values.get('name') or self.env['utm.source']._generate_name(self, values.get(self._rec_name))} + for values in vals_list + if not values.get('source_id') + ]) + + # Update "vals_list" to add the ID of the newly created source + vals_list_missing_source = [values for values in vals_list if not values.get('source_id')] + for values, source in zip(vals_list_missing_source, utm_sources): + values['source_id'] = source.id + + for values in vals_list: + if 'name' in values: + del values['name'] + + return super().create(vals_list) + + def write(self, values): + if values.get(self._rec_name) and not values.get('name'): + values['name'] = self.env['utm.source']._generate_name(self, values[self._rec_name]) + if values.get('name'): + values['name'] = self.env['utm.mixin']._get_unique_names(self._name, [values['name']])[0] + + super().write(values) + + def copy(self, default=None): + """Increment the counter when duplicating the source.""" + default = default or {} + default['name'] = self.env['utm.mixin']._get_unique_names(self._name, [self.name])[0] + return super().copy(default) diff --git a/addons/utm/models/utm_stage.py b/addons/utm/models/utm_stage.py index 4d662107405..100018c7c71 100644 --- a/addons/utm/models/utm_stage.py +++ b/addons/utm/models/utm_stage.py @@ -13,4 +13,4 @@ class UtmStage(models.Model): _order = 'sequence' name = fields.Char(required=True, translate=True) - sequence = fields.Integer() + sequence = fields.Integer(default=1) diff --git a/addons/utm/tests/__init__.py b/addons/utm/tests/__init__.py index bef2cca0769..270036bf8af 100644 --- a/addons/utm/tests/__init__.py +++ b/addons/utm/tests/__init__.py @@ -3,5 +3,6 @@ from . import common from . import test_routes +from . import test_utm from . import test_utm_consistency from . import test_utm_security diff --git a/addons/utm/tests/test_utm.py b/addons/utm/tests/test_utm.py new file mode 100644 index 00000000000..71d9c5505e9 --- /dev/null +++ b/addons/utm/tests/test_utm.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.utm.tests.common import TestUTMCommon + + +class TestUtm(TestUTMCommon): + def test_find_or_create_record(self): + source_1, source_2 = self.env['utm.source'].create([{ + 'name': 'Source 1', + }, { + 'name': 'Source 2', + }]) + + # Find the record based on the given name + source = self.env['utm.mixin']._find_or_create_record('utm.source', 'Source 1') + self.assertEqual(source, source_1) + + # Create a new record + source_4 = self.env['utm.mixin']._find_or_create_record('utm.source', 'Source 3') + self.assertNotIn(source_4, source_1 | source_2) + self.assertEqual(source_4.name, 'Source 3') + + def test_name_generation(self): + """Test that the name is always unique. + + A counter must be added at the end of the name if it's not the case. + """ + for utm_model in ('utm.source', 'utm.medium', 'utm.campaign'): + utm_0 = self.env[utm_model].create({'name': 'UTM dup'}) + + utm_1, utm_2, utm_3, utm_4, utm_5 = self.env[utm_model].create([{ + 'name': 'UTM 1', + }, { + 'name': 'UTM 2', + }, { + 'name': 'UTM dup', + }, { + # UTM record 4 has the same name of the previous UTM record + 'name': 'UTM dup', + }, { + # UTM record 5 has the same name of the previous UTM record + # but with a wrong counter part, it will be removed and updated + 'name': 'UTM dup [0]', + }]) + + self.assertEqual(utm_0.name, 'UTM dup', msg='The first "UTM dup" should be left unchanged since it is unique') + self.assertEqual(utm_1.name, 'UTM 1', msg='This name is already unique') + self.assertEqual(utm_2.name, 'UTM 2', msg='This name is already unique') + self.assertEqual(utm_3.name, 'UTM dup [2]', msg='Must add a counter as suffix to ensure uniqueness') + self.assertEqual(utm_4.name, 'UTM dup [3]', msg='Must add a counter as suffix to ensure uniqueness') + self.assertEqual(utm_5.name, 'UTM dup [4]', msg='Must add a counter as suffix to ensure uniqueness') + + (utm_0 | utm_3 | utm_4).unlink() + + utm_6 = self.env[utm_model].create({'name': 'UTM dup'}) + self.assertEqual(utm_6.name, 'UTM dup [5]') + + utm_7 = self.env[utm_model].create({'name': 'UTM d'}) + self.assertEqual(utm_7.name, 'UTM d', msg='Even if this name has the same prefix as the other, it is still unique') + + utm_8 = utm_7.copy() + self.assertEqual(utm_8.name, 'UTM d [2]', msg='Must add a counter as suffix to ensure uniqueness') diff --git a/addons/utm/tests/test_utm_security.py b/addons/utm/tests/test_utm_security.py index 1f6960ad599..05c4fe4f2c3 100644 --- a/addons/utm/tests/test_utm_security.py +++ b/addons/utm/tests/test_utm_security.py @@ -17,9 +17,9 @@ class TestUTMSecurity(TestUTMCommon): UtmSource = self.env['utm.source'] # CREATE - test_utm_campaign = UtmCampaign.create({'name': 'Test Campaign'}) - test_utm_medium = UtmMedium.create({'name': 'Test Medium'}) - test_utm_source = UtmSource.create({'name': 'Test Source'}) + test_utm_campaign = UtmCampaign.create({'name': 'Campaign ACLs'}) + test_utm_medium = UtmMedium.create({'name': 'Medium ACLs'}) + test_utm_source = UtmSource.create({'name': 'Source ACLs'}) # READ self.assertEqual( @@ -50,9 +50,9 @@ class TestUTMSecurity(TestUTMCommon): UtmSource = self.env['utm.source'] # CREATE - test_utm_campaign = UtmCampaign.create({'name': 'Test Campaign'}) - test_utm_medium = UtmMedium.create({'name': 'Test Medium'}) - test_utm_source = UtmSource.create({'name': 'Test Source'}) + test_utm_campaign = UtmCampaign.create({'name': 'Campaign ACLs'}) + test_utm_medium = UtmMedium.create({'name': 'Medium ACLs'}) + test_utm_source = UtmSource.create({'name': 'Source ACLs'}) # READ self.assertEqual( diff --git a/addons/utm/views/utm_campaign_views.xml b/addons/utm/views/utm_campaign_views.xml index 6c2f58d78e5..33a2b6e4a5b 100644 --- a/addons/utm/views/utm_campaign_views.xml +++ b/addons/utm/views/utm_campaign_views.xml @@ -5,7 +5,7 @@ utm.campaign - + @@ -31,7 +31,8 @@
- + + @@ -47,7 +48,8 @@ utm.campaign - + + @@ -62,7 +64,8 @@
- + + @@ -99,7 +102,7 @@
-

+

diff --git a/addons/website_crm/tests/test_website_crm.py b/addons/website_crm/tests/test_website_crm.py index 287d0aaa8b8..feca6d48603 100644 --- a/addons/website_crm/tests/test_website_crm.py +++ b/addons/website_crm/tests/test_website_crm.py @@ -8,9 +8,12 @@ import odoo.tests class TestWebsiteCrm(odoo.tests.HttpCase): def test_tour(self): + all_utm_campaign = self.env['utm.campaign'].search([]) + utm_medium = self.env['utm.medium'].create({'name': 'Medium'}) + utm_source = self.env['utm.source'].create({'name': 'Source'}) # change action to create opportunity self.start_tour("/", 'website_crm_pre_tour', login='admin') - self.start_tour("/", 'website_crm_tour') + self.start_tour("/?utm_source=Source&utm_medium=Medium&utm_campaign=New campaign", 'website_crm_tour') # check result record = self.env['crm.lead'].search([('description', '=', '### TOUR DATA ###')]) @@ -19,6 +22,12 @@ class TestWebsiteCrm(odoo.tests.HttpCase): self.assertEqual(record.email_from, 'john@smith.com') self.assertEqual(record.partner_name, 'Odoo S.A.') + # check UTM records + self.assertEqual(record.source_id, utm_source) + self.assertEqual(record.medium_id, utm_medium) + self.assertNotIn(record.campaign_id, all_utm_campaign, 'Should have created a new campaign') + self.assertEqual(record.campaign_id.name, 'New campaign', 'Name of the "on the fly" created campaign is wrong') + def test_catch_logged_partner_info_tour(self): user_login = 'admin' user_partner = self.env['res.users'].search([('login', '=', user_login)]).partner_id