diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py index ae6eac6ae5e..701f4069a29 100644 --- a/addons/account/models/account_journal.py +++ b/addons/account/models/account_journal.py @@ -613,7 +613,16 @@ class AccountJournal(models.Model): invoices = self.env['account.move'] for attachment in attachments: attachment.write({'res_model': 'mail.compose.message'}) - invoices += self._create_invoice_from_single_attachment(attachment) + decoders = self.env['account.move']._get_create_invoice_from_attachment_decoders() + invoice = False + for decoder in sorted(decoders, key=lambda d: d[0]): + invoice = decoder[1](attachment) + if invoice: + break + if not invoice: + invoice = self.env['account.move'].create({}) + invoice.with_context(no_new_invoice=True).message_post(attachment_ids=[attachment.id]) + invoices += invoice action_vals = { 'name': _('Generated Documents'), @@ -638,12 +647,11 @@ class AccountJournal(models.Model): def _create_invoice_from_single_attachment(self, attachment): """ Creates an invoice and post the attachment. If the related modules are installed, it will trigger OCR or the import from the EDI. + DEPRECATED : use create_invoice_from_attachment instead :returns: the created invoice. """ - invoice = self.env['account.move'].create({}) - invoice.message_post(attachment_ids=[attachment.id]) - return invoice + return self.create_invoice_from_attachment(attachment.ids) def _create_secure_sequence(self, sequence_fields): """This function creates a no_gap sequence on each journal in self that will ensure diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 1665ab0424f..58f77fffc2c 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2887,6 +2887,57 @@ class AccountMove(models.Model): return rslt + @api.returns('mail.message', lambda value: value.id) + def message_post(self, **kwargs): + # OVERRIDE + # When posting a message, check the attachment to see if it's an invoice and update with the imported data. + res = super().message_post(**kwargs) + + attachment_ids = kwargs.get('attachment_ids', []) + if len(self) != 1 or not attachment_ids or self.env.context.get('no_new_invoice') or not self.is_invoice(include_receipts=True): + return res + + attachments = self.env['ir.attachment'].browse(attachment_ids) + odoobot = self.env.ref('base.partner_root') + if attachments and self.state != 'draft': + self.message_post(body='The invoice is not a draft, it was not updated from the attachment.', + message_type='comment', + subtype_xmlid='mail.mt_note', + author_id=odoobot.id) + return res + if attachments and self.line_ids: + self.message_post(body='The invoice already contains lines, it was not updated from the attachment.', + message_type='comment', + subtype_xmlid='mail.mt_note', + author_id=odoobot.id) + return res + + decoders = self.env['account.move']._get_update_invoice_from_attachment_decoders(self) + for decoder in sorted(decoders, key=lambda d: d[0]): + # start with message_main_attachment_id, that way if OCR is installed, only that one will be parsed. + # this is based on the fact that the ocr will be the last decoder. + for attachment in attachments.sorted(lambda x: x != self.message_main_attachment_id): + invoice = decoder[1](attachment, self) + if invoice: + break + + 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. + + :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 [] + class AccountMoveLine(models.Model): _name = "account.move.line" _description = "Journal Item" diff --git a/addons/account/wizard/account_tour_upload_bill.py b/addons/account/wizard/account_tour_upload_bill.py index 4c0209d370f..ef0f9c08247 100644 --- a/addons/account/wizard/account_tour_upload_bill.py +++ b/addons/account/wizard/account_tour_upload_bill.py @@ -65,7 +65,7 @@ class AccountTourUploadBill(models.TransientModel): 'res_model': 'mail.compose.message', 'datas': self.sample_bill_preview, }) - bill = purchase_journal.with_context(default_journal_id=purchase_journal.id, default_move_type='in_invoice')._create_invoice_from_single_attachment(attachment) + bill = purchase_journal.with_context(default_journal_id=purchase_journal.id, default_move_type='in_invoice').create_invoice_from_attachment(attachment.ids) if self.selection == 'sample': bill.write({ 'partner_id': self.env.ref('base.main_partner').id, diff --git a/addons/account_edi/models/account_journal.py b/addons/account_edi/models/account_journal.py index 21503fe004f..a40a90b27bd 100644 --- a/addons/account_edi/models/account_journal.py +++ b/addons/account_edi/models/account_journal.py @@ -51,12 +51,3 @@ class AccountJournal(models.Model): for journal in self: journal.edi_format_ids += edi_formats.filtered(lambda e: e._is_compatible_with_journal(journal)) - - def _create_invoice_from_single_attachment(self, attachment): - # OVERRIDE - invoice = self.env['account.edi.format'].search([])._create_invoice_from_attachment(attachment) - if invoice: - # with_context: we don't want to import the attachment since the invoice was just created from it. - invoice.with_context(no_new_invoice=True).message_post(attachment_ids=attachment.ids) - return invoice - return super(AccountJournal, self.with_context(no_new_invoice=True))._create_invoice_from_single_attachment(attachment) diff --git a/addons/account_edi/models/account_move.py b/addons/account_edi/models/account_move.py index 1f2bc32b67a..170e5fa6efd 100644 --- a/addons/account_edi/models/account_move.py +++ b/addons/account_edi/models/account_move.py @@ -216,37 +216,16 @@ class AccountMove(models.Model): # Import Electronic Document #################################################### - @api.returns('mail.message', lambda value: value.id) - def message_post(self, **kwargs): + def _get_create_invoice_from_attachment_decoders(self): # OVERRIDE - # When posting a message, analyse the attachment to check if it is an EDI document and update the invoice - # with the imported data. - res = super().message_post(**kwargs) - - if len(self) != 1 or self.env.context.get('no_new_invoice') or not self.is_invoice(include_receipts=True): - return res - - attachments = self.env['ir.attachment'].browse(kwargs.get('attachment_ids', [])) - odoobot = self.env.ref('base.partner_root') - if attachments and self.state != 'draft': - self.message_post(body='The invoice is not a draft, it was not updated from the attachment.', - message_type='comment', - subtype_xmlid='mail.mt_note', - author_id=odoobot.id) - return res - if attachments and self.line_ids: - self.message_post(body='The invoice already contains lines, it was not updated from the attachment.', - message_type='comment', - subtype_xmlid='mail.mt_note', - author_id=odoobot.id) - return res - - edi_formats = self.env['account.edi.format'].search([]) - for attachment in attachments: - invoice = edi_formats._update_invoice_from_attachment(attachment, self) - if invoice: - break + res = super()._get_create_invoice_from_attachment_decoders() + res.append((10, self.env['account.edi.format'].search([])._create_invoice_from_attachment)) + return res + def _get_update_invoice_from_attachment_decoders(self, invoice): + # OVERRIDE + res = super()._get_update_invoice_from_attachment_decoders(invoice) + res.append((10, self.env['account.edi.format'].search([])._update_invoice_from_attachment)) return res #################################################### diff --git a/addons/account_edi/tests/common.py b/addons/account_edi/tests/common.py index 37c7eb3702e..b3151b95570 100644 --- a/addons/account_edi/tests/common.py +++ b/addons/account_edi/tests/common.py @@ -67,7 +67,7 @@ class AccountEdiTestCommon(AccountTestInvoicingCommon): }) journal_id = self.company_data['default_journal_sale'] - journal_id.with_context(default_move_type='in_invoice')._create_invoice_from_single_attachment(attachment) + journal_id.with_context(default_move_type='in_invoice').create_invoice_from_attachment(attachment.ids) def assert_generated_file_equal(self, invoice, expected_values, applied_xpath=None): invoice.action_post()