Rationale ========= Since v8, the `display_name` field is present on all models. By default, `display_name` uses `name_get` which has pretty much the same purpose (return record name used by the web client). Gradually, many (backend) developers (and the ORM: https://github.com/odoo/odoo/commit/6da1c3ac4c036eac289597602976538e243cb939) started using `display_name` (more convenient than `record.name_get()[0][1]`) but it still had the `name_get` override. It becomes more complex than necessary and poeple start to misunderstand the two (and sometimes override both, leading to inconstiencies between `display_name`/`name_get`). To simplify the ORM and the API, we decided to keep only one of them, the `display_name` field: - It is much more convenient from a backend point of view (`record.name_get()[0][1]` vs `record.display_name`) - It is cached during the same transaction (and invalidated if its dependencies change) - It can be overridden like any other compute field (override `_compute_display_name` with any extra dependencies) - `name_get` is replaced by `read(['display_name'])` (API perceptive), which can actually be more efficient (if `display_name`'s depends are correct, the ORM will only fetch the fields it needs instead of every prefetchable field) Changes ======= - Deprecates `name_get` for the v17 and based the method on `display_name` (the opposite of before) - Converts all usage of `name_get` - Overrides of `name_get` are now overrides of `_compute_display_name` - For `res.partner`, rename the field store `display_name` into `complete_name` because `display_name` context-dependent and it makes no sense to have a compute store that is context-dependent. - Previously, it was possible to return multiple names for the same record with `name_get`, but it was tricky and most of the usage of this `name_get` didn't take this into account. The only example of this is the `name_get` of `product.product` (now use `", ".join(<names>)`). Part-of: odoo/odoo#122085
174 lines
7.8 KiB
Python
174 lines
7.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import UserError
|
|
from odoo.osv import expression
|
|
|
|
|
|
class MassMailingContact(models.Model):
|
|
"""Model of a contact. This model is different from the partner model
|
|
because it holds only some basic information: name, email. The purpose is to
|
|
be able to deal with large contact list to email without bloating the partner
|
|
base."""
|
|
_name = 'mailing.contact'
|
|
_inherit = ['mail.thread.blacklist']
|
|
_description = 'Mailing Contact'
|
|
_order = 'email'
|
|
_mailing_enabled = True
|
|
|
|
def default_get(self, fields_list):
|
|
""" 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_list)
|
|
if 'subscription_list_ids' in fields_list 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')
|
|
email = fields.Char('Email')
|
|
list_ids = fields.Many2many(
|
|
'mailing.list', 'mailing_contact_list_rel',
|
|
'contact_id', 'list_id', string='Mailing Lists')
|
|
subscription_list_ids = fields.One2many(
|
|
'mailing.contact.subscription', 'contact_id', string='Subscription Information')
|
|
country_id = fields.Many2one('res.country', string='Country')
|
|
tag_ids = fields.Many2many('res.partner.category', string='Tags')
|
|
opt_out = fields.Boolean(
|
|
'Opt Out',
|
|
compute='_compute_opt_out', search='_search_opt_out',
|
|
help='Opt out flag for a specific mailing list. '
|
|
'This field should not be used in a view without a unique and active mailing list context.')
|
|
|
|
@api.model
|
|
def _search_opt_out(self, operator, value):
|
|
# Assumes operator is '=' or '!=' and value is True or False
|
|
if operator != '=':
|
|
if operator == '!=' and isinstance(value, bool):
|
|
value = not value
|
|
else:
|
|
raise NotImplementedError()
|
|
|
|
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']
|
|
contacts = self.env['mailing.contact.subscription'].search([('list_id', '=', active_list_id)])
|
|
return [('id', 'in', [record.contact_id.id for record in contacts if record.opt_out == value])]
|
|
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']
|
|
for record in self:
|
|
active_subscription_list = record.subscription_list_ids.filtered(lambda l: l.list_id.id == active_list_id)
|
|
record.opt_out = active_subscription_list.opt_out
|
|
else:
|
|
for record in self:
|
|
record.opt_out = False
|
|
|
|
def get_name_email(self, name):
|
|
name, email = self.env['res.partner']._parse_partner_name(name)
|
|
if name and not email:
|
|
email = name
|
|
if email and not name:
|
|
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 []
|
|
|
|
for vals in vals_list:
|
|
if vals.get('list_ids') and vals.get('subscription_list_ids'):
|
|
raise UserError(_('You should give either list_ids, either subscription_list_ids to create new contacts.'))
|
|
|
|
if default_list_ids:
|
|
for vals in vals_list:
|
|
if vals.get('list_ids'):
|
|
continue
|
|
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.returns('self', lambda value: value.id)
|
|
def copy(self, default=None):
|
|
""" Cleans the default_list_ids while duplicating mailing contact in context of
|
|
a mailing list because we already have subscription lists copied over for newly
|
|
created contact, no need to add the ones from default_list_ids again """
|
|
if self.env.context.get('default_list_ids'):
|
|
self = self.with_context(default_list_ids=False)
|
|
return super().copy(default)
|
|
|
|
@api.model
|
|
def name_create(self, name):
|
|
name, email = self.get_name_email(name)
|
|
contact = self.create({'name': name, 'email': email})
|
|
return contact.id, contact.display_name
|
|
|
|
@api.model
|
|
def add_to_list(self, name, list_id):
|
|
name, email = self.get_name_email(name)
|
|
contact = self.create({'name': name, 'email': email, 'list_ids': [(4, list_id)]})
|
|
return contact.id, contact.display_name
|
|
|
|
def _message_get_default_recipients(self):
|
|
return {r.id: {
|
|
'partner_ids': [],
|
|
'email_to': r.email_normalized,
|
|
'email_cc': False}
|
|
for r in self
|
|
}
|
|
|
|
def action_import(self):
|
|
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_contact_import_action")
|
|
context = self.env.context.copy()
|
|
action['context'] = context
|
|
if (not context.get('default_mailing_list_ids') and context.get('from_mailing_list_ids')):
|
|
action['context'].update({
|
|
'default_mailing_list_ids': context.get('from_mailing_list_ids'),
|
|
})
|
|
|
|
return action
|
|
|
|
def action_add_to_mailing_list(self):
|
|
ctx = dict(self.env.context, default_contact_ids=self.ids)
|
|
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_contact_to_list_action")
|
|
action['view_mode'] = 'form'
|
|
action['target'] = 'new'
|
|
action['context'] = ctx
|
|
|
|
return action
|
|
|
|
@api.model
|
|
def get_import_templates(self):
|
|
return [{
|
|
'label': _('Import Template for Mailing List Contacts'),
|
|
'template': '/mass_mailing/static/xls/mailing_contact.xls'
|
|
}]
|