[FIX] account_edi: a failed import of an attachment with account_edi now allows OCR to run automatically.

Before this commit, account_invoice_extract and account_edi where independent in parsing files uploaded or added as attachment in an invoice. Some tricks where used to avoid clash, but they were not perfect and some bugs appeared like the OCR not triggering automatically or an attachment being parsed twice by account_edi when parsing failed. The goal of this commit is to unify the import of files between the two features and ensure that they will not clash.

For more information about potential problems that appeared before :
See https://github.com/odoo/odoo/pull/61169
See https://github.com/odoo/enterprise/pull/15124/

closes odoo/odoo#65660

X-original-commit: 8435d0e8990509ee67425977f970ab27bea8133a
Related: odoo/enterprise#16172
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
Signed-off-by: bfr-o <bfr-o@users.noreply.github.com>
This commit is contained in:
Benjamin Frantzen (bfr)
2021-02-05 17:56:12 +00:00
parent b14e5926ae
commit fbc77a6ecd
6 changed files with 73 additions and 44 deletions
+12 -4
View File
@@ -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
+51
View File
@@ -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"
@@ -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,
@@ -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)
+8 -29
View File
@@ -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
####################################################
+1 -1
View File
@@ -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()