diff options
| author | Odoo's Mergebot <robodoo@odoo.com> | 2020-12-11 17:37:27 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-11 17:37:27 +0100 |
| commit | d33d7bdf21d8a0743a1fa10b9f248d42a20f7785 (patch) | |
| tree | 7ffb846554da48cc3cf904cee84e3cc57f3c8ae4 | |
| parent | 47079f67f586adf45da15fc98dbc81571f7a9100 (diff) | |
| parent | 4f83be33d4cc53d50081050b3a9dee2892c562a4 (diff) | |
[MERGE] mass_mailing(_sms / _sale), utm: provide various fixes
PURPOSE
Provide various fixes related to mass mailing (sms) and marketing automation
applications.
SPECIFICATIONS
See sub commits for more details.
[FIX] link_tracker: disable create and edit on clicks list view
[FIX] sale: prevent creating quotations from UTM campaign reporting buttons
[REF] mass_mailing: split mailing.trace.report sql to ease inheritance
[FIX] mass_mailing: let subscription_ids won over default_list_ids
[FIX] utm: fix naming of utm campaign card
[FIX] utm: show campaign tags by default by giving them a color
LINKS
Task ID-2300385
COM PR odoo/odoo#59872
ENT PR odoo/enterprise#14031
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
| -rw-r--r-- | addons/link_tracker/views/link_tracker_views.xml | 8 | ||||
| -rw-r--r-- | addons/mass_mailing/i18n/mass_mailing.pot | 2 | ||||
| -rw-r--r-- | addons/mass_mailing/models/mailing_contact.py | 44 | ||||
| -rw-r--r-- | addons/mass_mailing/report/mailing_trace_report.py | 75 | ||||
| -rw-r--r-- | addons/mass_mailing/tests/test_mailing_list.py | 34 | ||||
| -rw-r--r-- | addons/sale/models/utm.py | 6 | ||||
| -rw-r--r-- | addons/utm/i18n/utm.pot | 12 | ||||
| -rw-r--r-- | addons/utm/models/utm.py | 11 | ||||
| -rw-r--r-- | addons/utm/views/utm_campaign_views.xml | 2 |
9 files changed, 161 insertions, 33 deletions
diff --git a/addons/link_tracker/views/link_tracker_views.xml b/addons/link_tracker/views/link_tracker_views.xml index 8999a5de34e..8dff64a7758 100644 --- a/addons/link_tracker/views/link_tracker_views.xml +++ b/addons/link_tracker/views/link_tracker_views.xml @@ -26,7 +26,7 @@ <field name="name">link.tracker.view.form</field> <field name="model">link.tracker</field> <field name="arch" type="xml"> - <form string="Website Link" duplicate="0"> + <form string="Website Link" duplicate="0" create="0"> <sheet> <div class="oe_button_box" name="button_box"> <button type="object" icon="fa-sign-out" name="action_visit_page" @@ -58,7 +58,7 @@ <field name="name">link.tracker.view.tree</field> <field name="model">link.tracker</field> <field name="arch" type="xml"> - <tree string="Links" sample="1"> + <tree string="Links" sample="1" create="0"> <field name="create_date"/> <field name="title"/> <field name="label"/> @@ -115,7 +115,7 @@ <field name="name">link.tracker.click.view.form</field> <field name="model">link.tracker.click</field> <field name="arch" type="xml"> - <form string="Link Click"> + <form string="Link Click" create="0"> <sheet> <group> <field name="link_id"/> @@ -131,7 +131,7 @@ <field name="name">link.tracker.click.view.tree</field> <field name="model">link.tracker.click</field> <field name="arch" type="xml"> - <tree string="Links Clicks"> + <tree string="Links Clicks" create="0"> <field name="link_id"/> <field name="ip"/> <field name="country_id"/> diff --git a/addons/mass_mailing/i18n/mass_mailing.pot b/addons/mass_mailing/i18n/mass_mailing.pot index 9e9a8239571..24ddf9c5a5f 100644 --- a/addons/mass_mailing/i18n/mass_mailing.pot +++ b/addons/mass_mailing/i18n/mass_mailing.pot @@ -257,7 +257,7 @@ msgstr "" #. module: mass_mailing #: model:ir.model.constraint,message:mass_mailing.constraint_mailing_contact_list_rel_unique_contact_list -msgid "A contact cannot be subscribed multiple times to the same list!" +msgid "A mailing contact cannot subscribe to the same mailing list multiple times." msgstr "" #. module: mass_mailing diff --git a/addons/mass_mailing/models/mailing_contact.py b/addons/mass_mailing/models/mailing_contact.py index 6ebd1132820..5d74fd511f9 100644 --- a/addons/mass_mailing/models/mailing_contact.py +++ b/addons/mass_mailing/models/mailing_contact.py @@ -24,7 +24,7 @@ class MassMailingContactListRel(models.Model): _sql_constraints = [ ('unique_contact_list', 'unique (contact_id, list_id)', - 'A contact cannot be subscribed multiple times to the same list!') + 'A mailing contact cannot subscribe to the same mailing list multiple times.') ] @api.model @@ -49,6 +49,18 @@ class MassMailingContact(models.Model): _description = 'Mailing Contact' _order = 'email' + def default_get(self, fields): + """ When coming from a mailing list we may have a default_list_ids context + key. We should use it to create subscription_list_ids default value that + are displayed to the user as list_ids is not displayed on form view. """ + res = super(MassMailingContact, self).default_get(fields) + if 'subscription_list_ids' in fields and not res.get('subscription_list_ids'): + list_ids = self.env.context.get('default_list_ids') + if 'default_list_ids' not in res and list_ids and isinstance(list_ids, (list, tuple)): + res['subscription_list_ids'] = [ + (0, 0, {'list_id': list_id}) for list_id in list_ids] + return res + name = fields.Char() company_name = fields.Char(string='Company Name') title_id = fields.Many2one('res.partner.title', string='Title') @@ -80,6 +92,7 @@ class MassMailingContact(models.Model): return expression.FALSE_DOMAIN if value else expression.TRUE_DOMAIN @api.depends('subscription_list_ids') + @api.depends_context('default_list_ids') def _compute_opt_out(self): if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1: [active_list_id] = self._context['default_list_ids'] @@ -98,6 +111,35 @@ class MassMailingContact(models.Model): name = email return name, email + @api.model_create_multi + def create(self, vals_list): + """ Synchronize default_list_ids (currently used notably for computed + fields) default key with subscription_list_ids given by user when creating + contacts. + + Those two values have the same purpose, adding a list to to the contact + either through a direct write on m2m, either through a write on middle + model subscription. + + This is a bit hackish but is due to default_list_ids key being + used to compute oupt_out field. This should be cleaned in master but here + we simply try to limit issues while keeping current behavior. """ + default_list_ids = self._context.get('default_list_ids') + default_list_ids = default_list_ids if isinstance(default_list_ids, (list, tuple)) else [] + + if default_list_ids: + for vals in vals_list: + current_list_ids = [] + subscription_ids = vals.get('subscription_list_ids') or [] + for subscription in subscription_ids: + if len(subscription) == 3: + current_list_ids.append(subscription[2]['list_id']) + for list_id in set(default_list_ids) - set(current_list_ids): + subscription_ids.append((0, 0, {'list_id': list_id})) + vals['subscription_list_ids'] = subscription_ids + + return super(MassMailingContact, self.with_context(default_list_ids=False)).create(vals_list) + @api.model def name_create(self, name): name, email = self.get_name_email(name) diff --git a/addons/mass_mailing/report/mailing_trace_report.py b/addons/mass_mailing/report/mailing_trace_report.py index 71c39fd389d..02931d629d4 100644 --- a/addons/mass_mailing/report/mailing_trace_report.py +++ b/addons/mass_mailing/report/mailing_trace_report.py @@ -31,26 +31,55 @@ class MailingTraceReport(models.Model): statistics collected for each mailing, and mailing.mailing model that models the various mailing performed. """ tools.drop_view_if_exists(self.env.cr, 'mailing_trace_report') - self.env.cr.execute(""" - CREATE OR REPLACE VIEW mailing_trace_report AS ( - SELECT - min(trace.id) as id, - utm_source.name as name, - mailing.mailing_type, - utm_campaign.name as campaign, - trace.scheduled as scheduled_date, - mailing.state, - mailing.email_from, - count(trace.sent) as sent, - (count(trace.sent) - count(trace.bounced)) as delivered, - count(trace.opened) as opened, - count(trace.replied) as replied, - count(trace.clicked) as clicked, - count(trace.bounced) as bounced - FROM - mailing_trace as trace - left join mailing_mailing as mailing ON (trace.mass_mailing_id=mailing.id) - left join utm_campaign as utm_campaign ON (mailing.campaign_id = utm_campaign.id) - left join utm_source as utm_source ON (mailing.source_id = utm_source.id) - GROUP BY trace.scheduled, utm_source.name, utm_campaign.name, mailing.mailing_type, mailing.state, mailing.email_from - )""") + self.env.cr.execute(self._report_get_request()) + + def _report_get_request(self): + sql_select = 'SELECT %s' % ', '.join(self._report_get_request_select_items()) + sql_from = 'FROM %s' % ' '.join(self._report_get_request_from_items()) + sql_where_items = self._report_get_request_where_items() + if sql_where_items and len(sql_where_items) == 1: + sql_where = 'WHERE %s' % sql_where_items[0] + elif sql_where_items: + sql_where = 'WHERE %s' % ' AND '.join(sql_where_items) + else: + sql_where = '' + sql_group_by = 'GROUP BY %s' % ', '.join(self._report_get_request_group_by_items()) + return f"CREATE OR REPLACE VIEW mailing_trace_report AS ({sql_select} {sql_from} {sql_where} {sql_group_by} )" + + def _report_get_request_select_items(self): + return [ + 'min(trace.id) as id', + 'utm_source.name as name', + 'mailing.mailing_type', + 'utm_campaign.name as campaign', + 'trace.scheduled as scheduled_date', + 'mailing.state', + 'mailing.email_from', + 'count(trace.sent) as sent', + '(count(trace.sent) - count(trace.bounced)) as delivered', + 'count(trace.opened) as opened', + 'count(trace.replied) as replied', + 'count(trace.clicked) as clicked', + 'count(trace.bounced) as bounced' + ] + + def _report_get_request_from_items(self): + return [ + 'mailing_trace as trace', + 'left join mailing_mailing as mailing ON (trace.mass_mailing_id=mailing.id)', + 'left join utm_campaign as utm_campaign ON (mailing.campaign_id = utm_campaign.id)', + 'left join utm_source as utm_source ON (mailing.source_id = utm_source.id)' + ] + + def _report_get_request_where_items(self): + return [] + + def _report_get_request_group_by_items(self): + return [ + 'trace.scheduled', + 'utm_source.name', + 'utm_campaign.name', + 'mailing.mailing_type', + 'mailing.state', + 'mailing.email_from' + ] diff --git a/addons/mass_mailing/tests/test_mailing_list.py b/addons/mass_mailing/tests/test_mailing_list.py index dc029facc43..a9c6b5a2c2e 100644 --- a/addons/mass_mailing/tests/test_mailing_list.py +++ b/addons/mass_mailing/tests/test_mailing_list.py @@ -19,6 +19,40 @@ class TestMailingListMerge(MassMailCommon): ] }) + + @users('user_marketing') + def test_mailing_contact_create(self): + default_list_ids = (self.mailing_list_2 | self.mailing_list_3).ids + + # simply set default list in context + new = self.env['mailing.contact'].with_context(default_list_ids=default_list_ids).create([{ + 'name': 'Contact_%d' % x, + 'email': 'contact_%d@test.example.com' % x, + } for x in range(0, 5)]) + self.assertEqual(new.list_ids, (self.mailing_list_2 | self.mailing_list_3)) + + # default list and subscriptions should be merged + new = self.env['mailing.contact'].with_context(default_list_ids=default_list_ids).create([{ + 'name': 'Contact_%d' % x, + 'email': 'contact_%d@test.example.com' % x, + 'subscription_list_ids': [(0, 0, { + 'list_id': self.mailing_list_1.id, + 'opt_out': True, + }), (0, 0, { + 'list_id': self.mailing_list_2.id, + 'opt_out': True, + })], + } for x in range(0, 5)]) + self.assertEqual(new.list_ids, (self.mailing_list_1 | self.mailing_list_2 | self.mailing_list_3)) + # should correctly take subscription opt_out value + for list_id in (self.mailing_list_1 | self.mailing_list_2).ids: + new = new.with_context(default_list_ids=[list_id]) + self.assertTrue(all(contact.opt_out for contact in new)) + # not opt_out for new subscription without specific create values + for list_id in self.mailing_list_3.ids: + new = new.with_context(default_list_ids=[list_id]) + self.assertFalse(any(contact.opt_out for contact in new)) + @users('user_marketing') def test_mailing_list_merge(self): # TEST CASE: Merge A,B into the existing mailing list C diff --git a/addons/sale/models/utm.py b/addons/sale/models/utm.py index 1eec837aa90..cc31dfdf84b 100644 --- a/addons/sale/models/utm.py +++ b/addons/sale/models/utm.py @@ -48,7 +48,11 @@ class UtmCampaign(models.Model): def action_redirect_to_quotations(self): action = self.env["ir.actions.actions"]._for_xml_id("sale.action_quotations_with_onboarding") action['domain'] = [('campaign_id', '=', self.id)] - action['context'] = {'default_campaign_id': self.id} + action['context'] = { + 'create': False, + 'edit': False, + 'default_campaign_id': self.id + } return action def action_redirect_to_invoiced(self): diff --git a/addons/utm/i18n/utm.pot b/addons/utm/i18n/utm.pot index f125c9d286b..c0392cbe3e8 100644 --- a/addons/utm/i18n/utm.pot +++ b/addons/utm/i18n/utm.pot @@ -222,6 +222,11 @@ msgid "Dropdown menu" msgstr "" #. module: utm +#: model_terms:ir.ui.view,arch_db:utm.utm_campaign_view_kanban +msgid "Edit" +msgstr "" + +#. module: utm #: model:utm.campaign,name:utm.utm_campaign_email_campaign_products msgid "Email Campaign - Products" msgstr "" @@ -550,6 +555,13 @@ msgid "" msgstr "" #. module: utm +#: model:ir.model.fields,help:utm.field_utm_tag__color +msgid "" +"Tag color. No color means no display in kanban to distinguish internal tags " +"from public categorization tags." +msgstr "" + +#. module: utm #: model:ir.model.constraint,message:utm.constraint_utm_tag_name_uniq msgid "Tag name already exists !" msgstr "" diff --git a/addons/utm/models/utm.py b/addons/utm/models/utm.py index 3c197d1814e..0bf712c4be4 100644 --- a/addons/utm/models/utm.py +++ b/addons/utm/models/utm.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from random import randint + from odoo import fields, models, api, SUPERUSER_ID @@ -64,9 +66,14 @@ class UtmTag(models.Model): _description = 'UTM Tag' _order = 'name' + def _default_color(self): + return randint(1, 11) + name = fields.Char(required=True, translate=True) - color = fields.Integer(string='Color Index') + color = fields.Integer( + string='Color Index', default=lambda self: self._default_color(), + help='Tag color. No color means no display in kanban to distinguish internal tags from public categorization tags.') _sql_constraints = [ - ('name_uniq', 'unique (name)', "Tag name already exists !"), + ('name_uniq', 'unique (name)', "Tag name already exists !"), ] diff --git a/addons/utm/views/utm_campaign_views.xml b/addons/utm/views/utm_campaign_views.xml index f9103d21253..84c67499b1c 100644 --- a/addons/utm/views/utm_campaign_views.xml +++ b/addons/utm/views/utm_campaign_views.xml @@ -86,7 +86,7 @@ </a> <div class="dropdown-menu" role="menu"> <t t-if="widget.editable"> - <a role="menuitem" type="edit" class="dropdown-item">Settings</a> + <a role="menuitem" type="edit" class="dropdown-item">Edit</a> </t> <t t-if="widget.deletable"> <a role="menuitem" type="delete" class="dropdown-item">Delete</a> |
