[REF] mass_mailing: move from onchange / default to stored editable computed fields
PURPOSE Try to move from onchange / default_get to stored editable computed fields. Normally behavior should be the same (computed or set by user), with support in create / write + onchange without additional code. SPECIFICATIONS Update classic fields with onchange to stored editable computed fields. It means their value will come either from manual user input, either computed based on triggers. Purpose is to remove all onchange and default_get when possible. Clean fields definition inconsistencies, like default / required on computed fields. Indeed computed fields should always have a value, maybe coming from user input. They should not have default / required that are attributes for classic fields. LINKS Task ID 2088577 PR #41877 Enterprise PR odoo/enterprise#7278
This commit is contained in:
committed by
Thibault Delavallée
parent
4c76247a88
commit
ffe1d4047d
@@ -40,7 +40,7 @@ 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']
|
||||
# number of periods for tracking mail_mail statistics
|
||||
_period_number = 6
|
||||
_order = 'sent_date DESC'
|
||||
@@ -56,16 +56,6 @@ class MassMailing(models.Model):
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(MassMailing, self).default_get(fields)
|
||||
if 'reply_to_mode' in fields and not 'reply_to_mode' in res and res.get('mailing_model_real'):
|
||||
if res['mailing_model_real'] in ['res.partner', 'mailing.contact']:
|
||||
res['reply_to_mode'] = 'email'
|
||||
else:
|
||||
res['reply_to_mode'] = 'thread'
|
||||
return res
|
||||
|
||||
active = fields.Boolean(default=True, tracking=True)
|
||||
subject = fields.Char('Subject', help='Subject of emails to send', required=True, translate=True)
|
||||
email_from = fields.Char(string='Send From', required=True,
|
||||
@@ -81,7 +71,10 @@ class MassMailing(models.Model):
|
||||
campaign_id = fields.Many2one('utm.campaign', string='UTM Campaign')
|
||||
source_id = fields.Many2one('utm.source', string='Source', required=True, ondelete='cascade',
|
||||
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', help="Delivery method: Email")
|
||||
medium_id = fields.Many2one(
|
||||
'utm.medium', string='Medium',
|
||||
compute='_compute_medium_id', readonly=False, store=True,
|
||||
help="UTM Medium: delivery method (email, sms, ...)")
|
||||
clicks_ratio = fields.Integer(compute="_compute_clicks_ratio", string="Number of Clicks")
|
||||
state = fields.Selection([('draft', 'Draft'), ('in_queue', 'In Queue'), ('sending', 'Sending'), ('done', 'Sent')],
|
||||
string='Status', required=True, tracking=True, copy=False, default='draft', group_expand='_group_expand_states')
|
||||
@@ -89,21 +82,30 @@ class MassMailing(models.Model):
|
||||
user_id = fields.Many2one('res.users', string='Responsible', tracking=True, default=lambda self: self.env.user)
|
||||
# mailing options
|
||||
mailing_type = fields.Selection([('mail', 'Email')], string="Mailing Type", default="mail", required=True)
|
||||
reply_to_mode = fields.Selection(
|
||||
[('thread', 'Recipient Followers'), ('email', 'Specified Email Address')], string='Reply-To Mode', required=True)
|
||||
reply_to = fields.Char(string='Reply To', help='Preferred Reply-To Address',
|
||||
default=lambda self: self.env.user.email_formatted)
|
||||
reply_to_mode = fields.Selection([
|
||||
('thread', 'Recipient Followers'), ('email', 'Specified Email Address')],
|
||||
string='Reply-To Mode', compute='_compute_reply_to_mode',
|
||||
readonly=False, store=True,
|
||||
help='Thread: replies go to target document. Email: replies are routed to a given email.')
|
||||
reply_to = fields.Char(
|
||||
string='Reply To', compute='_compute_reply_to', readonly=False, store=True,
|
||||
help='Preferred Reply-To Address')
|
||||
# recipients
|
||||
mailing_model_real = fields.Char(compute='_compute_model', string='Recipients Real Model', default='mailing.contact', required=True)
|
||||
mailing_model_id = fields.Many2one('ir.model', string='Recipients Model', domain=[('model', 'in', MASS_MAILING_BUSINESS_MODELS)],
|
||||
mailing_model_real = fields.Char(string='Recipients Real Model', compute='_compute_model')
|
||||
mailing_model_id = fields.Many2one(
|
||||
'ir.model', string='Recipients Model', ondelete='cascade', required=True,
|
||||
domain=[('model', 'in', MASS_MAILING_BUSINESS_MODELS)],
|
||||
default=lambda self: self.env.ref('mass_mailing.model_mailing_list').id)
|
||||
mailing_model_name = fields.Char(related='mailing_model_id.model', string='Recipients Model Name', readonly=True, related_sudo=True)
|
||||
mailing_domain = fields.Char(string='Domain', default=[])
|
||||
mailing_model_name = fields.Char(
|
||||
string='Recipients Model Name', related='mailing_model_id.model',
|
||||
readonly=True, related_sudo=True)
|
||||
mailing_domain = fields.Char(
|
||||
string='Domain', compute='_compute_mailing_domain',
|
||||
readonly=False, store=True)
|
||||
mail_server_id = fields.Many2one('ir.mail_server', string='Mail Server',
|
||||
default=_get_default_mail_server_id,
|
||||
help="Use a specific mail server in priority. Otherwise Odoo relies on the first outgoing mail server available (based on their sequencing) as it does for normal mails.")
|
||||
contact_list_ids = fields.Many2many('mailing.list', 'mail_mass_mailing_list_rel',
|
||||
string='Mailing Lists')
|
||||
contact_list_ids = fields.Many2many('mailing.list', 'mail_mass_mailing_list_rel', string='Mailing Lists')
|
||||
contact_ab_pc = fields.Integer(string='A/B Testing percentage',
|
||||
help='Percentage of the contacts that will be mailed. Recipients will be taken randomly.', default=100)
|
||||
unique_ab_testing = fields.Boolean(string='Allow A/B Testing', default=False,
|
||||
@@ -128,7 +130,7 @@ class MassMailing(models.Model):
|
||||
replied_ratio = fields.Integer(compute="_compute_statistics", string='Replied Ratio')
|
||||
bounced_ratio = fields.Integer(compute="_compute_statistics", string='Bounced Ratio')
|
||||
next_departure = fields.Datetime(compute="_compute_next_departure", string='Scheduled date')
|
||||
|
||||
|
||||
def _compute_total(self):
|
||||
for mass_mailing in self:
|
||||
mass_mailing.total = len(mass_mailing.sudo()._get_recipients())
|
||||
@@ -147,11 +149,6 @@ class MassMailing(models.Model):
|
||||
for mass_mailing in self:
|
||||
mass_mailing.clicks_ratio = mapped_data.get(mass_mailing.id, 0)
|
||||
|
||||
@api.depends('mailing_model_id')
|
||||
def _compute_model(self):
|
||||
for record in self:
|
||||
record.mailing_model_real = (record.mailing_model_name != 'mailing.list') and record.mailing_model_name or 'mailing.contact'
|
||||
|
||||
def _compute_statistics(self):
|
||||
""" Compute statistics of the mass mailing """
|
||||
self.env.cr.execute("""
|
||||
@@ -197,30 +194,53 @@ class MassMailing(models.Model):
|
||||
else:
|
||||
mass_mailing.next_departure = cron_time
|
||||
|
||||
@api.onchange('mailing_model_name', 'contact_list_ids')
|
||||
def _onchange_model_and_list(self):
|
||||
mailing_domain = literal_eval(self.mailing_domain) if self.mailing_domain else []
|
||||
if self.mailing_model_name:
|
||||
if mailing_domain:
|
||||
try:
|
||||
self.env[self.mailing_model_name].search(mailing_domain, limit=1)
|
||||
except:
|
||||
mailing_domain = []
|
||||
if not mailing_domain:
|
||||
if self.mailing_model_name == 'mailing.list' and self.contact_list_ids:
|
||||
mailing_domain = [('list_ids', 'in', self.contact_list_ids.ids)]
|
||||
elif 'is_blacklisted' in self.env[self.mailing_model_name]._fields and not self.mailing_domain:
|
||||
mailing_domain = [('is_blacklisted', '=', False)]
|
||||
elif 'opt_out' in self.env[self.mailing_model_name]._fields and not self.mailing_domain:
|
||||
mailing_domain = [('opt_out', '=', False)]
|
||||
else:
|
||||
mailing_domain = []
|
||||
self.mailing_domain = repr(mailing_domain)
|
||||
@api.depends('mailing_type')
|
||||
def _compute_medium_id(self):
|
||||
for mailing in self:
|
||||
if mailing.mailing_type == 'mail' and not mailing.medium_id:
|
||||
mailing.medium_id = self.env.ref('utm.utm_medium_email').id
|
||||
|
||||
@api.onchange('mailing_type')
|
||||
def _onchange_mailing_type(self):
|
||||
if self.mailing_type == 'mail' and not self.medium_id:
|
||||
self.medium_id = self.env.ref('utm.utm_medium_email').id
|
||||
@api.depends('mailing_model_id')
|
||||
def _compute_model(self):
|
||||
for record in self:
|
||||
record.mailing_model_real = (record.mailing_model_name != 'mailing.list') and record.mailing_model_name or 'mailing.contact'
|
||||
|
||||
@api.depends('mailing_model_real')
|
||||
def _compute_reply_to_mode(self):
|
||||
for mailing in self:
|
||||
if mailing.mailing_model_real in ['res.partner', 'mailing.contact']:
|
||||
mailing.reply_to_mode = 'email'
|
||||
else:
|
||||
mailing.reply_to_mode = 'thread'
|
||||
|
||||
@api.depends('reply_to_mode')
|
||||
def _compute_reply_to(self):
|
||||
for mailing in self:
|
||||
if mailing.reply_to_mode == 'email' and not mailing.reply_to:
|
||||
mailing.reply_to = self.env.user.email_formatted
|
||||
elif mailing.reply_to_mode == 'thread':
|
||||
mailing.reply_to = False
|
||||
|
||||
@api.depends('mailing_model_name', 'contact_list_ids')
|
||||
def _compute_mailing_domain(self):
|
||||
for mailing in self:
|
||||
mailing_domain = literal_eval(mailing.mailing_domain) if mailing.mailing_domain else []
|
||||
if mailing.mailing_model_name:
|
||||
if mailing_domain:
|
||||
try:
|
||||
self.env[mailing.mailing_model_name].search(mailing_domain, limit=1)
|
||||
except:
|
||||
mailing_domain = []
|
||||
if not mailing_domain:
|
||||
if mailing.mailing_model_name == 'mailing.list' and mailing.contact_list_ids:
|
||||
mailing_domain = [('list_ids', 'in', mailing.contact_list_ids.ids)]
|
||||
elif 'is_blacklisted' in self.env[mailing.mailing_model_name]._fields and not mailing.mailing_domain:
|
||||
mailing_domain = [('is_blacklisted', '=', False)]
|
||||
elif 'opt_out' in self.env[mailing.mailing_model_name]._fields and not mailing.mailing_domain:
|
||||
mailing_domain = [('opt_out', '=', False)]
|
||||
else:
|
||||
mailing_domain = []
|
||||
mailing.mailing_domain = repr(mailing_domain)
|
||||
|
||||
# ------------------------------------------------------
|
||||
# ORM
|
||||
@@ -232,8 +252,6 @@ class MassMailing(models.Model):
|
||||
values['name'] = "%s %s" % (values['subject'], datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))
|
||||
if values.get('body_html'):
|
||||
values['body_html'] = self._convert_inline_images_to_urls(values['body_html'])
|
||||
if 'medium_id' not in values and values.get('mailing_type', 'mail') == 'mail':
|
||||
values['medium_id'] = self.env.ref('utm.utm_medium_email').id
|
||||
return super(MassMailing, self).create(values)
|
||||
|
||||
def write(self, values):
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
<div name="mailing_model_id_container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-3" >
|
||||
<field name="mailing_model_id" widget="selection" required="True"
|
||||
<field name="mailing_model_id" widget="selection"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('mailing_model_name', '!=', 'mailing.list')]}" class="col-xs-12 col-md-9 pt-1">
|
||||
|
||||
@@ -4,10 +4,11 @@ from odoo import models, api
|
||||
class MassMailingCampaign(models.Model):
|
||||
_inherit = "mailing.mailing"
|
||||
|
||||
@api.onchange('mailing_model_real', 'contact_list_ids')
|
||||
def _onchange_model_and_list(self):
|
||||
@api.depends('mailing_model_real', 'contact_list_ids')
|
||||
def _compute_mailing_domain(self):
|
||||
# TDE FIXME: whuuut ?
|
||||
result = super(MassMailingCampaign, self)._onchange_model_and_list()
|
||||
if self.mailing_model_name == 'event.registration' and self.mailing_domain == '[]':
|
||||
self.mailing_domain = self.env.context.get('default_mailing_domain', '[]')
|
||||
result = super(MassMailingCampaign, self)._compute_mailing_domain()
|
||||
for mailing in self:
|
||||
if mailing.mailing_model_name == 'event.registration' and mailing.mailing_domain == '[]':
|
||||
mailing.mailing_domain = self.env.context.get('default_mailing_domain', '[]')
|
||||
return result
|
||||
|
||||
@@ -22,7 +22,7 @@ class Mailing(models.Model):
|
||||
# mailing options
|
||||
mailing_type = fields.Selection(selection_add=[('sms', 'SMS')])
|
||||
# sms options
|
||||
body_plaintext = fields.Text('SMS Body')
|
||||
body_plaintext = fields.Text('SMS Body', compute='_compute_body_plaintext', store=True, readonly=False)
|
||||
sms_template_id = fields.Many2one('sms.template', string='SMS Template', ondelete='set null')
|
||||
sms_has_insufficient_credit = fields.Boolean(
|
||||
'Insufficient IAP credits', compute='_compute_sms_has_insufficient_credit',
|
||||
@@ -32,17 +32,20 @@ class Mailing(models.Model):
|
||||
# opt_out_link
|
||||
sms_allow_unsubscribe = fields.Boolean('Include opt-out link', default=False)
|
||||
|
||||
@api.onchange('mailing_type')
|
||||
def _onchange_mailing_type(self):
|
||||
if self.mailing_type == 'sms' and (not self.medium_id or self.medium_id == self.env.ref('utm.utm_medium_email')):
|
||||
self.medium_id = self.env.ref('mass_mailing_sms.utm_medium_sms').id
|
||||
elif self.mailing_type == 'mail' and (not self.medium_id or self.medium_id == self.env.ref('mass_mailing_sms.utm_medium_sms')):
|
||||
self.medium_id = self.env.ref('utm.utm_medium_email').id
|
||||
@api.depends('mailing_type')
|
||||
def _compute_medium_id(self):
|
||||
super(Mailing, self)._compute_medium_id()
|
||||
for mailing in self:
|
||||
if mailing.mailing_type == 'sms' and (not mailing.medium_id or mailing.medium_id == self.env.ref('utm.utm_medium_email')):
|
||||
mailing.medium_id = self.env.ref('mass_mailing_sms.utm_medium_sms').id
|
||||
elif mailing.mailing_type == 'mail' and (not mailing.medium_id or mailing.medium_id == self.env.ref('mass_mailing_sms.utm_medium_sms')):
|
||||
mailing.medium_id = self.env.ref('utm.utm_medium_email').id
|
||||
|
||||
@api.onchange('sms_template_id', 'mailing_type')
|
||||
def _onchange_sms_template_id(self):
|
||||
if self.mailing_type == 'sms' and self.sms_template_id:
|
||||
self.body_plaintext = self.sms_template_id.body
|
||||
@api.depends('sms_template_id', 'mailing_type')
|
||||
def _compute_body_plaintext(self):
|
||||
for mailing in self:
|
||||
if mailing.mailing_type == 'sms' and mailing.sms_template_id:
|
||||
mailing.body_plaintext = mailing.sms_template_id.body
|
||||
|
||||
@api.depends('mailing_trace_ids.failure_type')
|
||||
def _compute_sms_has_insufficient_credit(self):
|
||||
@@ -54,19 +57,6 @@ class Mailing(models.Model):
|
||||
for mailing in self:
|
||||
mailing.sms_has_insufficient_credit = mailing in mailing_ids
|
||||
|
||||
# --------------------------------------------------
|
||||
# CRUD
|
||||
# --------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
if values.get('mailing_type') == 'sms':
|
||||
if not values.get('medium_id'):
|
||||
values['medium_id'] = self.env.ref('mass_mailing_sms.utm_medium_sms').id
|
||||
if values.get('sms_template_id') and not values.get('body_plaintext'):
|
||||
values['body_plaintext'] = self.env['sms.template'].browse(values['sms_template_id']).body
|
||||
return super(Mailing, self).create(values)
|
||||
|
||||
# --------------------------------------------------
|
||||
# BUSINESS / VIEWS ACTIONS
|
||||
# --------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user