19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:39 +01:00
parent 38c6088dcc
commit d9452d2060
243 changed files with 30797 additions and 10815 deletions

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import mail_test_access
from . import mail_test_lead
from . import mail_test_ticket
from . import test_mail_corner_case_models
from . import test_mail_feature_models
from . import test_mail_models
from . import test_mail_thread_models

View file

@ -1,9 +1,11 @@
from odoo import exceptions, fields, models
from odoo import fields, models, tools
class MailTestAccess(models.Model):
""" Test access on mail models without depending on real models like channel
or partner which have their own set of ACLs. """
or partner which have their own set of ACLs. Public, portal and internal
have access to this model depending on 'access' field, allowing to check
ir.rule usage. """
_description = 'Mail Access Test'
_name = 'mail.test.access'
_inherit = ['mail.thread.blacklist']
@ -27,7 +29,7 @@ class MailTestAccess(models.Model):
],
name='Access', default='public')
def _mail_get_partner_fields(self):
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']
@ -36,7 +38,7 @@ class MailTestAccessCusto(models.Model):
or partner which have their own set of ACLs. """
_description = 'Mail Access Test with Custo'
_name = 'mail.test.access.custo'
_inherit = ['mail.thread.blacklist']
_inherit = ['mail.thread.blacklist', 'mail.activity.mixin']
_mail_post_access = 'write' # default value but ease mock
_order = 'id DESC'
_primary_email = 'email_from'
@ -46,15 +48,47 @@ class MailTestAccessCusto(models.Model):
phone = fields.Char()
customer_id = fields.Many2one('res.partner', 'Customer')
is_locked = fields.Boolean()
is_readonly = fields.Boolean()
def _mail_get_partner_fields(self):
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']
def _get_mail_message_access(self, res_ids, operation, model_name=None):
# customize message creation
if operation == "create":
if any(record.is_locked for record in self.browse(res_ids)):
raise exceptions.AccessError('Cannot post on locked records')
else:
return "read"
return super()._get_mail_message_access(res_ids, operation, model_name=model_name)
def _mail_get_operation_for_mail_message_operation(self, message_operation):
# customize message creation: only unlocked, except admins
if message_operation == "create" and not self.env.user._is_admin():
return dict.fromkeys(self.filtered(lambda r: not r.is_locked), 'read')
# customize read: read access on unlocked, write access on locked
elif message_operation == "read":
return {
record: 'write' if record.is_locked else 'read'
for record in self
}
return super()._mail_get_operation_for_mail_message_operation(message_operation)
class MailTestAccessPublic(models.Model):
"""A model inheriting from mail.thread with public read and write access
to test some public and guest interactions."""
_description = "Access Test Public"
_name = "mail.test.access.public"
_inherit = ["mail.thread"]
name = fields.Char("Name")
customer_id = fields.Many2one('res.partner', 'Customer')
email = fields.Char('Email')
mobile = fields.Char('Mobile')
is_locked = fields.Boolean()
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']
def _get_customer_information(self):
email_key_to_values = super()._get_customer_information()
for record in self.filtered('email'):
# do not fill Falsy with random data, unless monorecord (= always correct)
if not tools.email_normalize(record.email) and len(self) > 1:
continue
values = email_key_to_values.setdefault(record.email, {})
if not values.get('phone'):
values['phone'] = record.mobile
return email_key_to_values

View file

