[MOV] account, purchase: move thread code in its own section

Purpose is to better isolate MailThread related methods in those huge files
containing lot of code. Better have them located in a sub-section in order
to have all mail code at the same place.

Task-2710804 (Mail: Clean Mail.Thread API)

Part-of: odoo/odoo#82167
This commit is contained in:
Thibault Delavallée
2022-01-31 17:47:29 +00:00
parent 07fb346919
commit 0b952df5e0
2 changed files with 142 additions and 130 deletions
+116 -112
View File
@@ -2346,41 +2346,6 @@ class AccountMove(models.Model):
result.append((move.id, name))
return result
def _creation_subtype(self):
# OVERRIDE
if self.move_type in ('out_invoice', 'out_refund', 'out_receipt'):
return self.env.ref('account.mt_invoice_created')
else:
return super(AccountMove, self)._creation_subtype()
def _track_subtype(self, init_values):
# OVERRIDE to add custom subtype depending of the state.
self.ensure_one()
if not self.is_invoice(include_receipts=True):
if self.payment_id and 'state' in init_values:
self.payment_id._message_track(['state'], {self.payment_id.id: init_values})
return super(AccountMove, self)._track_subtype(init_values)
if 'payment_state' in init_values and self.payment_state == 'paid':
return self.env.ref('account.mt_invoice_paid')
elif 'state' in init_values and self.state == 'posted' and self.is_sale_document(include_receipts=True):
return self.env.ref('account.mt_invoice_validated')
return super(AccountMove, self)._track_subtype(init_values)
def _creation_message(self):
# OVERRIDE
if not self.is_invoice(include_receipts=True):
return super()._creation_message()
return {
'out_invoice': _('Invoice Created'),
'out_refund': _('Credit Note Created'),
'in_invoice': _('Vendor Bill Created'),
'in_refund': _('Refund Created'),
'out_receipt': _('Sales Receipt Created'),
'in_receipt': _('Purchase Receipt Created'),
}[self.move_type]
# -------------------------------------------------------------------------
# RECONCILIATION METHODS
# -------------------------------------------------------------------------
@@ -2857,59 +2822,6 @@ class AccountMove(models.Model):
'views': [(self.env.ref('account.view_move_tree').id, 'tree'), (False, 'form')],
}
@api.model
def message_new(self, msg_dict, custom_values=None):
# OVERRIDE
# Add custom behavior when receiving a new invoice through the mail's gateway.
if (custom_values or {}).get('move_type', 'entry') not in ('out_invoice', 'in_invoice'):
return super().message_new(msg_dict, custom_values=custom_values)
def is_internal_partner(partner):
# Helper to know if the partner is an internal one.
return partner.user_ids and all(user.has_group('base.group_user') for user in partner.user_ids)
extra_domain = False
if custom_values.get('company_id'):
extra_domain = ['|', ('company_id', '=', custom_values['company_id']), ('company_id', '=', False)]
# Search for partners in copy.
cc_mail_addresses = email_split(msg_dict.get('cc', ''))
followers = [partner for partner in self._mail_find_partner_from_emails(cc_mail_addresses, extra_domain) if partner]
# Search for partner that sent the mail.
from_mail_addresses = email_split(msg_dict.get('from', ''))
senders = partners = [partner for partner in self._mail_find_partner_from_emails(from_mail_addresses, extra_domain) if partner]
# Search for partners using the user.
if not senders:
senders = partners = list(self._mail_search_on_user(from_mail_addresses))
if partners:
# Check we are not in the case when an internal user forwarded the mail manually.
if is_internal_partner(partners[0]):
# Search for partners in the mail's body.
body_mail_addresses = set(email_re.findall(msg_dict.get('body')))
partners = [partner for partner in self._mail_find_partner_from_emails(body_mail_addresses, extra_domain) if not is_internal_partner(partner)]
# Little hack: Inject the mail's subject in the body.
if msg_dict.get('subject') and msg_dict.get('body'):
msg_dict['body'] = '<div><div><h3>%s</h3></div>%s</div>' % (msg_dict['subject'], msg_dict['body'])
# Create the invoice.
values = {
'name': '/', # we have to give the name otherwise it will be set to the mail's subject
'invoice_source_email': from_mail_addresses[0],
'partner_id': partners and partners[0].id or False,
}
move_ctx = self.with_context(default_move_type=custom_values['move_type'], default_journal_id=custom_values['journal_id'])
move = super(AccountMove, move_ctx).message_new(msg_dict, custom_values=values)
move._compute_name() # because the name is given, we need to recompute in case it is the first invoice of the journal
# Assign followers.
all_followers_ids = set(partner.id for partner in followers + senders + partners if is_internal_partner(partner))
move.message_subscribe(list(all_followers_ids))
return move
def post(self):
warnings.warn(
"RedirectWarning method 'post()' is a deprecated alias to 'action_post()' or _post()",
@@ -3403,6 +3315,92 @@ class AccountMove(models.Model):
return rslt
def _get_create_invoice_from_attachment_decoders(self):
""" Returns a list of method that are able to create an invoice from an attachment and a priority.
:returns: A list of tuples (priority, method) where method takes an attachment as parameter.
"""
return []
def _get_update_invoice_from_attachment_decoders(self, invoice):
""" Returns a list of method that are able to create an invoice from an attachment and a priority.
:param invoice: The invoice on which to update the data.
:returns: A list of tuples (priority, method) where method takes an attachment as parameter.
"""
return []
@api.depends('move_type', 'partner_id', 'company_id')
def _compute_narration(self):
use_invoice_terms = self.env['ir.config_parameter'].sudo().get_param('account.use_invoice_terms')
for move in self.filtered(lambda am: not am.narration):
if not use_invoice_terms or not move.is_sale_document(include_receipts=True):
move.narration = False
else:
if not move.company_id.terms_type == 'html':
narration = move.company_id.invoice_terms if not is_html_empty(move.company_id.invoice_terms) else ''
else:
baseurl = self.env.company.get_base_url() + '/terms'
narration = _('Terms & Conditions: %s', baseurl)
move.narration = narration or False
# ------------------------------------------------------------
# MAIL.THREAD
# ------------------------------------------------------------
@api.model
def message_new(self, msg_dict, custom_values=None):
# OVERRIDE
# Add custom behavior when receiving a new invoice through the mail's gateway.
if (custom_values or {}).get('move_type', 'entry') not in ('out_invoice', 'in_invoice'):
return super().message_new(msg_dict, custom_values=custom_values)
def is_internal_partner(partner):
# Helper to know if the partner is an internal one.
return partner.user_ids and all(user.has_group('base.group_user') for user in partner.user_ids)
extra_domain = False
if custom_values.get('company_id'):
extra_domain = ['|', ('company_id', '=', custom_values['company_id']), ('company_id', '=', False)]
# Search for partners in copy.
cc_mail_addresses = email_split(msg_dict.get('cc', ''))
followers = [partner for partner in self._mail_find_partner_from_emails(cc_mail_addresses, extra_domain) if partner]
# Search for partner that sent the mail.
from_mail_addresses = email_split(msg_dict.get('from', ''))
senders = partners = [partner for partner in self._mail_find_partner_from_emails(from_mail_addresses, extra_domain) if partner]
# Search for partners using the user.
if not senders:
senders = partners = list(self._mail_search_on_user(from_mail_addresses))
if partners:
# Check we are not in the case when an internal user forwarded the mail manually.
if is_internal_partner(partners[0]):
# Search for partners in the mail's body.
body_mail_addresses = set(email_re.findall(msg_dict.get('body')))
partners = [partner for partner in self._mail_find_partner_from_emails(body_mail_addresses, extra_domain) if not is_internal_partner(partner)]
# Little hack: Inject the mail's subject in the body.
if msg_dict.get('subject') and msg_dict.get('body'):
msg_dict['body'] = '<div><div><h3>%s</h3></div>%s</div>' % (msg_dict['subject'], msg_dict['body'])
# Create the invoice.
values = {
'name': '/', # we have to give the name otherwise it will be set to the mail's subject
'invoice_source_email': from_mail_addresses[0],
'partner_id': partners and partners[0].id or False,
}
move_ctx = self.with_context(default_move_type=custom_values['move_type'], default_journal_id=custom_values['journal_id'])
move = super(AccountMove, move_ctx).message_new(msg_dict, custom_values=values)
move._compute_name() # because the name is given, we need to recompute in case it is the first invoice of the journal
# Assign followers.
all_followers_ids = set(partner.id for partner in followers + senders + partners if is_internal_partner(partner))
move.message_subscribe(list(all_followers_ids))
return move
def _message_post_after_hook(self, new_message, message_values):
# OVERRIDE
# When posting a message, check the attachment to see if it's an invoice and update with the imported data.
@@ -3437,34 +3435,40 @@ class AccountMove(models.Model):
return res
def _get_create_invoice_from_attachment_decoders(self):
""" Returns a list of method that are able to create an invoice from an attachment and a priority.
def _creation_subtype(self):
# OVERRIDE
if self.move_type in ('out_invoice', 'out_refund', 'out_receipt'):
return self.env.ref('account.mt_invoice_created')
else:
return super(AccountMove, self)._creation_subtype()
:returns: A list of tuples (priority, method) where method takes an attachment as parameter.
"""
return []
def _track_subtype(self, init_values):
# OVERRIDE to add custom subtype depending of the state.
self.ensure_one()
def _get_update_invoice_from_attachment_decoders(self, invoice):
""" Returns a list of method that are able to create an invoice from an attachment and a priority.
if not self.is_invoice(include_receipts=True):
if self.payment_id and 'state' in init_values:
self.payment_id._message_track(['state'], {self.payment_id.id: init_values})
return super(AccountMove, self)._track_subtype(init_values)
:param invoice: The invoice on which to update the data.
:returns: A list of tuples (priority, method) where method takes an attachment as parameter.
"""
return []
if 'payment_state' in init_values and self.payment_state == 'paid':
return self.env.ref('account.mt_invoice_paid')
elif 'state' in init_values and self.state == 'posted' and self.is_sale_document(include_receipts=True):
return self.env.ref('account.mt_invoice_validated')
return super(AccountMove, self)._track_subtype(init_values)
@api.depends('move_type', 'partner_id', 'company_id')
def _compute_narration(self):
use_invoice_terms = self.env['ir.config_parameter'].sudo().get_param('account.use_invoice_terms')
for move in self.filtered(lambda am: not am.narration):
if not use_invoice_terms or not move.is_sale_document(include_receipts=True):
move.narration = False
else:
if not move.company_id.terms_type == 'html':
narration = move.company_id.invoice_terms if not is_html_empty(move.company_id.invoice_terms) else ''
else:
baseurl = self.env.company.get_base_url() + '/terms'
narration = _('Terms & Conditions: %s', baseurl)
move.narration = narration or False
def _creation_message(self):
# OVERRIDE
if not self.is_invoice(include_receipts=True):
return super()._creation_message()
return {
'out_invoice': _('Invoice Created'),
'out_refund': _('Credit Note Created'),
'in_invoice': _('Vendor Bill Created'),
'in_refund': _('Refund Created'),
'out_receipt': _('Sales Receipt Created'),
'in_receipt': _('Purchase Receipt Created'),
}[self.move_type]
class AccountMoveLine(models.Model):
+26 -18
View File
@@ -289,18 +289,6 @@ class PurchaseOrder(models.Model):
del line[2]['date_planned']
return result
def _track_subtype(self, init_values):
self.ensure_one()
if 'state' in init_values and self.state == 'purchase':
if init_values['state'] == 'to approve':
return self.env.ref('purchase.mt_rfq_approved')
return self.env.ref('purchase.mt_rfq_confirmed')
elif 'state' in init_values and self.state == 'to approve':
return self.env.ref('purchase.mt_rfq_confirmed')
elif 'state' in init_values and self.state == 'done':
return self.env.ref('purchase.mt_rfq_done')
return super(PurchaseOrder, self)._track_subtype(init_values)
def _get_report_base_filename(self):
self.ensure_one()
return 'Purchase Order-%s' % (self.name)
@@ -353,6 +341,32 @@ class PurchaseOrder(models.Model):
return {'warning': warning}
return {}
# ------------------------------------------------------------
# MAIL.THREAD
# ------------------------------------------------------------
@api.returns('mail.message', lambda value: value.id)
def message_post(self, **kwargs):
if self.env.context.get('mark_rfq_as_sent'):
self.filtered(lambda o: o.state == 'draft').write({'state': 'sent'})
return super(PurchaseOrder, self.with_context(mail_post_autofollow=self.env.context.get('mail_post_autofollow', True))).message_post(**kwargs)
def _track_subtype(self, init_values):
self.ensure_one()
if 'state' in init_values and self.state == 'purchase':
if init_values['state'] == 'to approve':
return self.env.ref('purchase.mt_rfq_approved')
return self.env.ref('purchase.mt_rfq_confirmed')
elif 'state' in init_values and self.state == 'to approve':
return self.env.ref('purchase.mt_rfq_confirmed')
elif 'state' in init_values and self.state == 'done':
return self.env.ref('purchase.mt_rfq_done')
return super(PurchaseOrder, self)._track_subtype(init_values)
# ------------------------------------------------------------
# ACTIONS
# ------------------------------------------------------------
def action_rfq_send(self):
'''
This function opens a window to compose an email, with the edi purchase template message loaded by default
@@ -410,12 +424,6 @@ class PurchaseOrder(models.Model):
'context': ctx,
}
@api.returns('mail.message', lambda value: value.id)
def message_post(self, **kwargs):
if self.env.context.get('mark_rfq_as_sent'):
self.filtered(lambda o: o.state == 'draft').write({'state': 'sent'})
return super(PurchaseOrder, self.with_context(mail_post_autofollow=self.env.context.get('mail_post_autofollow', True))).message_post(**kwargs)
def print_quotation(self):
self.write({'state': "sent"})
return self.env.ref('purchase.report_purchase_quotation').report_action(self)