[LINT] mail, various: use explicit subtype in message_post_{with_...}

RATIONALE

Purpose of this commit is to be explicit in subtype chosen when invoking the
message composer / calling message_post. As default value may not always be
clear, better be explicit in case the composer default value changes.

SPECIFICATIONS

Add explicit references to subtype when it is not obvious what will be the
final subtype, notably when using helpers (post_with_view or template which
uses the composer that is not crystal clear in its subtype management).

In this commit we also add support of XMLID-based subtype when invoking the
composer. A ``default_subtype_xmlid`` context key is transformed into a
``default_subtype_id``, to be used notably in JS where we cannot easily
use a ``ref``-like statement. Post API now also supports 'subytpe_xmlid'
argument allowing to give the xml id and ease calling the methods.

Use ``_xmlid_to_res_id`` to get directly the ID of subtypes in order to
avoid useless queries from ``ref`` that does an exists.

Also remove useless values given to post API, notably author_id that is by
default the current users' partner.

Task-2710804 (Mail: Clean MailThread Posting API)

Part-of: odoo/odoo#99482
This commit is contained in:
Thibault Delavallée
2023-01-17 20:58:33 +01:00
parent d6aa0050da
commit 418761e344
51 changed files with 214 additions and 104 deletions
@@ -41,6 +41,7 @@ class AccountInvoiceSend(models.TransientModel):
composer = self.env['mail.compose.message'].create({
'composition_mode': 'comment' if len(res_ids) == 1 else 'mass_mail',
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
})
res.update({
'invoice_ids': res_ids,
@@ -86,7 +87,8 @@ class AccountInvoiceSend(models.TransientModel):
if not self.composer_id:
self.composer_id = self.env['mail.compose.message'].create({
'composition_mode': 'comment' if len(res_ids) == 1 else 'mass_mail',
'template_id': self.template_id.id
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
'template_id': self.template_id.id,
})
else:
self.composer_id.composition_mode = 'comment' if len(res_ids) == 1 else 'mass_mail'
+1 -1
View File
@@ -1568,7 +1568,7 @@ class Lead(models.Model):
"opportunities": opportunities_tail,
"is_html_empty": is_html_empty,
},
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
def _format_properties(self):
+9 -4
View File
@@ -70,7 +70,8 @@ class Lead(models.Model):
if not normalized_email:
lead.message_post_with_view(
'crm_iap_enrich.mail_message_lead_enrich_no_email',
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
continue
email_domain = normalized_email.split('@')[1]
@@ -79,7 +80,8 @@ class Lead(models.Model):
lead.write({'iap_enrich_done': True})
lead.message_post_with_view(
'crm_iap_enrich.mail_message_lead_enrich_notfound',
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
else:
lead_emails[lead.id] = email_domain
@@ -126,7 +128,10 @@ 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('crm_iap_enrich.mail_message_lead_enrich_notfound', subtype_id=self.env.ref('mail.mt_note').id)
lead.message_post_with_view(
'crm_iap_enrich.mail_message_lead_enrich_notfound',
subtype_xmlid='mail.mt_note',
)
continue
values = {'iap_enrich_done': True}
@@ -159,7 +164,7 @@ class Lead(models.Model):
lead.message_post_with_view(
'iap_mail.enrich_company',
values=template_values,
subtype_id=self.env.ref('mail.mt_note').id
subtype_xmlid='mail.mt_note',
)
def _merge_get_fields_specific(self):
@@ -278,7 +278,11 @@ 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('iap_mail.enrich_company', values=messages_to_post[lead.reveal_id], subtype_id=self.env.ref('mail.mt_note').id)
lead.message_post_with_view(
'iap_mail.enrich_company',
values=messages_to_post[lead.reveal_id],
subtype_xmlid='mail.mt_note',
)
# Methods responsible for format response data into valid odoo lead data
@api.model
+1 -1
View File
@@ -105,7 +105,7 @@ class EventBooth(models.Model):
values={
'booth': booth,
},
subtype_id=self.env.ref('event_booth.mt_event_booth_booked').id,
subtype_xmlid='event_booth.mt_event_booth_booked',
)
def action_confirm(self, additional_values=None):
@@ -80,7 +80,8 @@ class EventRegistration(models.Model):
registration.message_post_with_view(
'mail.message_origin_link',
values={'self': registration, 'origin': registration.sale_order_id},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
return registrations
def write(self, vals):
+18 -10
View File
@@ -636,8 +636,11 @@ 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})
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',
)
@api.model
def get_expense_dashboard(self):
@@ -798,21 +801,21 @@ class HrExpense(models.Model):
# TDE TODO: seems louche, check to use notify
if expense.employee_id.user_id.partner_id:
expense.message_post(
body=body,
email_layout_xmlid='mail.mail_notification_light',
partner_ids=expense.employee_id.user_id.partner_id.ids,
subject='Re: %s' % msg_dict.get('subject', ''),
body=body,
subtype_id=self.env.ref('mail.mt_note').id,
email_layout_xmlid='mail.mail_notification_light',
subtype_xmlid='mail.mt_note',
)
else:
self.env['mail.mail'].sudo().create({
'email_from': self.env.user.email_formatted,
'author_id': self.env.user.partner_id.id,
'body_html': body,
'subject': 'Re: %s' % msg_dict.get('subject', ''),
'email_to': msg_dict.get('email_from', False),
'auto_delete': True,
'body_html': body,
'email_from': self.env.user.email_formatted,
'email_to': msg_dict.get('email_from', False),
'references': msg_dict.get('message_id'),
'subject': 'Re: %s' % msg_dict.get('subject', ''),
}).send()
@@ -1213,8 +1216,13 @@ class HrExpenseSheet(models.Model):
raise UserError(_("You can only refuse your department expenses"))
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('hr_expense.hr_expense_template_refuse_reason', values={'reason': reason, 'is_sheet': True, 'name': sheet.name})
sheet.message_post_with_view(
'hr_expense.hr_expense_template_refuse_reason',
subtype_id=subtype_id,
values={'reason': reason, 'is_sheet': True, 'name': sheet.name},
)
self.activity_update()
def reset_expense_sheets(self):
+2 -2
View File
@@ -1,7 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.tools.translate import _
from datetime import timedelta
@@ -31,5 +30,6 @@ class HrEmployee(models.Model):
employee.applicant_id.message_post_with_view(
'hr_recruitment.applicant_hired_template',
values={'applicant': employee.applicant_id},
subtype_id=self.env.ref("hr_recruitment.mt_applicant_hired").id)
subtype_xmlid='hr_recruitment.mt_applicant_hired',
)
return employees
@@ -45,8 +45,9 @@ 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,
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
'email_layout_xmlid': 'mail.mail_notification_light'
})
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',
)
@@ -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):
subtype_note = self.env.ref('mail.mt_note')
new_leave.message_post_with_view(
'mail.message_origin_link',
values={'self': new_leave, 'origin': all_new_leave_origin[index]},
subtype_id=subtype_note.id)
subtype_xmlid='mail.mt_note',
)
return super(HrContract, self - specific_contracts).write(vals)
+1 -1
View File
@@ -581,7 +581,7 @@ class MailActivity(models.Model):
'feedback': feedback,
'display_assignee': activity.user_id != self.env.user
},
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_activities'),
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 [],
)
+2 -1
View File
@@ -345,7 +345,8 @@ class MailActivityMixin(models.AbstractModel):
for record in self:
record.message_post_with_template(
template_id,
composition_mode='comment'
composition_mode='comment',
subtype_xmlid='mail.mt_comment',
)
return True
+2 -2
View File
@@ -81,7 +81,7 @@ class MailBlackList(models.Model):
if message:
record.with_context(mail_create_nosubscribe=True).message_post(
body=message,
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
return record
@@ -97,7 +97,7 @@ class MailBlackList(models.Model):
if message:
record.with_context(mail_create_nosubscribe=True).message_post(
body=message,
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
return record
+10 -2
View File
@@ -2119,6 +2119,10 @@ class MailThread(models.AbstractModel):
res_id = kwargs.get('res_id', self.ids and self.ids[0] or 0)
res_ids = kwargs.get('res_id') and [kwargs['res_id']] or self.ids
# 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'))
# Create the composer
composer = self.env['mail.compose.message'].with_context(
active_id=res_id,
@@ -2139,7 +2143,9 @@ class MailThread(models.AbstractModel):
def message_notify(self, *,
partner_ids=False, parent_id=False, model=False, res_id=False,
author_id=None, email_from=None, body='', subject=False, **kwargs):
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. """
@@ -2181,7 +2187,9 @@ class MailThread(models.AbstractModel):
}
msg_values.update(msg_kwargs)
# add default-like values afterwards, to avoid useless queries
if 'subtype_id' not in msg_values:
if subtype_xmlid:
msg_values['subtype_id'] = self.env['ir.model.data']._xmlid_to_res_id(subtype_xmlid)
elif 'subtype_id' not in msg_values:
msg_values['subtype_id'] = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
if 'reply_to' not in msg_values:
msg_values['reply_to'] = self._notify_get_reply_to(default=email_from)[self.id if self else False]
@@ -551,6 +551,7 @@ Model({
default_model: this.composer.activeThread.model,
default_partner_ids: this.composer.recipients.map((partner) => partner.id),
default_res_id: this.composer.activeThread.id,
default_subtype_xmlid: 'mail.mt_comment',
mail_post_autofollow: this.composer.activeThread.hasWriteAccess,
};
@@ -19,6 +19,7 @@ Model({
default_res_id: activity.thread.id,
default_model: activity.thread.model,
default_use_template: true,
default_subtype_xmlid: 'mail.mt_comment',
default_template_id: this.id,
force_email: true,
},
+1
View File
@@ -110,6 +110,7 @@
<field name="context" eval="{
'default_composition_mode': 'mass_mail',
'default_partner_to': '{{ object.id or \'\' }}',
'default_subtype_xmlid': 'mail.mt_comment',
'default_use_template': False,
'default_reply_to_force_new': True,
}"/>
@@ -54,6 +54,13 @@ class MailComposer(models.TransientModel):
# email_layout_xmlid field: to remove in 15.1+
if self._context.get('custom_layout') and 'default_email_layout_xmlid' not in self._context:
self = self.with_context(default_email_layout_xmlid=self._context['custom_layout'])
# support subtype xmlid, like ``message_post``, when easier than using ``ref``
if self.env.context.get('default_subtype_xmlid'):
self = self.with_context(
default_subtype_id=self.env['ir.model.data']._xmlid_to_res_id(
self.env.context['default_subtype_xmlid']
)
)
result = super(MailComposer, self).default_get(fields)
@@ -120,7 +120,7 @@ class MailPluginController(http.Controller):
partner.message_post_with_view(
'iap_mail.enrich_company',
values=iap_data,
subtype_id=request.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
return {
@@ -360,7 +360,7 @@ class MailPluginController(http.Controller):
new_company.message_post_with_view(
'iap_mail.enrich_company',
values=iap_data,
subtype_id=request.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
return new_company, {'type': 'company_created'}
+2 -3
View File
@@ -31,11 +31,10 @@ class MailThread(models.AbstractModel):
def message_post_with_template(self, template_id, **kwargs):
# avoid having message send through `message_post*` methods being implicitly considered as
# mass-mailing
no_massmail = self.with_context(
return super(MailThread, self.with_context(
default_mass_mailing_name=False,
default_mass_mailing_id=False,
)
return super(MailThread, no_massmail).message_post_with_template(template_id, **kwargs)
)).message_post_with_template(template_id, **kwargs)
@api.model
def _routing_handle_bounce(self, email_message, message_dict):
+2 -1
View File
@@ -212,7 +212,8 @@ class MrpUnbuild(models.Model):
)
self.mo_id.message_post(
body=unbuild_msg,
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
return self.write({'state': 'done'})
def _generate_consume_moves(self):
+7 -3
View File
@@ -48,6 +48,7 @@ class StockRule(models.Model):
productions_values_by_company[procurement.company_id.id].append(rule._prepare_mo_vals(*procurement, bom))
note_subtype_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
for company_id, productions_values in productions_values_by_company.items():
# create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(productions_values)
@@ -61,17 +62,20 @@ class StockRule(models.Model):
production.message_post(
body=_('This production order has been created from Replenishment Report.'),
message_type='comment',
subtype_xmlid='mail.mt_note')
subtype_id=note_subtype_id
)
elif orderpoint:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': orderpoint},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_id=note_subtype_id,
)
elif origin_production:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': origin_production},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_id=note_subtype_id,
)
return True
@api.model
@@ -84,7 +84,7 @@ class ResCompany(models.Model):
self.partner_id.message_post_with_view(
'iap_mail.enrich_company',
values=template_values,
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
return True
@@ -169,7 +169,7 @@ class ResPartner(models.Model):
partners.message_post_with_view(
'iap_mail.enrich_company',
values=template_values,
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
partners.write({'additional_info': False})
@@ -98,7 +98,7 @@ class PhoneBlackList(models.Model):
for record in new_records:
record.with_context(mail_create_nosubscribe=True).message_post(
body=message,
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
records += new_records
return records
@@ -123,7 +123,7 @@ class PhoneBlackList(models.Model):
for record in new_records:
record.with_context(mail_create_nosubscribe=True).message_post(
body=message,
subtype_id=self.env.ref('mail.mt_note').id,
subtype_xmlid='mail.mt_note',
)
records += new_records
return records
+6 -11
View File
@@ -57,11 +57,7 @@ class PortalShare(models.TransientModel):
record = res_model.browse(rec.res_id)
rec.access_warning = record.access_warning
@api.model
def _get_note(self):
return self.env.ref('mail.mt_note')
def _send_public_link(self, note, partners=None):
def _send_public_link(self, partners=None):
if partners is None:
partners = self.partner_ids
for partner in partners:
@@ -73,12 +69,12 @@ class PortalShare(models.TransientModel):
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_id=note.id,
subtype_xmlid='mail.mt_note',
email_layout_xmlid='mail.mail_notification_light',
partner_ids=[(6, 0, partner.ids)])
self = self.with_context(lang=saved_lang)
def _send_signup_link(self, note, partners=None):
def _send_signup_link(self, partners=None):
if partners is None:
partners = self.partner_ids.filtered(lambda partner: not partner.user_ids)
for partner in partners:
@@ -92,13 +88,12 @@ class PortalShare(models.TransientModel):
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_id=note.id,
subtype_xmlid='mail.mt_note',
email_layout_xmlid='mail.mail_notification_light',
partner_ids=[(6, 0, partner.ids)])
self = self.with_context(lang=saved_lang)
def action_send_mail(self):
note = self._get_note()
signup_enabled = self.env['ir.config_parameter'].sudo().get_param('auth_signup.invitation_scope') == 'b2c'
if getattr(self.resource_ref, 'access_token', False) or not signup_enabled:
@@ -106,9 +101,9 @@ class PortalShare(models.TransientModel):
else:
partner_ids = self.partner_ids.filtered(lambda x: x.user_ids)
# if partner already user or record has access token send common link in batch to all user
self._send_public_link(note, partner_ids)
self._send_public_link(partner_ids)
# when partner not user send individual mail with signup token
self._send_signup_link(note, self.partner_ids - partner_ids)
self._send_signup_link(self.partner_ids - partner_ids)
# subscribe all recipients so that they receive future communication (better than
# using autofollow as more precise)
@@ -15,12 +15,14 @@ class AccountMove(models.Model):
# subscribe the partner to the invoice
if invoice.partner_id not in invoice.message_partner_ids:
invoice.message_subscribe([invoice.partner_id.id])
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",
email_layout_xmlid="mail.mail_notification_light"
email_layout_xmlid="mail.mail_notification_light",
subtype_id=comment_subtype_id,
)
return True
@@ -39,10 +39,9 @@ class ProjectShareWizard(models.TransientModel):
self.ensure_one()
if self.access_mode == 'edit':
portal_partners = self.partner_ids.filtered('user_ids')
note = self._get_note()
self.resource_ref._add_collaborators(self.partner_ids)
self._send_public_link(note, portal_partners)
self._send_signup_link(note, partners=self.partner_ids - portal_partners)
self._send_public_link(portal_partners)
self._send_signup_link(partners=self.partner_ids - portal_partners)
self.resource_ref.message_subscribe(partner_ids=self.partner_ids.ids)
return {'type': 'ir.actions.act_window_close'}
return super().action_send_mail()
+11 -5
View File
@@ -750,7 +750,11 @@ 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, email_layout_xmlid="mail.mail_notification_layout_with_responsible_signature", composition_mode='comment')
order.with_context(is_reminder=True).message_post_with_template(
template.id,
email_layout_xmlid="mail.mail_notification_layout_with_responsible_signature",
subtype_xmlid='mail.mt_comment',
)
def send_reminder_preview(self):
self.ensure_one()
@@ -1100,9 +1104,11 @@ 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('purchase.track_po_line_template',
values={'line': line, 'product_qty': values['product_qty']},
subtype_id=self.env.ref('mail.mt_note').id)
line.order_id.message_post_with_view(
'purchase.track_po_line_template',
values={'line': line, 'product_qty': values['product_qty']},
subtype_xmlid='mail.mt_note',
)
if 'qty_received' in values:
for line in self:
line._track_qty_received(values['qty_received'])
@@ -1422,7 +1428,7 @@ class PurchaseOrderLine(models.Model):
self.order_id.message_post_with_view(
'purchase.track_po_line_qty_received_template',
values={'line': self, 'qty_received': new_qty},
subtype_id=self.env.ref('mail.mt_note').id
subtype_xmlid='mail.mt_note',
)
def _validate_analytic_distribution(self):
@@ -142,13 +142,13 @@ class PurchaseOrder(models.Model):
origin_po_id.purchase_group_id.order_ids |= orders
else:
self.env['purchase.order.group'].create({'order_ids': [Command.set(origin_po_id.ids + orders.ids)]})
mt_note = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
for order in orders:
if order.requisition_id:
order.message_post_with_view(
'mail.message_origin_link',
values={'self': order, 'origin': order.requisition_id},
subtype_id=mt_note)
subtype_xmlid='mail.mt_note',
)
return orders
def write(self, vals):
@@ -161,7 +161,7 @@ class PurchaseOrder(models.Model):
order.message_post_with_view(
'mail.message_origin_link',
values={'self': order, 'origin': order.requisition_id, 'edit': True},
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
subtype_xmlid='mail.mt_note',
)
if vals.get('alternative_po_ids', False):
if not self.purchase_group_id and len(self.alternative_po_ids + self) > len(self):
+2 -1
View File
@@ -253,7 +253,8 @@ class PurchaseOrder(models.Model):
(pickings | forward_pickings).action_confirm()
picking.message_post_with_view('mail.message_origin_link',
values={'self': picking, 'origin': order},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
return True
def _add_picking_info(self, activity):
+3 -2
View File
@@ -185,7 +185,7 @@ class RatingMixin(models.AbstractModel):
record.message_post_with_template(
template.id,
email_layout_xmlid='mail.mail_notification_light',
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
subtype_xmlid='mail.mt_note',
)
def rating_apply(self, rate, token=None, rating=None, feedback=None,
@@ -223,7 +223,7 @@ class RatingMixin(models.AbstractModel):
if subtype_xmlid is None:
subtype_id = self._rating_apply_get_default_subtype_id()
else:
subtype_id = self.env['ir.model.data']._xmlid_to_res_id(subtype_xmlid)
subtype_id = False
feedback = tools.plaintext2html(feedback or '')
scheduled_datetime = (
@@ -249,6 +249,7 @@ class RatingMixin(models.AbstractModel):
rating_id=rating.id,
scheduled_date=scheduled_datetime,
subtype_id=subtype_id,
subtype_xmlid=subtype_xmlid,
)
return rating
+6 -4
View File
@@ -125,9 +125,10 @@ class PaymentTransaction(models.Model):
return super()._reconcile_after_done()
def _send_invoice(self):
template_id = self.env['ir.config_parameter'].sudo().get_param(
'sale.default_invoice_email_template'
)
template_id = int(self.env['ir.config_parameter'].sudo().get_param(
'sale.default_invoice_email_template',
default=0
))
if not template_id:
return
@@ -141,8 +142,9 @@ class PaymentTransaction(models.Model):
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(
int(template_id),
template_id,
email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature',
subtype_xmlid='mail.mt_comment',
)
def _cron_send_invoice(self):
+3 -2
View File
@@ -844,8 +844,8 @@ class SaleOrder(models.Model):
continue
sale_order.with_context(force_send=True).message_post_with_template(
mail_template.id,
composition_mode='comment',
email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature',
subtype_xmlid='mail.mt_comment',
)
def action_done(self):
@@ -1184,7 +1184,8 @@ class SaleOrder(models.Model):
move.message_post_with_view(
'mail.message_origin_link',
values={'self': move, 'origin': move.line_ids.sale_line_ids.order_id},
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'))
subtype_xmlid='mail.mt_note',
)
return moves
# MAIL #
+3 -1
View File
@@ -88,6 +88,8 @@ class TestSaleSignature(HttpCaseWithUserPortal):
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'))
email_ctx.get('default_template_id'),
subtype_xmlid='mail.mt_comment',
)
self.start_tour("/", 'sale_signature', login="portal")
+8 -2
View File
@@ -79,7 +79,10 @@ 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_template(
email_ctx.get('default_template_id'),
subtype_xmlid='mail.mt_comment',
)
self.assertTrue(self.sale_order.state == 'sent', 'Sale: state after sending is wrong')
self.sale_order.order_line._compute_product_updatable()
self.assertTrue(self.sale_order.order_line[0].product_updatable)
@@ -101,7 +104,10 @@ 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_template(
mail_template.id,
subtype_xmlid='mail.mt_comment',
)
self.assertTrue(sale_order.state == 'sent', 'Sale : state should be changed to sent')
mail_message = sale_order.message_ids[0]
self.assertEqual(mail_message.author_id, sale_order.partner_id, 'Sale: author should be same as customer')
+4 -1
View File
@@ -632,7 +632,10 @@ 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_template(
email_ctx.get('default_template_id'),
subtype_xmlid='mail.mt_comment',
)
self.assertTrue(self.sale_order.state == 'sent', 'Sale: state after sending is wrong')
self.sale_order.order_line._compute_product_updatable()
self.assertTrue(self.sale_order.order_line[0].product_updatable)
@@ -189,7 +189,8 @@ class SaleAdvancePaymentInv(models.TransientModel):
invoice.message_post_with_view(
'mail.message_origin_link',
values={'self': invoice, 'origin': order},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
return invoice
+2 -1
View File
@@ -45,7 +45,8 @@ class StockMove(models.Model):
picking_id.message_post_with_view(
'mail.message_origin_link',
values={'self': picking_id, 'origin': sale_order_id},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
class ProcurementGroup(models.Model):
+5 -1
View File
@@ -667,7 +667,11 @@ 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(template, values={'move': move, 'vals': dict(vals, **data)}, subtype_id=self.env.ref('mail.mt_note').id)
record.message_post_with_view(
template,
values={'move': move, 'vals': dict(vals, **data)},
subtype_xmlid='mail.mt_note',
)
def _free_reservation(self, product_id, location_id, quantity, lot_id=None, package_id=None, owner_id=None, ml_ids_to_ignore=None):
""" When editing a done move line or validating one with some forced quantities, it is
+6 -1
View File
@@ -884,9 +884,14 @@ class Picking(models.Model):
return True
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, email_layout_xmlid='mail.mail_notification_light')
stock_pick.with_context(force_send=True).message_post_with_template(
delivery_template_id,
email_layout_xmlid='mail.mail_notification_light',
subtype_id=subtype_id,
)
@api.depends('state', 'move_ids', 'move_ids.state', 'move_ids.package_level_id', 'move_ids.move_line_ids.package_level_id')
def _compute_move_without_package(self):
+4 -2
View File
@@ -133,9 +133,11 @@ 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('mail.message_origin_link',
new_picking.message_post_with_view(
'mail.message_origin_link',
values={'self': new_picking, 'origin': self.picking_id},
subtype_id=self.env.ref('mail.mt_note').id)
subtype_xmlid='mail.mt_note',
)
returned_lines = 0
for return_line in self.product_return_moves:
if not return_line.move_id:
+14 -2
View File
@@ -144,9 +144,21 @@ class MailTestTicket(models.Model):
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'})
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'})
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):
@@ -351,6 +351,7 @@ QUnit.module("test_mail", {}, function () {
{
default_model: "mail.test.activity",
default_res_id: mailTestActivityId2,
default_subtype_xmlid: 'mail.mt_comment',
default_template_id: mailTemplateId1,
default_use_template: true,
force_email: true,
@@ -1106,6 +1106,16 @@ class TestComposerResultsComment(TestMailComposer):
_mail, message = composer._action_send_mail()
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_note'))
# subtype through xml id
composer = self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_record),
default_subtype_xmlid='mail.mt_note',
).create({
'body': '<p>Default subtype through xml id</p>',
})
_mail, message = composer._action_send_mail()
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_note'))
@users('employee')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_recipients(self):
+4 -2
View File
@@ -1007,6 +1007,7 @@ class TestMessagePostHelpers(TestMessagePostCommon):
_new_mails, _new_messages = test_records.with_user(self.user_employee).message_post_with_template(
template.id,
composition_mode='mass_mail',
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
)
# created partners from inline email addresses
@@ -1056,6 +1057,7 @@ class TestMessagePostHelpers(TestMessagePostCommon):
values={'partner': self.user_employee.partner_id},
composition_mode='mass_mail',
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(self._new_mails), 10)
@@ -1351,7 +1353,7 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
composition_mode='mass_mail',
email_layout_xmlid='mail.test_layout',
message_type='comment',
subtype_id=self.env.ref('mail.mt_comment').id,
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)
@@ -1417,7 +1419,7 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
test_template.id,
email_layout_xmlid='mail.test_layout',
message_type='comment',
subtype_id=self.env.ref('mail.mt_comment').id,
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)
+7 -1
View File
@@ -866,7 +866,12 @@ class TestMailComplexPerformance(BaseMailPerformance):
# about 20 (19 ?) queries per additional customer group
with self.assertQueryCount(__system__=42, employee=43):
record.message_post_with_template(template_id, message_type='comment', composition_mode='comment')
record.message_post_with_template(
template_id,
message_type='comment',
composition_mode='comment',
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'),
)
self.assertEqual(record.message_ids[0].body, '<p>Adding stuff on %s</p>' % record.name)
self.assertEqual(record.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id | self.customer)
@@ -890,6 +895,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
messages_as_sudo = test_records.message_post_with_view(
'test_mail.mail_template_simple_test',
values={'partner': self.user_test.partner_id},
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
)
self.assertEqual(len(messages_as_sudo), 10)
+2 -1
View File
@@ -225,7 +225,8 @@ class BlogPost(models.Model):
'website_blog.blog_post_template_new_post',
subject=post.name,
values={'post': post},
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('website_blog.mt_blog_blog_published'))
subtype_xmlid='website_blog.mt_blog_blog_published',
)
return True
return False
@@ -379,7 +379,7 @@ class CRMRevealRule(models.Model):
lead.message_post_with_view(
'iap_mail.enrich_company',
values=template_values,
subtype_id=self.env.ref('mail.mt_note').id
subtype_xmlid='mail.mt_note',
)
return lead
@@ -404,16 +404,16 @@ class Track(models.Model):
tracks = super(Track, self).create(vals_list)
post_values = {} if self.env.user.email else {'email_from': self.env.company.catchall_formatted}
for track in tracks:
email_values = {} if self.env.user.email else {'email_from': self.env.company.catchall_formatted}
track.event_id.message_post_with_view(
'website_event_track.event_track_template_new',
values={
'track': track,
'is_html_empty': is_html_empty,
},
subtype_id=self.env.ref('website_event_track.mt_event_track').id,
**email_values,
subtype_xmlid='website_event_track.mt_event_track',
**post_values,
)
track._synchronize_with_stage(track.stage_id)
@@ -492,10 +492,10 @@ class Track(models.Model):
track = self[0]
if 'stage_id' in changes and track.stage_id.mail_template_id:
res['stage_id'] = (track.stage_id.mail_template_id, {
'composition_mode': 'comment',
'auto_delete_message': True,
'composition_mode': 'comment',
'email_layout_xmlid': 'mail.mail_notification_light',
'subtype_id': self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'),
'email_layout_xmlid': 'mail.mail_notification_light'
})
return res
+6 -3
View File
@@ -627,13 +627,15 @@ class Post(models.Model):
'website_forum.forum_post_template_new_answer',
subject=_('Re: %s', post.parent_id.name),
partner_ids=[(4, p.id) for p in tag_partners],
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('website_forum.mt_answer_new'))
subtype_xmlid='website_forum.mt_answer_new',
)
elif post.state == 'active' and not post.parent_id:
post.message_post_with_view(
'website_forum.forum_post_template_new_question',
subject=post.name,
partner_ids=[(4, p.id) for p in tag_partners],
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('website_forum.mt_question_new'))
subtype_xmlid='website_forum.mt_question_new',
)
elif post.state == 'pending' and not post.parent_id:
# TDE FIXME: in master, you should probably use a subtype;
# however here we remove subtype but set partner_ids
@@ -644,7 +646,8 @@ class Post(models.Model):
'website_forum.forum_post_template_validation',
subject=post.name,
partner_ids=partners.ids,
subtype_id=self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note'))
subtype_xmlid='mail.mt_note',
)
return True
def reopen(self):