@ -0,0 +1,55 @@
from odoo import fields, models, _
from odoo.tools.mail import parse_contact_from_email
class MailTestTLead(models.Model):
""" Lead-like model for business flows testing """
_name = "mail.test.lead"
_description = 'Lead-like model'
_inherit = [
'mail.thread.blacklist',
'mail.thread.cc',
'mail.activity.mixin',
]
_mail_defaults_to_email = True
_primary_email = 'email_from'
name = fields.Char()
company_id = fields.Many2one('res.company')
user_id = fields.Many2one('res.users', tracking=1)
email_from = fields.Char()
customer_name = fields.Char()
partner_id = fields.Many2one('res.partner', tracking=2)
lang_code = fields.Char()
phone = fields.Char()
def _creation_message(self):
self.ensure_one()
return _('A new lead has been created and is assigned to %(user_name)s.', user_name=self.user_id.name or _('nobody'))
def _get_customer_information(self):
email_normalized_to_values = super()._get_customer_information()
for lead in self:
email_key = lead.email_normalized or lead.email_from
values = email_normalized_to_values.setdefault(email_key, {})
values['lang'] = values.get('lang') or lead.lang_code
values['name'] = values.get('name') or lead.customer_name or parse_contact_from_email(lead.email_from)[0] or lead.email_from
values['phone'] = values.get('phone') or lead.phone
return email_normalized_to_values
def _message_post_after_hook(self, message, msg_vals):
if self.email_from and not self.partner_id:
# we consider that posting a message with a specified recipient (not a follower, a specific one)
# on a document without customer means that it was created through the chatter using
# suggested recipients. This heuristic allows to avoid ugly hacks in JS.
new_partner = message.partner_ids.filtered(
lambda partner: partner.email == self.email_from or (self.email_normalized and partner.email_normalized == self.email_normalized)
)
if new_partner:
if new_partner[0].email_normalized:
email_domain = ('email_normalized', '=', new_partner[0].email_normalized)
else:
email_domain = ('email_from', '=', new_partner[0].email)
self.search([('partner_id', '=', False), email_domain]).write({'partner_id': new_partner[0].id})
return super()._message_post_after_hook(message, msg_vals)

View file

@ -0,0 +1,266 @@
import ast
from odoo import api, fields, models, _
from odoo.tools import email_normalize
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)
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 _notify_get_recipients_groups(self, message, model_description, msg_vals=False):
# 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
elif group_name == 'customer':
group_data['active'] = True
group_data['has_button_access'] = True
return groups
def _track_template(self, changes):
res = super()._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_keys_to_values = super()._get_customer_information()
for ticket in self:
email_key = email_normalize(ticket.email_from) or ticket.email_from
# do not fill Falsy with random data, unless monorecord (= always correct)
if not email_key and len(self) > 1:
continue
values = email_keys_to_values.setdefault(email_key, {})
if not values.get('phone'):
values['phone'] = ticket.phone_number
return email_keys_to_values
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 _get_customer_information(self):
email_keys_to_values = super()._get_customer_information()
for ticket in self:
email_key = email_normalize(ticket.email_from) or ticket.email_from
# do not fill Falsy with random data, unless monorecord (= always correct)
if not email_key and len(self) > 1:
continue
values = email_keys_to_values.setdefault(email_key, {})
if not values.get('company_id'):
values['company_id'] = ticket.company_id.id
return email_keys_to_values
def _notify_get_reply_to(self, default=None, author_id=False):
# Override to use alias of the parent container
aliases = self.sudo().mapped('container_id')._notify_get_reply_to(default=default, author_id=author_id)
res = {ticket.id: aliases.get(ticket.container_id.id) for ticket in self}
leftover = self.filtered(lambda rec: not rec.container_id)
if leftover:
res.update(super()._notify_get_reply_to(default=default, author_id=author_id))
return res
def _creation_subtype(self):
if self.container_id:
return self.env.ref('test_mail.st_mail_test_ticket_container_mc_upd')
return super()._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_mc_upd')
return super()._track_subtype(init_values)
class MailTestTicketPartner(models.Model):
""" Mail.test.ticket.mc, with complete partner support. More functional
and therefore done in a separate model to avoid breaking other tests. """
_description = 'MC ticket-like model with partner support'
_name = "mail.test.ticket.partner"
_inherit = [
'mail.test.ticket.mc',
'mail.thread.blacklist',
]
_primary_email = 'email_from'
# fields to mimic stage-based tracing
state = fields.Selection(
[('new', 'New'), ('open', 'Open'), ('close', 'Close'),],
default='open', tracking=10)
state_template_id = fields.Many2one('mail.template')
def _message_post_after_hook(self, message, msg_vals):
if self.email_from and not self.customer_id:
# we consider that posting a message with a specified recipient (not a follower, a specific one)
# on a document without customer means that it was created through the chatter using
# suggested recipients. This heuristic allows to avoid ugly hacks in JS.
new_partner = message.partner_ids.filtered(
lambda partner: partner.email == self.email_from or (self.email_normalized and partner.email_normalized == self.email_normalized)
)
if new_partner:
if new_partner[0].email_normalized:
email_domain = ('email_normalized', '=', new_partner[0].email_normalized)
else:
email_domain = ('email_from', '=', new_partner[0].email)
self.search([
('customer_id', '=', False), email_domain,
]).write({'customer_id': new_partner[0].id})
return super()._message_post_after_hook(message, msg_vals)
def _creation_subtype(self):
if self.state == 'new':
return self.env.ref('test_mail.st_mail_test_ticket_partner_new')
return super(MailTestTicket, self)._creation_subtype()
def _track_template(self, changes):
res = super()._track_template(changes)
record = self[0]
# acknowledgement-like email, like in project/helpdesk
if 'state' in changes and record.state == 'new' and record.state_template_id:
res['state'] = (
record.state_template_id,
{
'auto_delete_keep_log': False,
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
'email_layout_xmlid': 'mail.mail_notification_light'
},
)
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 _notify_get_recipients_groups(self, message, model_description, msg_vals=False):
# 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

