diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index a8484dba4d2..d7b9de5bd9e 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -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'] = '

%s

%s
' % (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'] = '

%s

%s
' % (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): diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 3d2dc6d67ca..e3fece18edf 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -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)