[REF] mail: cleanup post with {view, template} wrappers
RATIONALE
Purpose of this commit is to cleanup main post helpers and have a more easy
and understandable way of calling them.
SUMMARY
We now have two main API methods, based on business flow: either posting
on documents, either sending a mass mailing. Indeed those two flows are
different
* post: create message, then launch notification process by taking into
account subtype, followers, ...
* mail: create mails in batch with recipients being based on template or
given partners. No notifications is involved, only maybe traces if a
mass mailing is linked
Delegate QWeb rendering to the render mixin (i.e. _render_template_qweb_view)
in order to have a single point to forge evaluation context and re-use
existing rendering code.
SPECIFICATIONS
Main API helpers are now
* ``message_post_with_source``: (batch) post on records, using an ir.ui.view
(given a record or its xml id) or a mail.template record (given a record or
its xml id). When using a template, a composer is called to post on each
record (as batch post is not yet supported). When using a view, a direct
call to message_post using the rendered bodies is done, one record at a
time.
* ``message_mail_with_source``: send a mass mailing on records, acting like
invoking the mail composer in mass mode. Same arguments are valid, either
a reference to a view, either a reference to a mail template.
Other helpers are
* ``_message_log_with_view``: (batch) log on records, using an ir.ui.view
to render the body using QWeb (no notification process);
* ``_message_log(_batch)``: (batch) log on records (no notification process);
* ``message_notify``: notify partners on records (creating notifications
specifically for some people while message itself is not displayed in
chatter);
Code migration
* ``message_post_with_template`` in "mass mode": use ``message_mail_with_source``
and set the template record as source;
* ``message_post_with_template`` in "comment" mode: use ``message_post_with_source``
and set the template record as source;
* ``message_post_with_view``: its main usage was to post on a document, in which
case it generally can be replaced by ``message_mail_with_source`` using
the view reference as source;
Task-2710804 (Mail: Clean MailThread Posting API)
Part-of: odoo/odoo#99482
This commit is contained in:
@@ -750,9 +750,9 @@ class Meeting(models.Model):
|
||||
raise UserError(_("There are no attendees on these events"))
|
||||
template_id = self.env['ir.model.data']._xmlid_to_res_id('calendar.calendar_template_meeting_update', raise_if_not_found=False)
|
||||
# The mail is sent with datetime corresponding to the sending user TZ
|
||||
composition_mode = self.env.context.get('composition_mode', 'comment')
|
||||
default_composition_mode = self.env.context.get('default_composition_mode', self.env.context.get('composition_mode', 'comment'))
|
||||
compose_ctx = dict(
|
||||
default_composition_mode=composition_mode,
|
||||
default_composition_mode=default_composition_mode,
|
||||
default_model='calendar.event',
|
||||
default_res_ids=self.ids,
|
||||
default_use_template=bool(template_id),
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Meetings" sample="1" multi_edit="1">
|
||||
<header>
|
||||
<button name="action_open_composer" type="object" context="{'composition_mode':'mass_mail'}"
|
||||
<button name="action_open_composer" type="object" context="{'default_composition_mode':'mass_mail'}"
|
||||
string="Send Mail"/>
|
||||
</header>
|
||||
<field name="name" string="Subject" decoration-bf="1" attrs="{'readonly':[('recurrency','=',True)]}"/>
|
||||
|
||||
@@ -1561,9 +1561,9 @@ class Lead(models.Model):
|
||||
def _merge_log_summary(self, merged_followers, opportunities_tail):
|
||||
"""Log the merge message on the lead."""
|
||||
self.ensure_one()
|
||||
self.message_post_with_view(
|
||||
self.message_post_with_source(
|
||||
"crm.crm_lead_merge_summary",
|
||||
values={
|
||||
render_values={
|
||||
"merged_followers": merged_followers,
|
||||
"opportunities": opportunities_tail,
|
||||
"is_html_empty": is_html_empty,
|
||||
|
||||
@@ -68,7 +68,7 @@ class Lead(models.Model):
|
||||
|
||||
normalized_email = tools.email_normalize(lead.email_from)
|
||||
if not normalized_email:
|
||||
lead.message_post_with_view(
|
||||
lead.message_post_with_source(
|
||||
'crm_iap_enrich.mail_message_lead_enrich_no_email',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
@@ -78,7 +78,7 @@ class Lead(models.Model):
|
||||
# Discard domains of generic email providers as it won't return relevant information
|
||||
if email_domain in iap_tools._MAIL_DOMAIN_BLACKLIST:
|
||||
lead.write({'iap_enrich_done': True})
|
||||
lead.message_post_with_view(
|
||||
lead.message_post_with_source(
|
||||
'crm_iap_enrich.mail_message_lead_enrich_notfound',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
@@ -128,7 +128,7 @@ class Lead(models.Model):
|
||||
iap_data = iap_response.get(str(lead.id))
|
||||
if not iap_data:
|
||||
lead.write({'iap_enrich_done': True})
|
||||
lead.message_post_with_view(
|
||||
lead.message_post_with_source(
|
||||
'crm_iap_enrich.mail_message_lead_enrich_notfound',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
@@ -161,9 +161,9 @@ class Lead(models.Model):
|
||||
|
||||
template_values = iap_data
|
||||
template_values['flavor_text'] = _("Lead enriched based on email address")
|
||||
lead.message_post_with_view(
|
||||
lead.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=template_values,
|
||||
render_values=template_values,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -278,9 +278,9 @@ class CRMLeadMiningRequest(models.Model):
|
||||
leads = self.env['crm.lead'].create(lead_vals_list)
|
||||
for lead in leads:
|
||||
if messages_to_post.get(lead.reveal_id):
|
||||
lead.message_post_with_view(
|
||||
lead.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=messages_to_post[lead.reveal_id],
|
||||
render_values=messages_to_post[lead.reveal_id],
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -100,9 +100,9 @@ class EventBooth(models.Model):
|
||||
|
||||
def _post_confirmation_message(self):
|
||||
for booth in self:
|
||||
booth.event_id.message_post_with_view(
|
||||
booth.event_id.message_post_with_source(
|
||||
'event_booth.event_booth_booked_template',
|
||||
values={
|
||||
render_values={
|
||||
'booth': booth,
|
||||
},
|
||||
subtype_xmlid='event_booth.mt_event_booth_booked',
|
||||
|
||||
@@ -77,9 +77,9 @@ class EventRegistration(models.Model):
|
||||
registrations = super(EventRegistration, self).create(vals_list)
|
||||
for registration in registrations:
|
||||
if registration.sale_order_id:
|
||||
registration.message_post_with_view(
|
||||
registration.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': registration, 'origin': registration.sale_order_id},
|
||||
render_values={'self': registration, 'origin': registration.sale_order_id},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
return registrations
|
||||
|
||||
@@ -636,11 +636,12 @@ class HrExpense(models.Model):
|
||||
def refuse_expense(self, reason):
|
||||
self.write({'is_refused': True})
|
||||
self.sheet_id.write({'state': 'cancel'})
|
||||
self.sheet_id.message_post_with_view(
|
||||
'hr_expense.hr_expense_template_refuse_reason',
|
||||
values={'reason': reason, 'is_sheet': False, 'name': self.name},
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
for expense in self:
|
||||
expense.sheet_id.message_post_with_source(
|
||||
'hr_expense.hr_expense_template_refuse_reason',
|
||||
render_values={'reason': reason, 'is_sheet': False, 'name': expense.name},
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
|
||||
@api.model
|
||||
def get_expense_dashboard(self):
|
||||
@@ -1218,10 +1219,10 @@ class HrExpenseSheet(models.Model):
|
||||
self.write({'state': 'cancel'})
|
||||
subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
|
||||
for sheet in self:
|
||||
sheet.message_post_with_view(
|
||||
sheet.message_post_with_source(
|
||||
'hr_expense.hr_expense_template_refuse_reason',
|
||||
subtype_id=subtype_id,
|
||||
values={'reason': reason, 'is_sheet': True, 'name': sheet.name},
|
||||
render_values={'reason': reason, 'is_sheet': True, 'name': sheet.name},
|
||||
)
|
||||
self.activity_update()
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ class HrEmployee(models.Model):
|
||||
employees = super().create(vals_list)
|
||||
for employee in employees:
|
||||
if employee.applicant_id:
|
||||
employee.applicant_id.message_post_with_view(
|
||||
employee.applicant_id.message_post_with_source(
|
||||
'hr_recruitment.applicant_hired_template',
|
||||
values={'applicant': employee.applicant_id},
|
||||
render_values={'applicant': employee.applicant_id},
|
||||
subtype_xmlid='hr_recruitment.mt_applicant_hired',
|
||||
)
|
||||
return employees
|
||||
|
||||
@@ -45,9 +45,16 @@ class ApplicantGetRefuseReason(models.TransientModel):
|
||||
self.applicant_ids.write({'refuse_reason_id': self.refuse_reason_id.id, 'active': False})
|
||||
if self.send_mail:
|
||||
applicants = self.applicant_ids.filtered(lambda x: x.email_from or x.partner_id.email)
|
||||
applicants.with_context(active_test=True).message_post_with_template(
|
||||
self.template_id.id,
|
||||
auto_delete_message=True,
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
# TDE note: keeping 16.0 behavior, clean me please
|
||||
message_values = {
|
||||
'auto_delete_message' : True,
|
||||
'email_layout_xmlid' : 'mail.mail_notification_light',
|
||||
}
|
||||
if len(applicants) > 1:
|
||||
applicants.with_context(active_test=True).message_mail_with_source(self.template_id, **message_values)
|
||||
else:
|
||||
applicants.with_context(active_test=True).message_post_with_source(
|
||||
self.template_id,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
**message_values
|
||||
)
|
||||
|
||||
@@ -134,9 +134,9 @@ class HrContract(models.Model):
|
||||
).create(all_new_leave_vals)
|
||||
new_leaves.filtered(lambda l: l.state in 'validate')._validate_leave_request()
|
||||
for index, new_leave in enumerate(new_leaves):
|
||||
new_leave.message_post_with_view(
|
||||
new_leave.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': new_leave, 'origin': all_new_leave_origin[index]},
|
||||
render_values={'self': new_leave, 'origin': all_new_leave_origin[index]},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
return super(HrContract, self - specific_contracts).write(vals)
|
||||
|
||||
@@ -207,12 +207,10 @@ class ServerActions(models.Model):
|
||||
subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
|
||||
else:
|
||||
subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
|
||||
for record in records:
|
||||
record.message_post_with_template(
|
||||
self.template_id.id,
|
||||
composition_mode='comment',
|
||||
subtype_id=subtype_id,
|
||||
)
|
||||
records.message_post_with_source(
|
||||
self.template_id,
|
||||
subtype_id=subtype_id,
|
||||
)
|
||||
else:
|
||||
template = self.template_id.with_context(cleaned_ctx)
|
||||
for res_id in res_ids:
|
||||
|
||||
@@ -574,16 +574,16 @@ class MailActivity(models.Model):
|
||||
next_activities_values.append(vals)
|
||||
|
||||
# post message on activity, before deleting it
|
||||
activity_message = record.message_post_with_view(
|
||||
activity_message = record.message_post_with_source(
|
||||
'mail.message_activity_done',
|
||||
values={
|
||||
attachment_ids=attachment_ids,
|
||||
render_values={
|
||||
'activity': activity,
|
||||
'feedback': feedback,
|
||||
'display_assignee': activity.user_id != self.env.user
|
||||
},
|
||||
subtype_xmlid='mail.mt_activities',
|
||||
mail_activity_type_id=activity.activity_type_id.id,
|
||||
attachment_ids=[Command.link(attachment_id) for attachment_id in attachment_ids] if attachment_ids else [],
|
||||
subtype_xmlid='mail.mt_activities',
|
||||
)
|
||||
|
||||
# Moving the attachments in the message
|
||||
|
||||
@@ -342,12 +342,10 @@ class MailActivityMixin(models.AbstractModel):
|
||||
template = self.env['mail.template'].browse(template_id).exists()
|
||||
if not template:
|
||||
return False
|
||||
for record in self:
|
||||
record.message_post_with_template(
|
||||
template_id,
|
||||
composition_mode='comment',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
self.message_post_with_source(
|
||||
template,
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
return True
|
||||
|
||||
def activity_search(self, act_type_xmlids='', user_id=None, additional_domain=None):
|
||||
|
||||
+287
-112
@@ -635,7 +635,7 @@ class MailThread(models.AbstractModel):
|
||||
:param dict changes: mapping {record_id: (changed_field_names, tracking_value_ids)}
|
||||
containing existing records only
|
||||
"""
|
||||
if not changes:
|
||||
if not self or not changes:
|
||||
return True
|
||||
# Clean the context to get rid of residual default_* keys
|
||||
# that could cause issues afterward during the mail.message
|
||||
@@ -644,15 +644,18 @@ class MailThread(models.AbstractModel):
|
||||
# its creation, but could refer to wrong parent message id,
|
||||
# leading to a traceback in case the related message_id
|
||||
# doesn't exist
|
||||
self = self.with_context(clean_context(self._context))
|
||||
cleaned_self = self.with_context(clean_context(self._context))._fallback_lang()
|
||||
templates = self._track_template(changes)
|
||||
default_composition_mode = 'mass_mail' if len(self) != 1 else 'comment'
|
||||
for _field_name, (template, post_kwargs) in templates.items():
|
||||
if not template:
|
||||
continue
|
||||
if isinstance(template, str):
|
||||
self._fallback_lang().message_post_with_view(template, **post_kwargs)
|
||||
|
||||
composition_mode = post_kwargs.pop('composition_mode', default_composition_mode)
|
||||
if composition_mode == 'mass_mail':
|
||||
cleaned_self.message_mail_with_source(template, **post_kwargs)
|
||||
else:
|
||||
self._fallback_lang().message_post_with_template(template.id, **post_kwargs)
|
||||
cleaned_self.message_post_with_source(template, **post_kwargs)
|
||||
return True
|
||||
|
||||
def _track_template(self, changes):
|
||||
@@ -1813,6 +1816,8 @@ class MailThread(models.AbstractModel):
|
||||
:param int author_id: optional ID of partner record being the author. See
|
||||
``_message_compute_author`` that uses it to make email_from / author_id coherent;
|
||||
:param int parent_id: handle thread formation
|
||||
:param str subtype_xmlid: optional xml id of a mail.message.subtype to
|
||||
fetch, will force value of subtype_id;
|
||||
:param int subtype_id: subtype_id of the message, used mainly for followers
|
||||
notification mechanism;
|
||||
:param list(int) partner_ids: partner_ids to notify in addition to partners
|
||||
@@ -1909,9 +1914,9 @@ class MailThread(models.AbstractModel):
|
||||
'res_id': self.id,
|
||||
# content
|
||||
'body': body,
|
||||
'subject': subject or False,
|
||||
'message_type': message_type,
|
||||
'parent_id': self._message_compute_parent_id(parent_id),
|
||||
'subject': subject or False,
|
||||
'subtype_id': subtype_id,
|
||||
# recipients
|
||||
'partner_ids': partner_ids,
|
||||
@@ -2094,95 +2099,203 @@ class MailThread(models.AbstractModel):
|
||||
# MESSAGE POST API / WRAPPERS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_compose_with_view(self, views_or_xmlid, message_log=False, **kwargs):
|
||||
""" Helper method to send a mail / post a message / log a note using
|
||||
a view_id to render using the ir.qweb engine. This method is stand
|
||||
alone, because there is nothing in template and composer that allows
|
||||
to handle views in batch. This method should probably disappear when
|
||||
templates handle ir ui views. """
|
||||
values = kwargs.pop('values', None) or dict()
|
||||
try:
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
values['slug'] = slug
|
||||
except ImportError:
|
||||
values['slug'] = lambda self: self.id
|
||||
view_ref = views_or_xmlid.id if isinstance(views_or_xmlid, models.BaseModel) else views_or_xmlid
|
||||
def message_mail_with_source(self, source_ref, render_values=None,
|
||||
message_type='notification',
|
||||
auto_commit=False,
|
||||
**kwargs):
|
||||
""" Send a mass mail on self, using an external source to render part
|
||||
of the content. It can be either a 'mail.template', either a view used
|
||||
to render the body using QWeb.
|
||||
|
||||
messages_as_sudo = self.env['mail.message']
|
||||
for record in self:
|
||||
values['object'] = record
|
||||
rendered_template = self.env['ir.qweb']._render(view_ref, values, minimal_qcontext=True, raise_if_not_found=False)
|
||||
if not rendered_template:
|
||||
continue
|
||||
if message_log:
|
||||
messages_as_sudo += record._message_log(body=rendered_template, **kwargs)
|
||||
else:
|
||||
kwargs['body'] = rendered_template
|
||||
# a bit complicated to handle, to be improved soon in master
|
||||
_mails_as_sudo, _messages_as_sudo = record.message_post_with_template(False, **kwargs)
|
||||
if _messages_as_sudo:
|
||||
messages_as_sudo += _messages_as_sudo
|
||||
SPOILER: this method currently calls a composer in a loop when using
|
||||
a view even if it is suboptimal. This is due to current composer
|
||||
implementation.. This will be cleaned soon to optimize mass mailing
|
||||
through mail.thread and lessen usage of composer itself.
|
||||
|
||||
return messages_as_sudo
|
||||
Default values
|
||||
* subtype_id: will be False, forced by composer in mass mode;
|
||||
|
||||
def message_post_with_view(self, views_or_xmlid, **kwargs):
|
||||
""" Helper method to send a mail / post a message using a view_id """
|
||||
return self._message_compose_with_view(views_or_xmlid, **kwargs)
|
||||
:param record/str source_ref: reference to a source for rendering.
|
||||
It can be one of
|
||||
* a MailTemplate record. It will be used to render the various
|
||||
message values (body, subject, recipients, ...). It should behave
|
||||
like using the mail composer with a template;
|
||||
* an IrUIView record. It will be used to render the content
|
||||
(body). Other fields are left to the caller and/or default values
|
||||
computation;
|
||||
* an XmlID of a MailTemplate or of an IrUiView: see above;
|
||||
:param dict render_values: additional rendering values for qweb context;
|
||||
|
||||
def message_post_with_template(self, template_id, email_layout_xmlid=None, auto_commit=False, **kwargs):
|
||||
""" Helper method to send a mail with a template
|
||||
:param template_id : the id of the template to render to create the body of the message
|
||||
:param **kwargs : parameter to create a mail.compose.message woaerd (which inherit from mail.message)
|
||||
:param str message_type: one of 'notification' or 'comment';
|
||||
:param bool auto_commit: auto commit after each batch of emails sent
|
||||
(see ``MailComposer._action_send_mail()``);
|
||||
:param dict kwargs: additional values given to the 'mail.compose.message'
|
||||
creation;
|
||||
|
||||
:return: created mail.mail records, as sudo
|
||||
"""
|
||||
template, view = self._get_source_from_ref(source_ref)
|
||||
|
||||
# preliminary value safety check
|
||||
self._raise_for_invalid_parameters(
|
||||
set(kwargs.keys()),
|
||||
forbidden_names={'model', 'res_id'}
|
||||
forbidden_names={'body', 'composition_mode', 'model', 'res_id', 'values'}
|
||||
)
|
||||
|
||||
# Get composition mode, or force it according to the number of record in self
|
||||
if not kwargs.get('composition_mode'):
|
||||
kwargs['composition_mode'] = 'comment' if len(self.ids) == 1 else 'mass_mail'
|
||||
if not kwargs.get('message_type'):
|
||||
kwargs['message_type'] = 'notification'
|
||||
res_id = self.ids[0] if self.ids else 0
|
||||
res_ids = self.ids
|
||||
# with a view, render bodies in batch (template is managed by composer)
|
||||
bodies = self.env['mail.render.mixin']._render_template_qweb_view(
|
||||
view,
|
||||
self._name,
|
||||
self.ids,
|
||||
add_context=render_values,
|
||||
) if view else {}
|
||||
|
||||
# support xml based subtype id
|
||||
if kwargs.get('subtype_xmlid') and not kwargs.get('subtype_id'):
|
||||
kwargs['subtype_id'] = self.env['ir.model.data']._xmlid_to_res_id(kwargs.pop('subtype_xmlid'))
|
||||
# Prepare composer values for creation
|
||||
composer_values = {
|
||||
'composition_mode': 'mass_mail',
|
||||
'message_type': message_type,
|
||||
# subtype is not really used in mass mail mode as it is used mainly
|
||||
# when posting, but keep it in case it is used in post send
|
||||
'subtype_id': kwargs.pop('subtype_id', False) or self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
**kwargs,
|
||||
}
|
||||
composer_ctx = {
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': self._name,
|
||||
'default_template_id': template.id if template else False,
|
||||
}
|
||||
|
||||
# Create the composer
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
active_id=res_id,
|
||||
active_ids=res_ids,
|
||||
active_model=self._name,
|
||||
default_composition_mode=kwargs['composition_mode'],
|
||||
default_email_layout_xmlid=email_layout_xmlid,
|
||||
default_model=self._name,
|
||||
default_res_id=res_id,
|
||||
default_template_id=template_id,
|
||||
).create(kwargs)
|
||||
# Simulate the onchange (like trigger in form the view) only
|
||||
# when having a template in single-email mode
|
||||
if template_id:
|
||||
update_values = composer._onchange_template_id(
|
||||
template_id,
|
||||
kwargs['composition_mode'],
|
||||
self._name,
|
||||
res_id
|
||||
)['value']
|
||||
composer.write(update_values)
|
||||
return composer._action_send_mail(auto_commit=auto_commit)
|
||||
mails_su = self.env['mail.mail'].sudo()
|
||||
for subset in [self] if template else self:
|
||||
composer_ctx['active_ids'] = subset.ids # still required for mass mail mode
|
||||
if template:
|
||||
composer_ctx['default_res_id'] = subset.ids[0] if subset.ids else 0
|
||||
else:
|
||||
composer_values['body'] = bodies[subset.id]
|
||||
composer_ctx['default_res_id'] = subset.id
|
||||
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
**composer_ctx
|
||||
).create(composer_values)
|
||||
# Simulate the onchange (like trigger in form the view) only
|
||||
# when having a template in single-email mode
|
||||
if template:
|
||||
update_values = composer._onchange_template_id(
|
||||
template.id,
|
||||
composer_values['composition_mode'],
|
||||
self._name,
|
||||
subset.ids[0] if subset.ids else 0
|
||||
)['value']
|
||||
composer.write(update_values)
|
||||
|
||||
mails_as_sudo, _messages_as_sudo = composer._action_send_mail(auto_commit=auto_commit)
|
||||
mails_su += mails_as_sudo
|
||||
return mails_su
|
||||
|
||||
def message_post_with_source(self, source_ref, render_values=None,
|
||||
message_type='notification',
|
||||
subtype_xmlid=False, subtype_id=False,
|
||||
**kwargs):
|
||||
""" Post a message on each record of self, using a view to render the
|
||||
body using QWeb.
|
||||
|
||||
Default values
|
||||
* subtype_id: if not given, fallback on ``note`` to be consistent
|
||||
with what message_post does;
|
||||
|
||||
:param record/str source_ref: reference to a source for rendering.
|
||||
It can be one of
|
||||
* a MailTemplate record. It will be used to render the various
|
||||
message values (body, subject, recipients, ...). It should behave
|
||||
like using the mail composer with a template;
|
||||
* an IrUIView record. It will be used to render the content
|
||||
(body). Other fields are left to the caller and/or default values
|
||||
computation;
|
||||
* an XmlID of a MailTemplate or of an IrUiView: see above
|
||||
:param dict render_values: additional rendering values for qweb context;
|
||||
|
||||
:param str message_type: one of 'notification' or 'comment';
|
||||
:param str subtype_xmlid: optional xml id of a mail.message.subtype to
|
||||
fetch, will force value of subtype_id;
|
||||
:param int subtype_id: subtype_id of the message, used mainly for followers
|
||||
notification mechanism;
|
||||
:param dict kwargs: additional values given to the 'mail.compose.message'
|
||||
creation;
|
||||
|
||||
:return: posted mail.message records
|
||||
"""
|
||||
template, view = self._get_source_from_ref(source_ref)
|
||||
|
||||
# preliminary value safety check
|
||||
self._raise_for_invalid_parameters(
|
||||
set(kwargs.keys()),
|
||||
forbidden_names={'body', 'composition_mode', 'model', 'res_id', 'values'}
|
||||
)
|
||||
|
||||
# with a view, render bodies in batch (template is managed by composer)
|
||||
bodies = self.env['mail.render.mixin']._render_template_qweb_view(
|
||||
view,
|
||||
self._name,
|
||||
self.ids,
|
||||
add_context=render_values,
|
||||
) if view else {}
|
||||
|
||||
# Prepare composer values for creation
|
||||
if subtype_xmlid:
|
||||
subtype_id = self.env['ir.model.data']._xmlid_to_res_id(subtype_xmlid)
|
||||
if not subtype_id:
|
||||
subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
|
||||
|
||||
messages = self.env['mail.message']
|
||||
for record in self:
|
||||
if template:
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
active_ids=record.ids, # still required for mass mail mode
|
||||
default_composition_mode='comment',
|
||||
default_model=self._name,
|
||||
default_res_id=record.id,
|
||||
default_template_id=template.id,
|
||||
).create({
|
||||
'message_type': message_type,
|
||||
'subtype_id': subtype_id,
|
||||
**kwargs,
|
||||
})
|
||||
|
||||
# Simulate the onchange (like trigger in form the view) to do the
|
||||
# rendering in mono-record mode
|
||||
update_values = composer._onchange_template_id(
|
||||
template.id,
|
||||
'comment',
|
||||
self._name,
|
||||
record.id,
|
||||
)['value']
|
||||
composer.write(update_values)
|
||||
_mails_as_sudo, messages_as_sudo = composer._action_send_mail()
|
||||
messages += messages_as_sudo
|
||||
else:
|
||||
messages += record.message_post(
|
||||
body=bodies[record.id],
|
||||
message_type=message_type,
|
||||
subtype_id=subtype_id,
|
||||
**kwargs
|
||||
)
|
||||
return messages
|
||||
|
||||
def message_notify(self, *,
|
||||
partner_ids=False, parent_id=False, model=False, res_id=False,
|
||||
author_id=None, email_from=None, body='', subject=False,
|
||||
subtype_xmlid=None,
|
||||
**kwargs):
|
||||
""" Shortcut allowing to notify partners of messages that shouldn't be
|
||||
displayed on a document. It pushes notifications on inbox or by email depending
|
||||
on the user configuration, like other notifications. """
|
||||
""" Shortcut allowing to notify partners of messages that should not be
|
||||
displayed on a document. It pushes notifications on inbox or by email
|
||||
depending on the user configuration, like other notifications.
|
||||
|
||||
Default values
|
||||
* subtype_id: if not given, fallback on ``note`` to be consistent
|
||||
with what message_post does;
|
||||
|
||||
:return: posted mail.message records
|
||||
"""
|
||||
if self:
|
||||
self.ensure_one()
|
||||
if not partner_ids:
|
||||
@@ -2245,47 +2358,51 @@ class MailThread(models.AbstractModel):
|
||||
self._notify_thread(new_message, msg_values, **notif_kwargs)
|
||||
return new_message
|
||||
|
||||
def _message_log_with_view(self, views_or_xmlid, **kwargs):
|
||||
""" Helper method to log a note using a view_id without notifying followers. """
|
||||
return self._message_compose_with_view(views_or_xmlid, message_log=True, **kwargs)
|
||||
def _message_log_with_view(self, view_ref, render_values=None,
|
||||
message_type='notification', **kwargs):
|
||||
""" Log a message on each record of self, using a view to render the
|
||||
body using QWeb.
|
||||
|
||||
:param str/int/record view_ref: source QWeb template. It should be an
|
||||
XmlID allowing to fetch an ``ir.ui.view``, or an ID of a view or
|
||||
an ``ir.ui.view`` record;
|
||||
:param dict render_values: additional rendering values for qweb context;
|
||||
:param str message_type: one of 'notification' or 'comment';
|
||||
:param kwargs: additional values propagated to ``_message_log``;
|
||||
|
||||
:return: posted mail.message records (as sudo)
|
||||
"""
|
||||
self._raise_for_invalid_parameters(
|
||||
set(kwargs.keys()),
|
||||
forbidden_names={'body', 'bodies'}
|
||||
)
|
||||
|
||||
# with a view, render bodies in batch (template is managed by composer)
|
||||
bodies = self.env['mail.render.mixin']._render_template_qweb_view(
|
||||
view_ref,
|
||||
self._name,
|
||||
self.ids,
|
||||
add_context=render_values,
|
||||
)
|
||||
|
||||
return self._message_log_batch(
|
||||
bodies=bodies,
|
||||
message_type=message_type,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def _message_log(self, *, body='', author_id=None, email_from=None, subject=False,
|
||||
message_type='notification',
|
||||
attachment_ids=False, tracking_value_ids=False):
|
||||
""" Shortcut allowing to post note on a document. It does not perform
|
||||
any notification and pre-computes some values to have a short code
|
||||
as optimized as possible. This method is private as it does not check
|
||||
access rights and perform the message creation as sudo to speedup
|
||||
the log process. This method should be called within methods where
|
||||
access rights are already granted to avoid privilege escalation.
|
||||
|
||||
:return: created message (as sudo)"""
|
||||
""" Shortcut allowing to post note on a document. See ``_message_log_batch``
|
||||
for more details. """
|
||||
self.ensure_one()
|
||||
author_id, email_from = self._message_compute_author(author_id, email_from, raise_on_email=False)
|
||||
|
||||
msg_values = {
|
||||
# author
|
||||
'author_id': author_id,
|
||||
'email_from': email_from,
|
||||
# document
|
||||
'model': self._name,
|
||||
'record_name': False,
|
||||
'res_id': self.id,
|
||||
# content
|
||||
'attachment_ids': attachment_ids,
|
||||
'body': body,
|
||||
'is_internal': True,
|
||||
'message_type': message_type,
|
||||
'subject': subject,
|
||||
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
'tracking_value_ids': tracking_value_ids,
|
||||
# recipients
|
||||
'email_add_signature': False, # False as no notification -> no need to compute signature
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False],
|
||||
}
|
||||
|
||||
return self.sudo()._message_create([msg_values])
|
||||
return self._message_log_batch(
|
||||
{self.id: body}, author_id=author_id, email_from=email_from,
|
||||
subject=subject, message_type=message_type,
|
||||
attachment_ids=attachment_ids, tracking_value_ids=tracking_value_ids
|
||||
)
|
||||
|
||||
def _message_log_batch(self, bodies, author_id=None, email_from=None, subject=False,
|
||||
message_type='notification',
|
||||
@@ -2326,6 +2443,7 @@ class MailThread(models.AbstractModel):
|
||||
'message_id': tools.generate_tracking_message_id('message-notify'), # why? this is all but a notify
|
||||
'reply_to': self.env['mail.thread']._notify_get_reply_to(default=email_from)[False],
|
||||
}
|
||||
|
||||
values_list = [dict(base_message_values,
|
||||
res_id=record.id,
|
||||
body=bodies.get(record.id, ''))
|
||||
@@ -2401,6 +2519,8 @@ class MailThread(models.AbstractModel):
|
||||
return self.name_get()[0][1]
|
||||
|
||||
def _message_create(self, values_list):
|
||||
""" Low-level helper to create mail.message records. It is mainly used
|
||||
to hide the cleanup of given values, for mail gateway or helpers."""
|
||||
create_values_list = []
|
||||
|
||||
# preliminary value safety check
|
||||
@@ -2455,6 +2575,61 @@ class MailThread(models.AbstractModel):
|
||||
'tracking_value_ids',
|
||||
}
|
||||
|
||||
def _get_source_from_ref(self, source_ref):
|
||||
""" From a source_reference, return either a mail template, either
|
||||
an ir ui view.
|
||||
|
||||
:return tuple(template, view): one is a recordset (may be void if
|
||||
source_ref is a void recordset, or a singleton), the other one is
|
||||
False. Always only one is set, as source is either a template,
|
||||
either a view.
|
||||
"""
|
||||
template, view = False, False
|
||||
if isinstance(source_ref, models.BaseModel):
|
||||
if source_ref._name == 'mail.template':
|
||||
template = source_ref
|
||||
elif source_ref._name == 'ir.ui.view':
|
||||
view = source_ref
|
||||
else:
|
||||
raise ValueError(
|
||||
_('Invalid template or view source record %(svalue)s, is %(model)s instead',
|
||||
svalue=source_ref,
|
||||
model=source_ref._name,
|
||||
))
|
||||
if not template and not view:
|
||||
raise ValueError(
|
||||
_('Mailing or posting with a source should not be called with an empty %(source_type)s',
|
||||
source_type=_('template') if template is not False else _('view'))
|
||||
)
|
||||
elif isinstance(source_ref, str):
|
||||
try:
|
||||
res_model, res_id = self.env['ir.model.data']._xmlid_to_res_model_res_id(
|
||||
source_ref,
|
||||
raise_if_not_found=True
|
||||
)
|
||||
except ValueError as e:
|
||||
raise ValueError(
|
||||
_('Invalid template or view source Xml ID %(source_ref)s does not exist anymore',
|
||||
source_ref=source_ref)
|
||||
) from e
|
||||
if res_model == 'mail.template':
|
||||
template = self.env['mail.template'].browse(res_id)
|
||||
elif res_model == 'ir.ui.view':
|
||||
view = self.env['ir.ui.view'].browse(res_id)
|
||||
else:
|
||||
raise ValueError(
|
||||
_('Invalid template or view source reference %(svalue)s, is %(model)s instead',
|
||||
svalue=source_ref,
|
||||
model=res_model,
|
||||
))
|
||||
else:
|
||||
raise ValueError(
|
||||
_('Invalid template or view source %(svalue)s (type %(stype)s), should be a record or an XMLID',
|
||||
svalue=source_ref,
|
||||
stype=type(source_ref),
|
||||
))
|
||||
return template, view
|
||||
|
||||
def _get_notify_valid_parameters(self):
|
||||
""" Several parameters exist for notification methods as business
|
||||
flows often want to customize the standard notification experience.
|
||||
|
||||
@@ -314,7 +314,7 @@ class MailGroup(models.Model):
|
||||
values
|
||||
))
|
||||
|
||||
mail_message = Mailthread._message_create(values)
|
||||
mail_message = Mailthread._message_create([values])
|
||||
|
||||
# Find the <mail.group.message> parent
|
||||
group_message_parent_id = False
|
||||
|
||||
@@ -117,9 +117,9 @@ class MailPluginController(http.Controller):
|
||||
|
||||
partner.write(partner_values)
|
||||
|
||||
partner.message_post_with_view(
|
||||
partner.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=iap_data,
|
||||
render_values=iap_data,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
@@ -357,9 +357,9 @@ class MailPluginController(http.Controller):
|
||||
|
||||
new_company = request.env['res.partner'].create(new_company_info)
|
||||
|
||||
new_company.message_post_with_view(
|
||||
new_company.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=iap_data,
|
||||
render_values=iap_data,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -28,13 +28,21 @@ class MailThread(models.AbstractModel):
|
||||
self.env['mailing.trace'].set_replied(domain=[('message_id', 'in', msg_references)])
|
||||
return super(MailThread, self)._message_route_process(message, message_dict, routes)
|
||||
|
||||
def message_post_with_template(self, template_id, **kwargs):
|
||||
def message_mail_with_source(self, source_ref, **kwargs):
|
||||
# avoid having message send through `message_post*` methods being implicitly considered as
|
||||
# mass-mailing
|
||||
return super(MailThread, self.with_context(
|
||||
default_mass_mailing_name=False,
|
||||
default_mass_mailing_id=False,
|
||||
)).message_post_with_template(template_id, **kwargs)
|
||||
)).message_mail_with_source(source_ref, **kwargs)
|
||||
|
||||
def message_post_with_source(self, source_ref, **kwargs):
|
||||
# avoid having message send through `message_post*` methods being implicitly considered as
|
||||
# mass-mailing
|
||||
return super(MailThread, self.with_context(
|
||||
default_mass_mailing_name=False,
|
||||
default_mass_mailing_id=False,
|
||||
)).message_post_with_source(source_ref, **kwargs)
|
||||
|
||||
@api.model
|
||||
def _routing_handle_bounce(self, email_message, message_dict):
|
||||
|
||||
@@ -65,15 +65,15 @@ class StockRule(models.Model):
|
||||
subtype_id=note_subtype_id
|
||||
)
|
||||
elif orderpoint:
|
||||
production.message_post_with_view(
|
||||
production.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': production, 'origin': orderpoint},
|
||||
render_values={'self': production, 'origin': orderpoint},
|
||||
subtype_id=note_subtype_id,
|
||||
)
|
||||
elif origin_production:
|
||||
production.message_post_with_view(
|
||||
production.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': production, 'origin': origin_production},
|
||||
render_values={'self': production, 'origin': origin_production},
|
||||
subtype_id=note_subtype_id,
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -81,9 +81,9 @@ class ResCompany(models.Model):
|
||||
if additional_data:
|
||||
template_values = json.loads(additional_data)
|
||||
template_values['flavor_text'] = _("Company auto-completed by Odoo Partner Autocomplete Service")
|
||||
self.partner_id.message_post_with_view(
|
||||
self.partner_id.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=template_values,
|
||||
render_values=template_values,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -166,9 +166,9 @@ class ResPartner(models.Model):
|
||||
if partners.additional_info:
|
||||
template_values = json.loads(partners.additional_info)
|
||||
template_values['flavor_text'] = _("Partner created by Odoo Partner Autocomplete Service")
|
||||
partners.message_post_with_view(
|
||||
partners.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=template_values,
|
||||
render_values=template_values,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
partners.write({'additional_info': False})
|
||||
|
||||
@@ -64,14 +64,14 @@ class PortalShare(models.TransientModel):
|
||||
share_link = self.resource_ref.get_base_url() + self.resource_ref._get_share_url(redirect=True, pid=partner.id)
|
||||
saved_lang = self.env.lang
|
||||
self = self.with_context(lang=partner.lang)
|
||||
template = self.env.ref('portal.portal_share_template', False)
|
||||
self.resource_ref.message_post_with_view(template,
|
||||
values={'partner': partner, 'note': self.note, 'record': self.resource_ref,
|
||||
self.resource_ref.message_post_with_source(
|
||||
'portal.portal_share_template',
|
||||
render_values={'partner': partner, 'note': self.note, 'record': self.resource_ref,
|
||||
'share_link': share_link},
|
||||
subject=_("You are invited to access %s", self.resource_ref.display_name),
|
||||
subtype_xmlid='mail.mt_note',
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
partner_ids=[(6, 0, partner.ids)])
|
||||
partner_ids=partner.ids)
|
||||
self = self.with_context(lang=saved_lang)
|
||||
|
||||
def _send_signup_link(self, partners=None):
|
||||
@@ -83,14 +83,14 @@ class PortalShare(models.TransientModel):
|
||||
share_link = partner._get_signup_url_for_action(action='/mail/view', res_id=self.res_id, model=self.res_model)[partner.id]
|
||||
saved_lang = self.env.lang
|
||||
self = self.with_context(lang=partner.lang)
|
||||
template = self.env.ref('portal.portal_share_template', False)
|
||||
self.resource_ref.message_post_with_view(template,
|
||||
values={'partner': partner, 'note': self.note, 'record': self.resource_ref,
|
||||
self.resource_ref.message_post_with_source(
|
||||
'portal.portal_share_template',
|
||||
render_values={'partner': partner, 'note': self.note, 'record': self.resource_ref,
|
||||
'share_link': share_link},
|
||||
subject=_("You are invited to access %s", self.resource_ref.display_name),
|
||||
subtype_xmlid='mail.mt_note',
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
partner_ids=[(6, 0, partner.ids)])
|
||||
partner_ids=partner.ids)
|
||||
self = self.with_context(lang=saved_lang)
|
||||
|
||||
def action_send_mail(self):
|
||||
|
||||
@@ -18,9 +18,8 @@ class AccountMove(models.Model):
|
||||
comment_subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
|
||||
for line in invoice.invoice_line_ids:
|
||||
if line.product_id.email_template_id:
|
||||
invoice.message_post_with_template(
|
||||
line.product_id.email_template_id.id,
|
||||
composition_mode="comment",
|
||||
invoice.message_post_with_source(
|
||||
line.product_id.email_template_id,
|
||||
email_layout_xmlid="mail.mail_notification_light",
|
||||
subtype_id=comment_subtype_id,
|
||||
)
|
||||
|
||||
@@ -750,8 +750,8 @@ class PurchaseOrder(models.Model):
|
||||
if send_single:
|
||||
return order._send_reminder_open_composer(template.id)
|
||||
else:
|
||||
order.with_context(is_reminder=True).message_post_with_template(
|
||||
template.id,
|
||||
order.with_context(is_reminder=True).message_post_with_source(
|
||||
template,
|
||||
email_layout_xmlid="mail.mail_notification_layout_with_responsible_signature",
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
@@ -1104,9 +1104,9 @@ class PurchaseOrderLine(models.Model):
|
||||
if 'product_qty' in values:
|
||||
for line in self:
|
||||
if line.order_id.state == 'purchase':
|
||||
line.order_id.message_post_with_view(
|
||||
line.order_id.message_post_with_source(
|
||||
'purchase.track_po_line_template',
|
||||
values={'line': line, 'product_qty': values['product_qty']},
|
||||
render_values={'line': line, 'product_qty': values['product_qty']},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
if 'qty_received' in values:
|
||||
@@ -1425,9 +1425,9 @@ class PurchaseOrderLine(models.Model):
|
||||
def _track_qty_received(self, new_qty):
|
||||
self.ensure_one()
|
||||
if new_qty != self.qty_received and self.order_id.state == 'purchase':
|
||||
self.order_id.message_post_with_view(
|
||||
self.order_id.message_post_with_source(
|
||||
'purchase.track_po_line_qty_received_template',
|
||||
values={'line': self, 'qty_received': new_qty},
|
||||
render_values={'line': self, 'qty_received': new_qty},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -144,9 +144,9 @@ class PurchaseOrder(models.Model):
|
||||
self.env['purchase.order.group'].create({'order_ids': [Command.set(origin_po_id.ids + orders.ids)]})
|
||||
for order in orders:
|
||||
if order.requisition_id:
|
||||
order.message_post_with_view(
|
||||
order.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': order, 'origin': order.requisition_id},
|
||||
render_values={'self': order, 'origin': order.requisition_id},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
return orders
|
||||
@@ -158,9 +158,9 @@ class PurchaseOrder(models.Model):
|
||||
result = super(PurchaseOrder, self).write(vals)
|
||||
if vals.get('requisition_id'):
|
||||
for order in self:
|
||||
order.message_post_with_view(
|
||||
order.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': order, 'origin': order.requisition_id, 'edit': True},
|
||||
render_values={'self': order, 'origin': order.requisition_id, 'edit': True},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
if vals.get('alternative_po_ids', False):
|
||||
|
||||
@@ -251,8 +251,9 @@ class PurchaseOrder(models.Model):
|
||||
# Get following pickings (created by push rules) to confirm them as well.
|
||||
forward_pickings = self.env['stock.picking']._get_impacted_pickings(moves)
|
||||
(pickings | forward_pickings).action_confirm()
|
||||
picking.message_post_with_view('mail.message_origin_link',
|
||||
values={'self': picking, 'origin': order},
|
||||
picking.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
render_values={'self': picking, 'origin': order},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -181,12 +181,11 @@ class RatingMixin(models.AbstractModel):
|
||||
"""
|
||||
if lang:
|
||||
template = template.with_context(lang=lang)
|
||||
for record in self.with_context(mail_notify_force_send=force_send):
|
||||
record.message_post_with_template(
|
||||
template.id,
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
self.with_context(mail_notify_force_send=force_send).message_post_with_source(
|
||||
template,
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
def rating_apply(self, rate, token=None, rating=None, feedback=None,
|
||||
subtype_xmlid=None, notify_delay_send=False):
|
||||
|
||||
@@ -131,6 +131,9 @@ class PaymentTransaction(models.Model):
|
||||
))
|
||||
if not template_id:
|
||||
return
|
||||
template = self.env['mail.template'].browse(template_id).exists()
|
||||
if not template:
|
||||
return
|
||||
|
||||
for tx in self:
|
||||
tx = tx.with_company(tx.company_id).with_context(
|
||||
@@ -140,12 +143,11 @@ class PaymentTransaction(models.Model):
|
||||
lambda i: not i.is_move_sent and i.state == 'posted' and i._is_ready_to_be_sent()
|
||||
)
|
||||
invoice_to_send.is_move_sent = True # Mark invoice as sent
|
||||
for invoice in invoice_to_send.with_user(SUPERUSER_ID):
|
||||
invoice.message_post_with_template(
|
||||
template_id,
|
||||
email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
invoice_to_send.with_user(SUPERUSER_ID).message_post_with_source(
|
||||
template,
|
||||
email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
|
||||
def _cron_send_invoice(self):
|
||||
"""
|
||||
|
||||
@@ -842,8 +842,8 @@ class SaleOrder(models.Model):
|
||||
mail_template = sale_order._get_confirmation_template()
|
||||
if not mail_template:
|
||||
continue
|
||||
sale_order.with_context(force_send=True).message_post_with_template(
|
||||
mail_template.id,
|
||||
sale_order.with_context(force_send=True).message_post_with_source(
|
||||
mail_template,
|
||||
email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
@@ -1181,9 +1181,9 @@ class SaleOrder(models.Model):
|
||||
if final:
|
||||
moves.sudo().filtered(lambda m: m.amount_total < 0).action_switch_move_type()
|
||||
for move in moves:
|
||||
move.message_post_with_view(
|
||||
move.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': move, 'origin': move.line_ids.sale_line_ids.order_id},
|
||||
render_values={'self': move, 'origin': move.line_ids.sale_line_ids.order_id},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
return moves
|
||||
|
||||
@@ -87,8 +87,8 @@ class TestSaleSignature(HttpCaseWithUserPortal):
|
||||
# must be sent to the user so he can see it
|
||||
email_act = sales_order.action_quotation_send()
|
||||
email_ctx = email_act.get('context', {})
|
||||
sales_order.with_context(**email_ctx).message_post_with_template(
|
||||
email_ctx.get('default_template_id'),
|
||||
sales_order.with_context(**email_ctx).message_post_with_source(
|
||||
self.env['mail.template'].browse(email_ctx.get('default_template_id')),
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ class TestSaleOrder(SaleCommon):
|
||||
# send quotation
|
||||
email_act = self.sale_order.action_quotation_send()
|
||||
email_ctx = email_act.get('context', {})
|
||||
self.sale_order.with_context(**email_ctx).message_post_with_template(
|
||||
email_ctx.get('default_template_id'),
|
||||
self.sale_order.with_context(**email_ctx).message_post_with_source(
|
||||
self.env['mail.template'].browse(email_ctx.get('default_template_id')),
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
self.assertTrue(self.sale_order.state == 'sent', 'Sale: state after sending is wrong')
|
||||
@@ -104,8 +104,8 @@ class TestSaleOrder(SaleCommon):
|
||||
# sent to to author or not (in case author is present in 'Recipients' of composer).
|
||||
mail_template = self.env['mail.template'].browse(email_ctx.get('default_template_id')).copy({'auto_delete': False})
|
||||
# send the mail with same user as customer
|
||||
sale_order.with_context(**email_ctx).with_user(self.sale_user).message_post_with_template(
|
||||
mail_template.id,
|
||||
sale_order.with_context(**email_ctx).with_user(self.sale_user).message_post_with_source(
|
||||
mail_template,
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
self.assertTrue(sale_order.state == 'sent', 'Sale : state should be changed to sent')
|
||||
|
||||
@@ -632,8 +632,8 @@ class TestSaleToInvoice(TestSaleCommon):
|
||||
# send quotation
|
||||
email_act = self.sale_order.action_quotation_send()
|
||||
email_ctx = email_act.get('context', {})
|
||||
self.sale_order.with_context(**email_ctx).message_post_with_template(
|
||||
email_ctx.get('default_template_id'),
|
||||
self.sale_order.with_context(**email_ctx).message_post_with_source(
|
||||
self.env['mail.template'].browse(email_ctx.get('default_template_id')),
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
self.assertTrue(self.sale_order.state == 'sent', 'Sale: state after sending is wrong')
|
||||
|
||||
@@ -186,9 +186,9 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
self._prepare_invoice_values(order, down_payment_so_line)
|
||||
).with_user(self.env.uid) # Unsudo the invoice after creation
|
||||
|
||||
invoice.message_post_with_view(
|
||||
invoice.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': invoice, 'origin': order},
|
||||
render_values={'self': invoice, 'origin': order},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ class StockMove(models.Model):
|
||||
picking_id = self.mapped('picking_id')
|
||||
sale_order_ids = self.mapped('sale_line_id.order_id')
|
||||
for sale_order_id in sale_order_ids:
|
||||
picking_id.message_post_with_view(
|
||||
picking_id.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': picking_id, 'origin': sale_order_id},
|
||||
render_values={'self': picking_id, 'origin': sale_order_id},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -310,7 +310,10 @@ class TestSaleStockMargin(TestStockValuationCommon):
|
||||
so = so_form.save()
|
||||
email_act = so.action_quotation_send()
|
||||
email_ctx = email_act.get('context', {})
|
||||
so.with_context(**email_ctx).message_post_with_template(email_ctx.get('default_template_id'))
|
||||
so.with_context(**email_ctx).message_post_with_source(
|
||||
self.env['mail.template'].browse(email_ctx.get('default_template_id')),
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
)
|
||||
|
||||
self.assertEqual(so.state, 'sent')
|
||||
self.assertEqual(so.order_line[0].purchase_price, 15)
|
||||
|
||||
@@ -667,9 +667,9 @@ class StockMoveLine(models.Model):
|
||||
data['result_package_name'] = self.env['stock.quant.package'].browse(vals.get('result_package_id')).name
|
||||
if 'owner_id' in vals and vals['owner_id'] != move.owner_id.id:
|
||||
data['owner_name'] = self.env['res.partner'].browse(vals.get('owner_id')).name
|
||||
record.message_post_with_view(
|
||||
record.message_post_with_source(
|
||||
template,
|
||||
values={'move': move, 'vals': dict(vals, **data)},
|
||||
render_values={'move': move, 'vals': dict(vals, **data)},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -886,9 +886,9 @@ class Picking(models.Model):
|
||||
def _send_confirmation_email(self):
|
||||
subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
|
||||
for stock_pick in self.filtered(lambda p: p.company_id.stock_move_email_validation and p.picking_type_id.code == 'outgoing'):
|
||||
delivery_template_id = stock_pick.company_id.stock_mail_confirmation_template_id.id
|
||||
stock_pick.with_context(force_send=True).message_post_with_template(
|
||||
delivery_template_id,
|
||||
delivery_template = stock_pick.company_id.stock_mail_confirmation_template_id
|
||||
stock_pick.with_context(force_send=True).message_post_with_source(
|
||||
delivery_template,
|
||||
email_layout_xmlid='mail.mail_notification_light',
|
||||
subtype_id=subtype_id,
|
||||
)
|
||||
|
||||
@@ -133,9 +133,9 @@ class ReturnPicking(models.TransientModel):
|
||||
# create new picking for returned products
|
||||
new_picking = self.picking_id.copy(self._prepare_picking_default_values())
|
||||
picking_type_id = new_picking.picking_type_id.id
|
||||
new_picking.message_post_with_view(
|
||||
new_picking.message_post_with_source(
|
||||
'mail.message_origin_link',
|
||||
values={'self': new_picking, 'origin': self.picking_id},
|
||||
render_values={'self': new_picking, 'origin': self.picking_id},
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
returned_lines = 0
|
||||
|
||||
@@ -404,6 +404,7 @@ class TestMessageNotify(TestMessagePostCommon):
|
||||
)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.tests')
|
||||
def test_notify_parameters(self):
|
||||
""" Test usage of parameters in notify, both for unwanted side effects
|
||||
and magic parameters. """
|
||||
@@ -536,7 +537,7 @@ class TestMessageLog(TestMessagePostCommon):
|
||||
|
||||
new_notes = test_records._message_log_with_view(
|
||||
'test_mail.mail_template_simple_test',
|
||||
values={'partner': self.user_employee.partner_id}
|
||||
render_values={'partner': self.user_employee.partner_id}
|
||||
)
|
||||
for test_record, new_note in zip(test_records, new_notes):
|
||||
self.assertMessageFields(
|
||||
@@ -1031,6 +1032,30 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
# Force the attachments of the template to be in the natural order.
|
||||
cls.test_template.invalidate_recordset(['attachment_ids'])
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_helpers_source_ref(self):
|
||||
""" Test various sources (record or xml id) to ensure source_ref right
|
||||
computation. """
|
||||
test_records = self.test_records.with_env(self.env)
|
||||
template = self.test_template.with_env(self.env)
|
||||
view = self.env.ref('test_mail.mail_template_simple_test')
|
||||
|
||||
for source_ref in ('test_mail.mail_test_ticket_tracking_tpl', template,
|
||||
'test_mail.mail_template_simple_test', view):
|
||||
with self.subTest(source_ref=source_ref), self.mock_mail_gateway():
|
||||
_new_mails = test_records.with_user(self.user_employee).message_mail_with_source(
|
||||
source_ref,
|
||||
render_values={'partner': self.user_employee.partner_id},
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
)
|
||||
|
||||
_new_messages = test_records.with_user(self.user_employee).message_post_with_source(
|
||||
source_ref,
|
||||
render_values={'partner': self.user_employee.partner_id},
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_mail_with_template(self):
|
||||
@@ -1038,9 +1063,8 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
test_records = self.test_records.with_env(self.env)
|
||||
template = self.test_template.with_env(self.env)
|
||||
with self.mock_mail_gateway():
|
||||
_new_mails, _new_messages = test_records.with_user(self.user_employee).message_post_with_template(
|
||||
template.id,
|
||||
composition_mode='mass_mail',
|
||||
_new_mails = test_records.with_user(self.user_employee).message_mail_with_source(
|
||||
template,
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
)
|
||||
|
||||
@@ -1086,14 +1110,13 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
test_record.message_subscribe(test_record.customer_id.ids)
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
new_messages = test_records.message_post_with_view(
|
||||
new_mails = test_records.message_mail_with_source(
|
||||
'test_mail.mail_template_simple_test',
|
||||
values={'partner': self.user_employee.partner_id},
|
||||
composition_mode='mass_mail',
|
||||
render_values={'partner': self.user_employee.partner_id},
|
||||
subject='About mass mailing',
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
|
||||
)
|
||||
self.assertEqual(len(new_messages), 0)
|
||||
self.assertEqual(len(new_mails), 10)
|
||||
self.assertEqual(len(self._new_mails), 10)
|
||||
|
||||
# sent emails (mass mail mode)
|
||||
@@ -1120,6 +1143,19 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
}
|
||||
)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_post_with_source_subtype(self):
|
||||
""" Test subtype tweaks when posting with a source """
|
||||
test_record = self.test_records.with_env(self.env)[0]
|
||||
test_template = self.test_template.with_env(self.env)
|
||||
with self.mock_mail_gateway():
|
||||
new_message = test_record.with_user(self.user_employee).message_post_with_source(
|
||||
test_template,
|
||||
subtype_xmlid='mail.mt_activities',
|
||||
)
|
||||
self.assertEqual(new_message.subtype_id, self.env.ref("mail.mt_activities"))
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_post_with_template(self):
|
||||
@@ -1128,9 +1164,8 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
test_record.message_subscribe(test_record.customer_id.ids)
|
||||
test_template = self.test_template.with_env(self.env)
|
||||
with self.mock_mail_gateway():
|
||||
_new_mail, new_message = test_record.with_user(self.user_employee).message_post_with_template(
|
||||
test_template.id,
|
||||
composition_mode='comment',
|
||||
new_message = test_record.with_user(self.user_employee).message_post_with_source(
|
||||
test_template,
|
||||
message_type='comment',
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
)
|
||||
@@ -1165,15 +1200,15 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_post_with_template_defaults(self):
|
||||
""" Test default values, notably subtype being a comment """
|
||||
test_record = self.test_records.with_env(self.env)[0]
|
||||
test_record.message_subscribe(test_record.customer_id.ids)
|
||||
test_template = self.test_template.with_env(self.env)
|
||||
with self.mock_mail_gateway():
|
||||
_new_mail, new_message = test_record.with_user(self.user_employee).message_post_with_template(
|
||||
test_template.id,
|
||||
new_message = test_record.with_user(self.user_employee).message_post_with_source(
|
||||
test_template,
|
||||
)
|
||||
|
||||
# created partners from inline email addresses
|
||||
@@ -1192,7 +1227,7 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
{'partner': new_partners[1], 'type': 'email'},
|
||||
{'partner': test_record.customer_id, 'type': 'email'},
|
||||
],
|
||||
'subtype': 'mail.mt_comment',
|
||||
'subtype': 'mail.mt_note',
|
||||
}])
|
||||
self.assertMessageFields(
|
||||
new_message,
|
||||
@@ -1213,10 +1248,10 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
test_record.message_subscribe(test_record.customer_id.ids)
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
new_message = test_record.message_post_with_view(
|
||||
new_message = test_record.message_post_with_source(
|
||||
'test_mail.mail_template_simple_test',
|
||||
message_type='comment',
|
||||
values={'partner': self.user_employee.partner_id},
|
||||
render_values={'partner': self.user_employee.partner_id},
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
)
|
||||
|
||||
@@ -1250,9 +1285,9 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
|
||||
# defaults is a note, take into account specified recipients
|
||||
with self.mock_mail_gateway():
|
||||
new_message = test_record.message_post_with_view(
|
||||
new_message = test_record.message_post_with_source(
|
||||
'test_mail.mail_template_simple_test',
|
||||
values={'partner': self.user_employee.partner_id},
|
||||
render_values={'partner': self.user_employee.partner_id},
|
||||
partner_ids=test_record.customer_id.ids,
|
||||
)
|
||||
|
||||
@@ -1263,7 +1298,7 @@ class TestMessagePostHelpers(TestMessagePostCommon):
|
||||
'notif': [
|
||||
{'partner': test_record.customer_id, 'type': 'email'},
|
||||
],
|
||||
'subtype': 'mail.mt_comment',
|
||||
'subtype': 'mail.mt_note',
|
||||
}])
|
||||
self.assertMessageFields(
|
||||
new_message,
|
||||
@@ -1340,9 +1375,8 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
|
||||
test_template = self.test_template.with_user(self.env.user)
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
test_record.message_post_with_template(
|
||||
test_template.id,
|
||||
composition_mode='comment',
|
||||
test_record.message_post_with_source(
|
||||
test_template,
|
||||
email_layout_xmlid='mail.test_layout',
|
||||
message_type='comment',
|
||||
subtype_id=self.env.ref('mail.mt_comment').id,
|
||||
@@ -1382,9 +1416,8 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
|
||||
test_template = self.test_template.with_user(self.env.user)
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
test_records.message_post_with_template(
|
||||
test_template.id,
|
||||
composition_mode='mass_mail',
|
||||
test_records.message_mail_with_source(
|
||||
test_template,
|
||||
email_layout_xmlid='mail.test_layout',
|
||||
message_type='comment',
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
@@ -1448,13 +1481,12 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
|
||||
test_template = self.test_template.with_user(self.env.user)
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
for test_record in test_records:
|
||||
test_record.message_post_with_template(
|
||||
test_template.id,
|
||||
email_layout_xmlid='mail.test_layout',
|
||||
message_type='comment',
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
)
|
||||
test_records.message_post_with_source(
|
||||
test_template,
|
||||
email_layout_xmlid='mail.test_layout',
|
||||
message_type='comment',
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
)
|
||||
|
||||
record0_customer = self.env['res.partner'].search([('email_normalized', '=', 'test.record.1@test.customer.com')], limit=1)
|
||||
self.assertTrue(record0_customer, 'Template usage should have created a contact based on record email')
|
||||
|
||||
@@ -150,7 +150,7 @@ class TestTracking(TestMailCommon):
|
||||
|
||||
def patched_message_track_post_template(*args, **kwargs):
|
||||
if args[0]._name == "mail.test.track":
|
||||
args[0].message_post_with_template(template.id)
|
||||
args[0].message_post_with_source(template)
|
||||
return True
|
||||
|
||||
with patch('odoo.addons.mail.models.mail_thread.MailThread._message_track_post_template', patched_message_track_post_template):
|
||||
|
||||
@@ -621,7 +621,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
with self.assertQueryCount(__system__=11, employee=11):
|
||||
records._message_log_with_view(
|
||||
'test_mail.mail_template_simple_test',
|
||||
values={'partner': self.customer.with_env(self.env)}
|
||||
render_values={'partner': self.customer.with_env(self.env)}
|
||||
)
|
||||
|
||||
@users('__system__', 'employee')
|
||||
@@ -862,14 +862,13 @@ class TestMailComplexPerformance(BaseMailPerformance):
|
||||
def test_complex_message_post_template(self):
|
||||
self.container.message_subscribe(self.user_portal.partner_id.ids)
|
||||
record = self.container.with_user(self.env.user)
|
||||
template_id = self.env.ref('test_mail.mail_test_container_tpl').id
|
||||
template = self.env.ref('test_mail.mail_test_container_tpl')
|
||||
|
||||
# about 20 (19 ?) queries per additional customer group
|
||||
with self.assertQueryCount(__system__=42, employee=43):
|
||||
record.message_post_with_template(
|
||||
template_id,
|
||||
record.message_post_with_source(
|
||||
template,
|
||||
message_type='comment',
|
||||
composition_mode='comment',
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
|
||||
)
|
||||
|
||||
@@ -892,9 +891,9 @@ class TestMailComplexPerformance(BaseMailPerformance):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
with self.assertQueryCount(__system__=121, employee=141):
|
||||
messages_as_sudo = test_records.message_post_with_view(
|
||||
messages_as_sudo = test_records.message_post_with_source(
|
||||
'test_mail.mail_template_simple_test',
|
||||
values={'partner': self.user_test.partner_id},
|
||||
render_values={'partner': self.user_test.partner_id},
|
||||
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
|
||||
)
|
||||
|
||||
|
||||
@@ -221,10 +221,10 @@ class BlogPost(models.Model):
|
||||
def _check_for_publication(self, vals):
|
||||
if vals.get('is_published'):
|
||||
for post in self.filtered(lambda p: p.active):
|
||||
post.blog_id.message_post_with_view(
|
||||
post.blog_id.message_post_with_source(
|
||||
'website_blog.blog_post_template_new_post',
|
||||
subject=post.name,
|
||||
values={'post': post},
|
||||
render_values={'post': post},
|
||||
subtype_xmlid='website_blog.mt_blog_blog_published',
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -376,9 +376,9 @@ class CRMRevealRule(models.Model):
|
||||
'flavor_text': _("Opportunity created by Odoo Lead Generation"),
|
||||
'people_data': result.get('people_data'),
|
||||
})
|
||||
lead.message_post_with_view(
|
||||
lead.message_post_with_source(
|
||||
'iap_mail.enrich_company',
|
||||
values=template_values,
|
||||
render_values=template_values,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
|
||||
@@ -406,9 +406,9 @@ class Track(models.Model):
|
||||
|
||||
post_values = {} if self.env.user.email else {'email_from': self.env.company.catchall_formatted}
|
||||
for track in tracks:
|
||||
track.event_id.message_post_with_view(
|
||||
track.event_id.message_post_with_source(
|
||||
'website_event_track.event_track_template_new',
|
||||
values={
|
||||
render_values={
|
||||
'track': track,
|
||||
'is_html_empty': is_html_empty,
|
||||
},
|
||||
|
||||
@@ -623,17 +623,17 @@ class Post(models.Model):
|
||||
tag_partners = post.tag_ids.sudo().mapped('message_partner_ids')
|
||||
|
||||
if post.state == 'active' and post.parent_id:
|
||||
post.parent_id.message_post_with_view(
|
||||
post.parent_id.message_post_with_source(
|
||||
'website_forum.forum_post_template_new_answer',
|
||||
subject=_('Re: %s', post.parent_id.name),
|
||||
partner_ids=[(4, p.id) for p in tag_partners],
|
||||
partner_ids=tag_partners.ids,
|
||||
subtype_xmlid='website_forum.mt_answer_new',
|
||||
)
|
||||
elif post.state == 'active' and not post.parent_id:
|
||||
post.message_post_with_view(
|
||||
post.message_post_with_source(
|
||||
'website_forum.forum_post_template_new_question',
|
||||
subject=post.name,
|
||||
partner_ids=[(4, p.id) for p in tag_partners],
|
||||
partner_ids=tag_partners.ids,
|
||||
subtype_xmlid='website_forum.mt_question_new',
|
||||
)
|
||||
elif post.state == 'pending' and not post.parent_id:
|
||||
@@ -642,7 +642,7 @@ class Post(models.Model):
|
||||
partners = post.sudo().message_partner_ids | tag_partners
|
||||
partners = partners.filtered(lambda partner: partner.user_ids and any(user.karma >= post.forum_id.karma_moderate for user in partner.user_ids))
|
||||
|
||||
post.message_post_with_view(
|
||||
post.message_post_with_source(
|
||||
'website_forum.forum_post_template_validation',
|
||||
subject=post.name,
|
||||
partner_ids=partners.ids,
|
||||
|
||||
Reference in New Issue
Block a user