View file

@ -21,6 +21,19 @@ class MailPerformanceThread(models.Model):
record.value_pc = float(record.value) / 100
class MailPerformanceThreadRecipients(models.Model):
_name = 'mail.performance.thread.recipients'
_description = 'Performance: mail.thread, for recipients'
_inherit = ['mail.thread']
_primary_email = 'email_from'
name = fields.Char()
value = fields.Integer()
email_from = fields.Char('Email From')
partner_id = fields.Many2one('res.partner', string='Customer')
user_id = fields.Many2one('res.users', 'Responsible', tracking=1)
class MailPerformanceTracking(models.Model):
_name = 'mail.performance.tracking'
_description = 'Performance: multi tracking'
@ -36,7 +49,7 @@ class MailTestFieldType(models.Model):
""" Test default values, notably type, messing through models during gateway
processing (i.e. lead.type versus attachment.type). """
_description = 'Test Field Type'
_name = 'mail.test.field.type'
_name = "mail.test.field.type"
_inherit = ['mail.thread']
name = fields.Char()
@ -49,11 +62,11 @@ class MailTestFieldType(models.Model):
@api.model_create_multi
def create(self, vals_list):
# Emulate an addon that alters the creation context, such as `crm`
if not self._context.get('default_type'):
if not self.env.context.get('default_type'):
self = self.with_context(default_type='first')
return super(MailTestFieldType, self).create(vals_list)
def _mail_get_partner_fields(self):
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']
@ -61,7 +74,7 @@ class MailTestLang(models.Model):
""" A simple chatter model with lang-based capabilities, allowing to
test translations. """
_description = 'Lang Chatter Model'
_name = 'mail.test.lang'
_name = "mail.test.lang"
_inherit = ['mail.thread']
name = fields.Char()
@ -69,30 +82,85 @@ class MailTestLang(models.Model):
customer_id = fields.Many2one('res.partner')
lang = fields.Char('Lang')
def _mail_get_partner_fields(self):
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']
def _notify_get_recipients_groups(self, msg_vals=None):
groups = super(MailTestLang, self)._notify_get_recipients_groups(msg_vals=msg_vals)
local_msg_vals = dict(msg_vals or {})
def _notify_get_recipients_groups(self, message, model_description, msg_vals=False):
groups = super()._notify_get_recipients_groups(
message, model_description, msg_vals=msg_vals
)
for group in [g for g in groups if g[0] in('follower', 'customer')]:
group_options = group[2]
group_options['has_button_access'] = True
group_options['actions'] = [
{'url': self._notify_get_action_link('controller', controller='/test_mail/do_stuff', **local_msg_vals),
'title': _('NotificationButtonTitle')}
]
return groups
# ------------------------------------------------------------
# TRACKING MODELS
# ------------------------------------------------------------
class MailTestTrackAllM2m(models.Model):
_description = 'Sub-model: pseudo tags for tracking'
_name = "mail.test.track.all.m2m"
_inherit = ['mail.thread']
name = fields.Char('Name')
class MailTestTrackAllO2m(models.Model):
_description = 'Sub-model: pseudo tags for tracking'
_name = "mail.test.track.all.o2m"
_inherit = ['mail.thread']
name = fields.Char('Name')
mail_track_all_id = fields.Many2one('mail.test.track.all')
class MailTestTrackAllPropertiesParent(models.Model):
_description = 'Properties Parent'
_name = "mail.test.track.all.properties.parent"
definition_properties = fields.PropertiesDefinition()
class MailTestTrackAll(models.Model):
_description = 'Test tracking on all field types'
_name = "mail.test.track.all"
_inherit = ['mail.thread']
boolean_field = fields.Boolean('Boolean', tracking=1)
char_field = fields.Char('Char', tracking=2)
company_id = fields.Many2one('res.company')
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
date_field = fields.Date('Date', tracking=3)
datetime_field = fields.Datetime('Datetime', tracking=4)
float_field = fields.Float('Float', tracking=5)
float_field_with_digits = fields.Float('Precise Float', digits=(10, 8), tracking=5)
html_field = fields.Html('Html', tracking=False)
integer_field = fields.Integer('Integer', tracking=7)
many2many_field = fields.Many2many(
'mail.test.track.all.m2m', string='Many2Many',
tracking=8)
many2one_field_id = fields.Many2one('res.partner', string='Many2one', tracking=9)
monetary_field = fields.Monetary('Monetary', tracking=10)
one2many_field = fields.One2many(
'mail.test.track.all.o2m', 'mail_track_all_id',
string='One2Many',
tracking=11)
properties_parent_id = fields.Many2one('mail.test.track.all.properties.parent', tracking=True)
properties = fields.Properties('Properties', definition='properties_parent_id.definition_properties')
selection_field = fields.Selection(
string='Selection',
selection=[('first', 'FIRST'), ('second', 'SECOND')],
tracking=12)
text_field = fields.Text('Text', tracking=13)
name = fields.Char('Name')
class MailTestTrackCompute(models.Model):
_name = 'mail.test.track.compute'
_description = "Test tracking with computed fields"
_name = "mail.test.track.compute"
_inherit = ['mail.thread']
partner_id = fields.Many2one('res.partner', tracking=True)
@ -101,63 +169,60 @@ class MailTestTrackCompute(models.Model):
partner_phone = fields.Char(related='partner_id.phone', tracking=True)
class MailTestTrackDurationMixin(models.Model):
_description = 'Fake model to test the mixin mail.tracking.duration.mixin'
_name = "mail.test.track.duration.mixin"
_track_duration_field = 'customer_id'
_inherit = ['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 MailTestTrackGroups(models.Model):
_description = "Test tracking with groups"
_name = "mail.test.track.groups"
_inherit = ['mail.thread']
name = fields.Char(tracking=1)
partner_id = fields.Many2one('res.partner', tracking=2, groups="base.group_user")
secret = fields.Char(tracking=3, groups="base.group_user")
class MailTestTrackMonetary(models.Model):
_name = 'mail.test.track.monetary'
_description = 'Test tracking monetary field'
_name = "mail.test.track.monetary"
_inherit = ['mail.thread']
company_id = fields.Many2one('res.company')
company_currency = fields.Many2one("res.currency", string='Currency', related='company_id.currency_id', readonly=True, tracking=True)
revenue = fields.Monetary('Revenue', currency_field='company_currency', tracking=True)
class MailTestMultiCompanyWithActivity(models.Model):
""" This model can be used in multi company tests with activity"""
_name = "mail.test.multi.company.with.activity"
_description = "Test Multi Company Mail With Activity"
_inherit = ["mail.thread", "mail.activity.mixin"]
name = fields.Char()
company_id = fields.Many2one("res.company")
class MailTestSelectionTracking(models.Model):
class MailTestTrackSelection(models.Model):
""" Test tracking for selection fields """
_description = 'Test Selection Tracking'
_name = 'mail.test.track.selection'
_name = "mail.test.track.selection"
_inherit = ['mail.thread']
name = fields.Char()
selection_type = fields.Selection([('first', 'First'), ('second', 'Second')], tracking=True)
class MailTestTrackAll(models.Model):
_name = 'mail.test.track.all'
_description = 'Test tracking on all field types'
_inherit = ['mail.thread']
boolean_field = fields.Boolean('Boolean', tracking=True)
char_field = fields.Char('Char', tracking=True)
company_id = fields.Many2one('res.company')
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
date_field = fields.Date('Date', tracking=True)
datetime_field = fields.Datetime('Datetime', tracking=True)
float_field = fields.Float('Float', tracking=True)
html_field = fields.Html('Html', tracking=True)
integer_field = fields.Integer('Integer', tracking=True)
many2one_field_id = fields.Many2one('res.partner', string='Many2one', tracking=True)
monetary_field = fields.Monetary('Monetary', tracking=True)
selection_field = fields.Selection(string='Selection', selection=[['first', 'FIRST']], tracking=True)
text_field = fields.Text('Text', tracking=True)
# ------------------------------------------------------------
# OTHER
# ------------------------------------------------------------
class MailTestMultiCompany(models.Model):
""" This model can be used in multi company tests"""
_name = 'mail.test.multi.company'
""" This model can be used in multi company tests, with attachments support
for checking record update in MC """
_description = "Test Multi Company Mail"
_inherit = 'mail.thread'
_name = "mail.test.multi.company"
_inherit = ['mail.thread.main.attachment']
name = fields.Char()
company_id = fields.Many2one('res.company')
@ -168,16 +233,29 @@ class MailTestMultiCompanyRead(models.Model):
even if the user has no write access. """
_description = 'Simple Chatter Model '
_name = 'mail.test.multi.company.read'
_inherit = ['mail.test.multi.company']
_inherit = ['mail.test.multi.company', 'mail.activity.mixin']
_mail_post_access = 'read'
class MailTestNotMailThread(models.Model):
class MailTestMultiCompanyWithActivity(models.Model):
""" This model can be used in multi company tests with activity"""
_description = "Test Multi Company Mail With Activity"
_name = "mail.test.multi.company.with.activity"
_inherit = ["mail.thread", "mail.activity.mixin"]
name = fields.Char()
company_id = fields.Many2one("res.company")
class MailTestNothread(models.Model):
""" Models not inheriting from mail.thread but using some cross models
capabilities of mail. """
_name = 'mail.test.nothread'
_description = "NoThread Model"
_name = "mail.test.nothread"
name = fields.Char()
company_id = fields.Many2one('res.company')
customer_id = fields.Many2one('res.partner')
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']

View file

@ -0,0 +1,95 @@
from odoo import api, fields, models
from odoo.fields import Domain
# ------------------------------------------------------------
# RECIPIENTS
# ------------------------------------------------------------
class MailTestRecipients(models.Model):
_name = 'mail.test.recipients'
_description = "Test Recipients Computation"
_inherit = ['mail.thread.cc']
_primary_email = 'customer_email'
company_id = fields.Many2one('res.company')
contact_ids = fields.Many2many('res.partner')
customer_id = fields.Many2one('res.partner')
customer_email = fields.Char('Customer Email', compute='_compute_customer_email', readonly=False, store=True)
customer_phone = fields.Char('Customer Phone', compute='_compute_customer_phone', readonly=False, store=True)
name = fields.Char()
@api.depends('customer_id')
def _compute_customer_email(self):
for source in self.filtered(lambda r: r.customer_id and not r.customer_email):
source.customer_email = source.customer_id.email_formatted
@api.depends('customer_id')
def _compute_customer_phone(self):
for source in self.filtered(lambda r: r.customer_id and not r.customer_phone):
source.customer_phone = source.customer_id.phone
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id', 'contact_ids']
class MailTestThreadCustomer(models.Model):
_name = 'mail.test.thread.customer'
_description = "Test Customer Thread Model"
_inherit = ['mail.test.recipients']
_mail_thread_customer = True
_primary_email = 'customer_email'
# ------------------------------------------------------------
# PROPERTIES
# ------------------------------------------------------------
class MailTestProperties(models.Model):
_name = 'mail.test.properties'
_description = 'Mail Test Properties'
_inherit = ['mail.thread']
name = fields.Char('Name')
parent_id = fields.Many2one('mail.test.properties', string='Parent')
properties = fields.Properties('Properties', definition='parent_id.definition_properties')
definition_properties = fields.PropertiesDefinition('Definitions')
# ------------------------------------------------------------
# ROTTING RESOURCES
# ------------------------------------------------------------
class MailTestStageField(models.Model):
_description = 'Fake model to be a stage to help test rotting implementation'
_name = 'mail.test.rotting.stage'
name = fields.Char()
rotting_threshold_days = fields.Integer(default=3)
no_rot = fields.Boolean(default=False)
class MailTestRottingMixin(models.Model):
_description = 'Fake model to test the rotting part of the mixin mail.thread.tracking.duration.mixin'
_name = 'mail.test.rotting.resource'
_track_duration_field = 'stage_id'
_inherit = ['mail.tracking.duration.mixin']
name = fields.Char()
date_last_stage_update = fields.Datetime(
'Last Stage Update', compute='_compute_date_last_stage_update', index=True, readonly=True, store=True)
stage_id = fields.Many2one('mail.test.rotting.stage', 'Stage')
done = fields.Boolean(default=False)
def _get_rotting_depends_fields(self):
return super()._get_rotting_depends_fields() + ['done', 'stage_id.no_rot']
def _get_rotting_domain(self):
return super()._get_rotting_domain() & Domain([
('done', '=', False),
('stage_id.no_rot', '=', False),
])
@api.depends('stage_id')
def _compute_date_last_stage_update(self):
self.date_last_stage_update = fields.Datetime.now()

View file

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
@ -8,29 +5,93 @@ 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'
_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 MailTestSimpleMainAttachment(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'
_name = "mail.test.gateway"
_inherit = ['mail.thread.blacklist']
_primary_email = 'email_from'
name = fields.Char()
email_from = fields.Char()
custom_field = fields.Char()
user_id = fields.Many2one('res.users', 'Responsible')
@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'),
}
@ -38,11 +99,32 @@ class MailTestGateway(models.Model):
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'
_name = "mail.test.gateway.groups"
_inherit = ['mail.thread.blacklist', 'mail.alias.mixin']
_mail_flat_thread = False
_primary_email = 'email_from'
@ -60,25 +142,15 @@ class MailTestGatewayGroups(models.Model):
values['alias_parent_thread_id'] = self.id
return values
def _mail_get_partner_fields(self):
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):
class MailTestTrack(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'
_name = "mail.test.track"
_inherit = ['mail.thread']
name = fields.Char()
@ -86,24 +158,40 @@ class MailTestStandard(models.Model):
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)
parent_id = fields.Many2one('mail.test.track', string='Parent')
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'
_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)
company_id = fields.Many2one('res.company')
def action_start(self, action_summary):
return self.activity_schedule(
'test_mail.mail_act_test_todo',
summary=action_summary
summary=action_summary,
user_id=self.env.uid,
)
def action_close(self, action_feedback, attachment_ids=None):
@ -112,196 +200,18 @@ class MailTestActivity(models.Model):
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)
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):
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
)
def _notify_get_recipients_groups(self, 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(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'})
elif 'datetime' in changes:
res['datetime'] = ('test_mail.mail_test_ticket_tracking_view', {'composition_mode': 'mass_mail'})
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)
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 _creation_subtype(self):
if self.container_id:
return self.env.ref('test_mail.st_mail_test_ticket_container_mc_upd')
return super()._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_mc_upd')
return super()._track_subtype(init_values)
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')
alias_id = fields.Many2one(
'mail.alias', 'Alias',
delegate=True)
def _mail_get_partner_fields(self):
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, msg_vals=None):
""" Activate more groups to test query counters notably (and be backward
compatible for tests). """
groups = super(MailTestContainer, self)._notify_get_recipients_groups(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(MailTestContainer, self)._alias_get_creation_values()
values['alias_model_id'] = self.env['ir.model']._get('mail.test.container').id
if self.id:
values['alias_force_thread_id'] = self.id
values['alias_parent_thread_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)
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'
_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=False)
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):
@ -310,8 +220,8 @@ class MailTestComposerMixin(models.Model):
class MailTestComposerSource(models.Model):
""" A simple model on which invites are sent. """
_description = 'Invite-like Wizard'
_name = 'mail.test.composer.source'
_description = 'Invite-like Source'
_name = "mail.test.composer.source"
_inherit = ['mail.thread.blacklist']
_primary_email = 'email_from'
@ -326,5 +236,5 @@ class MailTestComposerSource(models.Model):
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):
def _mail_get_partner_fields(self, introspect_fields=False):
return ['customer_id']

View file

@ -4,7 +4,7 @@
from odoo import api, fields, models
class MailTestCC(models.Model):
class MailTestCc(models.Model):
_name = 'mail.test.cc'
_description = "Test Email CC Thread"
_inherit = ['mail.thread.cc']