From e9af6096167392c2d3605389367eba9df1b68c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Jul 2021 12:14:17 +0000 Subject: [PATCH] [IMP] mail, various: reorganize and lint composer action name In this commit some reorganization is performed within mail compose message code. Purpose is to reorder a bit methods by main usage: onchange, CRUD, actions, values generation with rendering and template management. Some renaming is performed on action methods, notably send_mail that has some impact on sub-addons. Finally we also set onchange and sub-onchange methods private. No functional change should be implied by this commit. LINKS Task ID-2377974 Community PR odoo/odoo#61467 Enterprise PR odoo/enterprise#14633 --- addons/account/wizard/account_invoice_send.py | 8 +- addons/coupon/models/mail_compose_message.py | 4 +- addons/mail/models/mail_thread.py | 4 +- addons/mail/wizard/mail_compose_message.py | 120 +++++++++--------- .../mail/wizard/mail_compose_message_view.xml | 3 +- addons/mass_mailing/models/mailing.py | 2 +- .../purchase/models/mail_compose_message.py | 4 +- addons/sale/models/res_company.py | 4 +- addons/sale/wizard/mail_compose_message.py | 4 +- addons/test_mail/tests/test_mail_composer.py | 42 +++--- addons/test_mail/tests/test_performance.py | 8 +- .../models/mail_compose_message.py | 4 +- 12 files changed, 103 insertions(+), 104 deletions(-) diff --git a/addons/account/wizard/account_invoice_send.py b/addons/account/wizard/account_invoice_send.py index 6c9468b9723..ce951d44a2e 100644 --- a/addons/account/wizard/account_invoice_send.py +++ b/addons/account/wizard/account_invoice_send.py @@ -78,7 +78,7 @@ class AccountInvoiceSend(models.TransientModel): if wizard.composer_id: wizard.composer_id.template_id = wizard.template_id.id wizard._compute_composition_mode() - wizard.composer_id.onchange_template_id_wrapper() + wizard.composer_id._onchange_template_id_wrapper() @api.onchange('is_email') def onchange_is_email(self): @@ -94,7 +94,7 @@ class AccountInvoiceSend(models.TransientModel): self.composer_id.template_id = self.template_id.id self._compute_composition_mode() if not self.env.context.get("wizard_opened"): - self.composer_id.onchange_template_id_wrapper() + self.composer_id._onchange_template_id_wrapper() @api.onchange('is_email') def _compute_invoice_without_email(self): @@ -117,7 +117,7 @@ class AccountInvoiceSend(models.TransientModel): def _send_email(self): if self.is_email: # with_context : we don't want to reimport the file we just exported. - self.composer_id.with_context(no_new_invoice=True, mail_notify_author=self.env.user.partner_id in self.composer_id.partner_ids).send_mail() + self.composer_id.with_context(no_new_invoice=True, mail_notify_author=self.env.user.partner_id in self.composer_id.partner_ids)._action_send_mail() if self.env.context.get('mark_invoice_as_sent'): #Salesman send posted invoice, without the right to write #but they should have the right to change this flag @@ -154,7 +154,7 @@ class AccountInvoiceSend(models.TransientModel): def save_as_template(self): self.ensure_one() - self.composer_id.save_as_template() + self.composer_id.action_save_as_template() self.template_id = self.composer_id.template_id.id action = _reopen(self, self.id, self.model, context=self._context) action.update({'name': _('Send Invoice')}) diff --git a/addons/coupon/models/mail_compose_message.py b/addons/coupon/models/mail_compose_message.py index a819777a75f..d4b50a79464 100644 --- a/addons/coupon/models/mail_compose_message.py +++ b/addons/coupon/models/mail_compose_message.py @@ -7,9 +7,9 @@ from odoo import models class MailComposeMessage(models.TransientModel): _inherit = 'mail.compose.message' - def send_mail(self, **kwargs): + def _action_send_mail(self, **kwargs): for wizard in self: if self._context.get('mark_coupon_as_sent') and wizard.model == 'coupon.coupon' and wizard.partner_ids: # Mark coupon as sent in sudo, as helpdesk users don't have the right to write on coupons self.env[wizard.model].sudo().browse(wizard.res_id).state = 'sent' - return super().send_mail(**kwargs) + return super()._action_send_mail(**kwargs) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 003d63535ae..f3336657c64 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -1942,9 +1942,9 @@ class MailThread(models.AbstractModel): # 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'] + update_values = composer._onchange_template_id(template_id, kwargs['composition_mode'], self._name, res_id)['value'] composer.write(update_values) - return composer.send_mail(auto_commit=auto_commit) + return composer._action_send_mail(auto_commit=auto_commit) def message_notify(self, *, partner_ids=False, parent_id=False, model=False, res_id=False, diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 388f08e5b87..f0764e045aa 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -9,10 +9,6 @@ from odoo import _, api, fields, models, tools, Command from odoo.exceptions import UserError -# main mako-like expression pattern -EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') - - def _reopen(self, res_id, model, context=None): # save original model in context, because selecting the list of available # templates requires a model in context @@ -161,6 +157,15 @@ class MailComposer(models.TransientModel): for composer in self: composer.render_model = composer.model + # Onchanges + + @api.onchange('template_id') + def _onchange_template_id_wrapper(self): + self.ensure_one() + values = self._onchange_template_id(self.template_id.id, self.composition_mode, self.model, self.res_id)['value'] + for fname, value in values.items(): + setattr(self, fname, value) + @api.model def get_record_data(self, values): """ Returns a defaults-like dict with initial values for the composition @@ -190,16 +195,37 @@ class MailComposer(models.TransientModel): return result + # ------------------------------------------------------------ + # CRUD / ORM + # ------------------------------------------------------------ + + @api.autovacuum + def _gc_lost_attachments(self): + """ Garbage collect lost mail attachments. Those are attachments + - linked to res_model 'mail.compose.message', the composer wizard + - with res_id 0, because they were created outside of an existing + wizard (typically user input through Chatter or reports + created on-the-fly by the templates) + - unused since at least one day (create_date and write_date) + """ + limit_date = fields.Datetime.subtract(fields.Datetime.now(), days=1) + self.env['ir.attachment'].search([ + ('res_model', '=', self._name), + ('res_id', '=', 0), + ('create_date', '<', limit_date), + ('write_date', '<', limit_date)] + ).unlink() + # ------------------------------------------------------------ # ACTIONS # ------------------------------------------------------------ - # action buttons call with positionnal arguments only, so we need an intermediary function - # to ensure the context is passed correctly + def action_send_mail(self): - self.send_mail() + """ Used for action button that do not accept arguments. """ + self._action_send_mail(auto_commit=False) return {'type': 'ir.actions.act_window_close'} - def send_mail(self, auto_commit=False): + def _action_send_mail(self, auto_commit=False): """ Process the wizard content and proceed with sending the related email(s), rendering any template patterns on the fly if needed. """ notif_layout = self._context.get('custom_layout') @@ -283,6 +309,30 @@ class MailComposer(models.TransientModel): if wizard.composition_mode == 'mass_mail': batch_mails_sudo.send(auto_commit=auto_commit) + def action_save_as_template(self): + """ hit save as template button: current form value will be a new + template attached to the current document. """ + for record in self: + model = self.env['ir.model']._get(record.model or 'mail.message') + model_name = model.name or '' + template_name = "%s: %s" % (model_name, tools.ustr(record.subject)) + values = { + 'name': template_name, + 'subject': record.subject or False, + 'body_html': record.body or False, + 'model_id': model.id or False, + 'attachment_ids': [Command.set(record.attachment_ids.ids)], + } + template = self.env['mail.template'].create(values) + # generate the saved template + record.write({'template_id': template.id}) + record._onchange_template_id_wrapper() + return _reopen(self, record.id, record.model, context=self._context) + + # ------------------------------------------------------------ + # RENDERING / VALUES GENERATION + # ------------------------------------------------------------ + def get_mail_values(self, res_ids): """Generate the values that will be used by send_mail to create mail_messages or mail_mails. """ @@ -371,18 +421,7 @@ class MailComposer(models.TransientModel): results[res_id] = mail_values return results - # ------------------------------------------------------------ - # TEMPLATES - # ------------------------------------------------------------ - - @api.onchange('template_id') - def onchange_template_id_wrapper(self): - self.ensure_one() - values = self.onchange_template_id(self.template_id.id, self.composition_mode, self.model, self.res_id)['value'] - for fname, value in values.items(): - setattr(self, fname, value) - - def onchange_template_id(self, template_id, composition_mode, model, res_id): + def _onchange_template_id(self, template_id, composition_mode, model, res_id): """ - mass_mailing: we cannot render, so return the template values - normal mode: return rendered values /!\ for x2many field, this onchange return command instead of ids @@ -427,30 +466,6 @@ class MailComposer(models.TransientModel): return {'value': values} - def save_as_template(self): - """ hit save as template button: current form value will be a new - template attached to the current document. """ - for record in self: - model = self.env['ir.model']._get(record.model or 'mail.message') - model_name = model.name or '' - template_name = "%s: %s" % (model_name, tools.ustr(record.subject)) - values = { - 'name': template_name, - 'subject': record.subject or False, - 'body_html': record.body or False, - 'model_id': model.id or False, - 'attachment_ids': [Command.set([att.id for att in record.attachment_ids])], - } - template = self.env['mail.template'].create(values) - # generate the saved template - record.write({'template_id': template.id}) - record.onchange_template_id_wrapper() - return _reopen(self, record.id, record.model, context=self._context) - - # ------------------------------------------------------------ - # RENDERING - # ------------------------------------------------------------ - def render_message(self, res_ids): """Generate template-based values of wizard, for the document records given by res_ids. This method is meant to be inherited by email_template that @@ -537,20 +552,3 @@ class MailComposer(models.TransientModel): values[res_id] = res_id_values return multi_mode and values or values[res_ids[0]] - - @api.autovacuum - def _gc_lost_attachments(self): - """ Garbage collect lost mail attachments. Those are attachments - - linked to res_model 'mail.compose.message', the composer wizard - - with res_id 0, because they were created outside of an existing - wizard (typically user input through Chatter or reports - created on-the-fly by the templates) - - unused since at least one day (create_date and write_date) - """ - limit_date = fields.Datetime.subtract(fields.Datetime.now(), days=1) - self.env['ir.attachment'].search([ - ('res_model', '=', self._name), - ('res_id', '=', 0), - ('create_date', '<', limit_date), - ('write_date', '<', limit_date)] - ).unlink() diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index cb3a11bb3fe..259e0d584fe 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -79,7 +79,8 @@