512 lines
19 KiB
Python
512 lines
19 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
import ast
|
||
|
|
||
|
from odoo import api, fields, models, _
|
||
|
from odoo.tools import email_normalize
|
||
|
|
||
|
|
||
|
class MailTestSimple(models.Model):
|
||
|
""" A very simple model only inheriting from mail.thread when only
|
||
|
communication history is necessary. """
|
||
|
_description = 'Simple Chatter Model'
|
||
|
_name = 'mail.test.simple'
|
||
|
_inherit = ['mail.thread']
|
||
|
|
||
|
name = fields.Char()
|
||
|
email_from = fields.Char()
|
||
|
|
||
|
def _message_compute_subject(self):
|
||
|
""" To ease mocks """
|
||
|
_a = super()._message_compute_subject()
|
||
|
return _a
|
||
|
|
||
|
def _notify_by_email_get_final_mail_values(self, *args, **kwargs):
|
||
|
""" To ease mocks """
|
||
|
_a = super()._notify_by_email_get_final_mail_values(*args, **kwargs)
|
||
|
return _a
|
||
|
|
||
|
def _notify_by_email_get_headers(self, headers=None):
|
||
|
headers = super()._notify_by_email_get_headers(headers=headers)
|
||
|
headers['X-Custom'] = 'Done'
|
||
|
return headers
|
||
|
|
||
|
class MailTestSimpleUnnamed(models.Model):
|
||
|
""" A very simple model only inheriting from mail.thread when only
|
||
|
communication history is necessary, and has no 'name' field """
|
||
|
_description = 'Simple Chatter Model without "name" field'
|
||
|
_name = 'mail.test.simple.unnamed'
|
||
|
_inherit = ['mail.thread']
|
||
|
_rec_name = "description"
|
||
|
|
||
|
description = fields.Char()
|
||
|
|
||
|
class MailTestSimpleWithMainAttachment(models.Model):
|
||
|
_description = 'Simple Chatter Model With Main Attachment Management'
|
||
|
_name = 'mail.test.simple.main.attachment'
|
||
|
_inherit = ['mail.test.simple', 'mail.thread.main.attachment']
|
||
|
|
||
|
|
||
|
class MailTestSimpleUnfollow(models.Model):
|
||
|
""" A very simple model only inheriting from mail.thread when only
|
||
|
communication history is necessary with unfollow link enabled in
|
||
|
notification emails even for non-internal user. """
|
||
|
_description = 'Simple Chatter Model'
|
||
|
_name = 'mail.test.simple.unfollow'
|
||
|
_inherit = ['mail.thread']
|
||
|
_partner_unfollow_enabled = True
|
||
|
|
||
|
name = fields.Char()
|
||
|
company_id = fields.Many2one('res.company')
|
||
|
email_from = fields.Char()
|
||
|
|
||
|
|
||
|
class MailTestAliasOptional(models.Model):
|
||
|
""" A chatter model inheriting from the alias mixin using optional alias_id
|
||
|
field, hence no inherits. """
|
||
|
_description = 'Chatter Model using Optional Alias Mixin'
|
||
|
_name = 'mail.test.alias.optional'
|
||
|
_inherit = ['mail.alias.mixin.optional']
|
||
|
|
||
|
name = fields.Char()
|
||
|
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
|
||
|
email_from = fields.Char()
|
||
|
|
||
|
def _alias_get_creation_values(self):
|
||
|
""" Updates itself """
|
||
|
values = super()._alias_get_creation_values()
|
||
|
values['alias_model_id'] = self.env['ir.model']._get_id('mail.test.alias.optional')
|
||
|
if self.id:
|
||
|
values['alias_force_thread_id'] = self.id
|
||
|
values['alias_defaults'] = {'company_id': self.company_id.id}
|
||
|
return values
|
||
|
|
||
|
|
||
|
class MailTestGateway(models.Model):
|
||
|
""" A very simple model only inheriting from mail.thread to test pure mass
|
||
|
mailing features and base performances. """
|
||
|
_description = 'Simple Chatter Model for Mail Gateway'
|
||
|
_name = 'mail.test.gateway'
|
||
|
_inherit = ['mail.thread.blacklist']
|
||
|
_primary_email = 'email_from'
|
||
|
|
||
|
name = fields.Char()
|
||
|
email_from = fields.Char()
|
||
|
custom_field = fields.Char()
|
||
|
|
||
|
@api.model
|
||
|
def message_new(self, msg_dict, custom_values=None):
|
||
|
""" Check override of 'message_new' allowing to update record values
|
||
|
base on incoming email. """
|
||
|
defaults = {
|
||
|
'email_from': msg_dict.get('from'),
|
||
|
}
|
||
|
defaults.update(custom_values or {})
|
||
|
return super().message_new(msg_dict, custom_values=defaults)
|
||
|
|
||
|
|
||
|
class MailTestGatewayCompany(models.Model):
|
||
|
""" A very simple model only inheriting from mail.thread to test pure mass
|
||
|
mailing features and base performances, with a company field. """
|
||
|
_description = 'Simple Chatter Model for Mail Gateway with company'
|
||
|
_name = 'mail.test.gateway.company'
|
||
|
_inherit = ['mail.test.gateway']
|
||
|
|
||
|
company_id = fields.Many2one('res.company', 'Company')
|
||
|
|
||
|
|
||
|
class MailTestGatewayMainAttachment(models.Model):
|
||
|
""" A very simple model only inheriting from mail.thread to test pure mass
|
||
|
mailing features and base performances, with a company field and main
|
||
|
attachment management. """
|
||
|
_description = 'Simple Chatter Model for Mail Gateway with company'
|
||
|
_name = 'mail.test.gateway.main.attachment'
|
||
|
_inherit = ['mail.test.gateway', 'mail.thread.main.attachment']
|
||
|
|
||
|
company_id = fields.Many2one('res.company', 'Company')
|
||
|
|
||
|
|
||
|
class MailTestGatewayGroups(models.Model):
|
||
|
""" A model looking like discussion channels / groups (flat thread and
|
||
|
alias). Used notably for advanced gatewxay tests. """
|
||
|
_description = 'Channel/Group-like Chatter Model for Mail Gateway'
|
||
|
_name = 'mail.test.gateway.groups'
|
||
|
_inherit = ['mail.thread.blacklist', 'mail.alias.mixin']
|
||
|
_mail_flat_thread = False
|
||
|
_primary_email = 'email_from'
|
||
|
|
||
|
name = fields.Char()
|
||
|
email_from = fields.Char()
|
||
|
custom_field = fields.Char()
|
||
|
customer_id = fields.Many2one('res.partner', 'Customer')
|
||
|
|
||
|
def _alias_get_creation_values(self):
|
||
|
values = super(MailTestGatewayGroups, self)._alias_get_creation_values()
|
||
|
values['alias_model_id'] = self.env['ir.model']._get('mail.test.gateway.groups').id
|
||
|
if self.id:
|
||
|
values['alias_force_thread_id'] = self.id
|
||
|
values['alias_parent_thread_id'] = self.id
|
||
|
return values
|
||
|
|
||
|
def _mail_get_partner_fields(self, introspect_fields=False):
|
||
|
return ['customer_id']
|
||
|
|
||
|
def _message_get_default_recipients(self):
|
||
|
return dict(
|
||
|
(record.id, {
|
||
|
'email_cc': False,
|
||
|
'email_to': record.email_from if not record.customer_id.ids else False,
|
||
|
'partner_ids': record.customer_id.ids,
|
||
|
})
|
||
|
for record in self
|
||
|
)
|
||
|
|
||
|
|
||
|
class MailTestStandard(models.Model):
|
||
|
""" This model can be used in tests when automatic subscription and simple
|
||
|
tracking is necessary. Most features are present in a simple way. """
|
||
|
_description = 'Standard Chatter Model'
|
||
|
_name = 'mail.test.track'
|
||
|
_inherit = ['mail.thread']
|
||
|
|
||
|
name = fields.Char()
|
||
|
email_from = fields.Char()
|
||
|
user_id = fields.Many2one('res.users', 'Responsible', tracking=True)
|
||
|
container_id = fields.Many2one('mail.test.container', tracking=True)
|
||
|
company_id = fields.Many2one('res.company')
|
||
|
track_fields_tofilter = fields.Char() # comma-separated list of field names
|
||
|
track_enable_default_log = fields.Boolean(default=False)
|
||
|
|
||
|
def _track_filter_for_display(self, tracking_values):
|
||
|
values = super()._track_filter_for_display(tracking_values)
|
||
|
filtered_fields = set(self.track_fields_tofilter.split(',') if self.track_fields_tofilter else '')
|
||
|
return values.filtered(lambda val: val.field_id.name not in filtered_fields)
|
||
|
|
||
|
def _track_get_default_log_message(self, changes):
|
||
|
filtered_fields = set(self.track_fields_tofilter.split(',') if self.track_fields_tofilter else '')
|
||
|
if self.track_enable_default_log and not all(change in filtered_fields for change in changes):
|
||
|
return f'There was a change on {self.name} for fields "{",".join(changes)}"'
|
||
|
return super()._track_get_default_log_message(changes)
|
||
|
|
||
|
class MailTestActivity(models.Model):
|
||
|
""" This model can be used to test activities in addition to simple chatter
|
||
|
features. """
|
||
|
_description = 'Activity Model'
|
||
|
_name = 'mail.test.activity'
|
||
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||
|
|
||
|
name = fields.Char()
|
||
|
date = fields.Date()
|
||
|
email_from = fields.Char()
|
||
|
active = fields.Boolean(default=True)
|
||
|
|
||
|
def action_start(self, action_summary):
|
||
|
return self.activity_schedule(
|
||
|
'test_mail.mail_act_test_todo',
|
||
|
summary=action_summary
|
||
|
)
|
||
|
|
||
|
def action_close(self, action_feedback, attachment_ids=None):
|
||
|
self.activity_feedback(['test_mail.mail_act_test_todo'],
|
||
|
feedback=action_feedback,
|
||
|
attachment_ids=attachment_ids)
|
||
|
|
||
|
|
||
|
class MailTestTicket(models.Model):
|
||
|
""" This model can be used in tests when complex chatter features are
|
||
|
required like modeling tasks or tickets. """
|
||
|
_description = 'Ticket-like model'
|
||
|
_name = 'mail.test.ticket'
|
||
|
_inherit = ['mail.thread']
|
||
|
_primary_email = 'email_from'
|
||
|
|
||
|
name = fields.Char()
|
||
|
email_from = fields.Char(tracking=True)
|
||
|
mobile_number = fields.Char()
|
||
|
phone_number = fields.Char()
|
||
|
count = fields.Integer(default=1)
|
||
|
datetime = fields.Datetime(default=fields.Datetime.now)
|
||
|
mail_template = fields.Many2one('mail.template', 'Template')
|
||
|
customer_id = fields.Many2one('res.partner', 'Customer', tracking=2)
|
||
|
user_id = fields.Many2one('res.users', 'Responsible', tracking=1)
|
||
|
container_id = fields.Many2one('mail.test.container', tracking=True)
|
||
|
|
||
|
def _mail_get_partner_fields(self, introspect_fields=False):
|
||
|
return ['customer_id']
|
||
|
|
||
|
def _message_compute_subject(self):
|
||
|
self.ensure_one()
|
||
|
return f"Ticket for {self.name} on {self.datetime.strftime('%m/%d/%Y, %H:%M:%S')}"
|
||
|
|
||
|
def _message_get_default_recipients(self):
|
||
|
return dict(
|
||
|
(record.id, {
|
||
|
'email_cc': False,
|
||
|
'email_to': record.email_from if not record.customer_id.ids else False,
|
||
|
'partner_ids': record.customer_id.ids,
|
||
|
})
|
||
|
for record in self
|
||
|
)
|
||
|
|
||
|
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||
|
""" Activate more groups to test query counters notably (and be backward
|
||
|
compatible for tests). """
|
||
|
local_msg_vals = dict(msg_vals or {})
|
||
|
groups = super()._notify_get_recipients_groups(
|
||
|
message, model_description, msg_vals=msg_vals
|
||
|
)
|
||
|
for group_name, _group_method, group_data in groups:
|
||
|
if group_name == 'portal':
|
||
|
group_data['active'] = True
|
||
|
elif group_name == 'customer':
|
||
|
group_data['active'] = True
|
||
|
group_data['has_button_access'] = True
|
||
|
group_data['actions'] = [{
|
||
|
'url': self._notify_get_action_link(
|
||
|
'controller',
|
||
|
controller='/test_mail/do_stuff',
|
||
|
**local_msg_vals
|
||
|
),
|
||
|
'title': _('NotificationButtonTitle')
|
||
|
}]
|
||
|
|
||
|
return groups
|
||
|
|
||
|
def _track_template(self, changes):
|
||
|
res = super(MailTestTicket, self)._track_template(changes)
|
||
|
record = self[0]
|
||
|
if 'customer_id' in changes and record.mail_template:
|
||
|
res['customer_id'] = (
|
||
|
record.mail_template,
|
||
|
{
|
||
|
'composition_mode': 'mass_mail',
|
||
|
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||
|
}
|
||
|
)
|
||
|
elif 'datetime' in changes:
|
||
|
res['datetime'] = (
|
||
|
'test_mail.mail_test_ticket_tracking_view',
|
||
|
{
|
||
|
'composition_mode': 'mass_mail',
|
||
|
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||
|
}
|
||
|
)
|
||
|
return res
|
||
|
|
||
|
def _creation_subtype(self):
|
||
|
if self.container_id:
|
||
|
return self.env.ref('test_mail.st_mail_test_ticket_container_upd')
|
||
|
return super(MailTestTicket, self)._creation_subtype()
|
||
|
|
||
|
def _track_subtype(self, init_values):
|
||
|
self.ensure_one()
|
||
|
if 'container_id' in init_values and self.container_id:
|
||
|
return self.env.ref('test_mail.st_mail_test_ticket_container_upd')
|
||
|
return super(MailTestTicket, self)._track_subtype(init_values)
|
||
|
|
||
|
def _get_customer_information(self):
|
||
|
email_normalized_to_values = super()._get_customer_information()
|
||
|
|
||
|
for record in self.filtered('email_from'):
|
||
|
email_from_normalized = email_normalize(record.email_from)
|
||
|
if not email_from_normalized: # do not fill Falsy with random data
|
||
|
continue
|
||
|
values = email_normalized_to_values.setdefault(email_from_normalized, {})
|
||
|
if not values.get('mobile'):
|
||
|
values['mobile'] = record.mobile_number
|
||
|
if not values.get('phone'):
|
||
|
values['phone'] = record.phone_number
|
||
|
return email_normalized_to_values
|
||
|
|
||
|
def _message_get_suggested_recipients(self):
|
||
|
recipients = super()._message_get_suggested_recipients()
|
||
|
if self.customer_id:
|
||
|
self.customer_id._message_add_suggested_recipient(
|
||
|
recipients,
|
||
|
partner=self.customer_id,
|
||
|
lang=None,
|
||
|
reason=_('Customer'),
|
||
|
)
|
||
|
elif self.email_from:
|
||
|
self._message_add_suggested_recipient(
|
||
|
recipients,
|
||
|
partner=None,
|
||
|
email=self.email_from,
|
||
|
lang=None,
|
||
|
reason=_('Customer Email'),
|
||
|
)
|
||
|
return recipients
|
||
|
|
||
|
|
||
|
class MailTestTicketEL(models.Model):
|
||
|
""" Just mail.test.ticket, but exclusion-list enabled. Kept as different
|
||
|
model to avoid messing with existing tests, notably performance, and ease
|
||
|
backward comparison. """
|
||
|
_description = 'Ticket-like model with exclusion list'
|
||
|
_name = 'mail.test.ticket.el'
|
||
|
_inherit = [
|
||
|
'mail.test.ticket',
|
||
|
'mail.thread.blacklist',
|
||
|
]
|
||
|
_primary_email = 'email_from'
|
||
|
|
||
|
email_from = fields.Char(
|
||
|
'Email',
|
||
|
compute='_compute_email_from', readonly=False, store=True)
|
||
|
|
||
|
@api.depends('customer_id')
|
||
|
def _compute_email_from(self):
|
||
|
for ticket in self.filtered(lambda r: r.customer_id and not r.email_from):
|
||
|
ticket.email_from = ticket.customer_id.email_formatted
|
||
|
|
||
|
|
||
|
class MailTestTicketMC(models.Model):
|
||
|
""" Just mail.test.ticket, but multi company. Kept as different model to
|
||
|
avoid messing with existing tests, notably performance, and ease backward
|
||
|
comparison. """
|
||
|
_description = 'Ticket-like model'
|
||
|
_name = 'mail.test.ticket.mc'
|
||
|
_inherit = ['mail.test.ticket']
|
||
|
_primary_email = 'email_from'
|
||
|
|
||
|
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
|
||
|
container_id = fields.Many2one('mail.test.container.mc', tracking=True)
|
||
|
|
||
|
def _notify_get_reply_to(self, default=None):
|
||
|
# Override to use alias of the parent container
|
||
|
aliases = self.sudo().mapped('container_id')._notify_get_reply_to(default=default)
|
||
|
res = {task.id: aliases.get(task.container_id.id) for task in self}
|
||
|
leftover = self.filtered(lambda rec: not rec.container_id)
|
||
|
if leftover:
|
||
|
res.update(super(MailTestTicketMC, leftover)._notify_get_reply_to(default=default))
|
||
|
return res
|
||
|
|
||
|
|
||
|
class MailTestContainer(models.Model):
|
||
|
""" This model can be used in tests when container records like projects
|
||
|
or teams are required. """
|
||
|
_description = 'Project-like model with alias'
|
||
|
_name = 'mail.test.container'
|
||
|
_mail_post_access = 'read'
|
||
|
_inherit = ['mail.thread', 'mail.alias.mixin']
|
||
|
|
||
|
name = fields.Char()
|
||
|
description = fields.Text()
|
||
|
customer_id = fields.Many2one('res.partner', 'Customer')
|
||
|
|
||
|
def _mail_get_partner_fields(self, introspect_fields=False):
|
||
|
return ['customer_id']
|
||
|
|
||
|
def _message_get_default_recipients(self):
|
||
|
return dict(
|
||
|
(record.id, {
|
||
|
'email_cc': False,
|
||
|
'email_to': False,
|
||
|
'partner_ids': record.customer_id.ids,
|
||
|
})
|
||
|
for record in self
|
||
|
)
|
||
|
|
||
|
def _notify_get_recipients_groups(self, message, model_description, msg_vals=None):
|
||
|
""" Activate more groups to test query counters notably (and be backward
|
||
|
compatible for tests). """
|
||
|
groups = super()._notify_get_recipients_groups(
|
||
|
message, model_description, msg_vals=msg_vals
|
||
|
)
|
||
|
for group_name, _group_method, group_data in groups:
|
||
|
if group_name == 'portal':
|
||
|
group_data['active'] = True
|
||
|
|
||
|
return groups
|
||
|
|
||
|
def _alias_get_creation_values(self):
|
||
|
values = super()._alias_get_creation_values()
|
||
|
values['alias_model_id'] = self.env['ir.model']._get('mail.test.ticket').id
|
||
|
values['alias_force_thread_id'] = False
|
||
|
if self.id:
|
||
|
values['alias_defaults'] = defaults = ast.literal_eval(self.alias_defaults or "{}")
|
||
|
defaults['container_id'] = self.id
|
||
|
return values
|
||
|
|
||
|
|
||
|
class MailTestContainerMC(models.Model):
|
||
|
""" Just mail.test.container, but multi company. Kept as different model to
|
||
|
avoid messing with existing tests, notably performance, and ease backward
|
||
|
comparison. """
|
||
|
_description = 'Project-like model with alias (MC)'
|
||
|
_name = 'mail.test.container.mc'
|
||
|
_mail_post_access = 'read'
|
||
|
_inherit = ['mail.test.container']
|
||
|
|
||
|
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
|
||
|
|
||
|
def _alias_get_creation_values(self):
|
||
|
values = super()._alias_get_creation_values()
|
||
|
values['alias_model_id'] = self.env['ir.model']._get('mail.test.ticket.mc').id
|
||
|
return values
|
||
|
|
||
|
|
||
|
class MailTestComposerMixin(models.Model):
|
||
|
""" A simple invite-like wizard using the composer mixin, rendering on
|
||
|
composer source test model. Purpose is to have a minimal composer which
|
||
|
runs on other records and check notably dynamic template support and
|
||
|
translations. """
|
||
|
_description = 'Invite-like Wizard'
|
||
|
_name = 'mail.test.composer.mixin'
|
||
|
_inherit = ['mail.composer.mixin']
|
||
|
|
||
|
name = fields.Char('Name')
|
||
|
author_id = fields.Many2one('res.partner')
|
||
|
description = fields.Html('Description', render_engine="qweb", render_options={"post_process": True}, sanitize='email_outgoing')
|
||
|
source_ids = fields.Many2many('mail.test.composer.source', string='Invite source')
|
||
|
|
||
|
def _compute_render_model(self):
|
||
|
self.render_model = 'mail.test.composer.source'
|
||
|
|
||
|
|
||
|
class MailTestComposerSource(models.Model):
|
||
|
""" A simple model on which invites are sent. """
|
||
|
_description = 'Invite-like Source'
|
||
|
_name = 'mail.test.composer.source'
|
||
|
_inherit = ['mail.thread.blacklist']
|
||
|
_primary_email = 'email_from'
|
||
|
|
||
|
name = fields.Char('Name')
|
||
|
customer_id = fields.Many2one('res.partner', 'Main customer')
|
||
|
email_from = fields.Char(
|
||
|
'Email',
|
||
|
compute='_compute_email_from', readonly=False, store=True)
|
||
|
|
||
|
@api.depends('customer_id')
|
||
|
def _compute_email_from(self):
|
||
|
for source in self.filtered(lambda r: r.customer_id and not r.email_from):
|
||
|
source.email_from = source.customer_id.email_formatted
|
||
|
|
||
|
def _mail_get_partner_fields(self, introspect_fields=False):
|
||
|
return ['customer_id']
|
||
|
|
||
|
|
||
|
class MailTestMailTrackingDuration(models.Model):
|
||
|
_description = 'Fake model to test the mixin mail.tracking.duration.mixin'
|
||
|
_name = 'mail.test.mail.tracking.duration'
|
||
|
_track_duration_field = 'customer_id'
|
||
|
_inherit = ['mail.thread', 'mail.tracking.duration.mixin']
|
||
|
|
||
|
name = fields.Char()
|
||
|
customer_id = fields.Many2one('res.partner', 'Customer', tracking=True)
|
||
|
|
||
|
def _mail_get_partner_fields(self, introspect_fields=False):
|
||
|
return ['customer_id']
|
||
|
|
||
|
|
||
|
class MailTestPublicThread(models.Model):
|
||
|
"""A model inheriting from mail.thread with public read and write access
|
||
|
to test some public and guest interactions."""
|
||
|
|
||
|
_description = "Portal Public Thread"
|
||
|
_name = "mail.test.public"
|
||
|
_inherit = ["mail.thread"]
|
||
|
|
||
|
name = fields.Char("Name")
|