[FIX] l10n_it_edi_sdicoop: fix EDI notification logic

In some cases, Odoo's move state is unaligned to that on the
IAP Proxy Server, because the ACK message from Odoo to IAP
deletes all attachments from the EDI transactions on the IAP
without regard if they are with type "Send" or "Receive".

We fix both cases by making Odoo just look at the state and not
to the attachment when it's not needed, and it's only needed when
there is a rejection of the document being sent.

We reserve for the future a change to the ACK to only delete the
attachments by transmission type (Send/Receive) so that they don't
interfere.

Error case 1:
- Odoo client sends an invoice to the IAP Proxy
- The IAP proxy will send it to the SdI
- If everything goes well, the SdI will send a notification back
- The IAP proxy sees the notification and saves it
- The Odoo of the client downloads the notification and sends an ack
- The IAP proxy sees the ack and deletes the attachment
- (ERR!) The client resets to draft, modifies and re-confirms
- The state of his invoice is set again To Send
- Odoo tries to re-send the invoice with the same Id Transaction
- The notification for that Id Transaction is already on the IAP proxy
- Odoo client asks for changes, but just sees the old notification
- (-->) The old notif. has no attachment, so Odoo thinks there's no news
- The move in Odoo stays in the To Send state forever

Error case 2:
- Odoo client sends a Tax Integration for a Vendor Bill (reverse charge)
- The IAP proxy will send it to the SdI
- If everything goes well, the SdI will send the same Vendor Bill back
  as the client's CodiceDestinatario is actually the recipient in the XML
- The IAP proxy sees the file, the Id Transaction is the same
  for both the received and the sent documents
- The SdI sends a notification because the tax integration is OK
- The IAP proxy saves the notification, there are now two EDI
  transactions with the same Id Transaction.
- Odoo checks for new documents
- IAP proxy sends the document to Odoo
- Odoo sends an ACK for that
- (ERR!) the IAP proxy sees the ACK and clears the attachment
  from BOTH the records for sent and received document
- Odoo checks for documents updates and gets the record without
  the attachment
- (-->) The notification has no attachment, so Odoo thinks there's no news
- The move in Odoo stays in the To Send state forever

Ticket link: https://www.odoo.com/web#id=3194378&model=project.task
opw-3194378

closes odoo/odoo#114051

X-original-commit: f85744ee4533ea8593b81a88ef200ec4dc1b1b94
Signed-off-by: Josse Colpaert <jco@odoo.com>
Signed-off-by: Paolo Gatti (pgi) <pgi@odoo.com>
This commit is contained in:
Paolo Gatti (pgi)
2023-03-02 00:27:48 +01:00
parent 9069d17794
commit 774e2e26e4
+62 -44
View File
@@ -944,8 +944,7 @@ class AccountEdiFormat(models.Model):
invoice.l10n_it_edi_transaction = response['id_transaction']
to_return[invoice].update({
'error': _('The invoice was sent to FatturaPA, but we are still awaiting a response. Click the link above to check for an update.'),
'blocking_level': 'info',
})
'blocking_level': 'info'})
return to_return
def _l10n_it_post_invoices_step_2(self, invoices):
@@ -954,6 +953,7 @@ class AccountEdiFormat(models.Model):
to_check = {i.l10n_it_edi_transaction: i for i in invoices}
to_return = {}
company = invoices.company_id
proxy_user = self._get_proxy_user(company)
if not proxy_user: # proxy user should exist, because there is a check in _check_move_configuration
return {invoice: {
@@ -981,50 +981,20 @@ class AccountEdiFormat(models.Model):
if state == 'awaiting_outcome':
to_return[invoice] = {
'error': _('The invoice was sent to FatturaPA, but we are still awaiting a response. Click the link above to check for an update.'),
'blocking_level': 'info',
}
continue
'blocking_level': 'info'}
elif state == 'not_found':
# Invoice does not exist on proxy. Either it does not belong to this proxy_user or it was not created correctly when
# it was sent to the proxy.
to_return[invoice] = {'error': _('You are not allowed to check the status of this invoice.'), 'blocking_level': 'error'}
continue
if not response.get('file'): # It means there is no status update, so we can skip it
document = invoice.edi_document_ids.filtered(lambda d: d.edi_format_id.code == 'fattura_pa')
to_return[invoice] = {'error': document.error, 'blocking_level': document.blocking_level}
continue
xml = proxy_user._decrypt_data(response['file'], response['key'])
response_tree = etree.fromstring(xml)
if state == 'ricevutaConsegna':
elif state == 'ricevutaConsegna':
if invoice._is_commercial_partner_pa():
to_return[invoice] = {'error': _('The invoice has been succesfully transmitted. The addressee has 15 days to accept or reject it.')}
else:
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
elif state == 'notificaScarto':
elements = response_tree.xpath('//Errore')
error_codes = [element.find('Codice').text for element in elements]
errors = [element.find('Descrizione').text for element in elements]
# Duplicated invoice
if '00404' in error_codes:
idx = error_codes.index('00404')
invoice.message_post(body=_(
'This invoice number had already been submitted to the SdI, so it is'
' set as Sent. Please verify that the system is correctly configured,'
' because the correct flow does not need to send the same invoice'
' twice for any reason.\n'
' Original message from the SDI: %s', errors[idx]))
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else:
# Add helpful text if duplicated filename error
if '00002' in error_codes:
idx = error_codes.index('00002')
errors[idx] = _(
'The filename is duplicated. Try again (or adjust the FatturaPA Filename sequence).'
' Original message from the SDI: %s', [errors[idx]]
)
to_return[invoice] = {'error': self._format_error_message(_('The invoice has been refused by the Exchange System'), errors), 'blocking_level': 'error'}
invoice.l10n_it_edi_transaction = False
proxy_acks.append(id_transaction)
elif state == 'notificaMancataConsegna':
if invoice._is_commercial_partner_pa():
to_return[invoice] = {'error': _(
@@ -1045,15 +1015,63 @@ class AccountEdiFormat(models.Model):
' System, and promptly notify him that the original is deposited'
' in his personal area on the portal "Invoices and Fees" of the'
' Revenue Agency.'))
elif state == 'notificaEsito':
outcome = response_tree.find('Esito').text
if outcome == 'EC01':
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else: # ECO2
to_return[invoice] = {'error': _('The invoice was refused by the addressee.'), 'blocking_level': 'error'}
proxy_acks.append(id_transaction)
elif state == 'NotificaDecorrenzaTermini':
# This condition is part of the Public Administration flow
invoice._message_log(body=_(
'The invoice has been correctly issued. The Public Administration recipient'
' had 15 days to either accept or refused this document, but they did not reply,'
' so from now on we consider it accepted.'))
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
proxy_acks.append(id_transaction)
proxy_acks.append(id_transaction)
# In the transaction states above, we don't need to read the attachment.
# In the following cases instead we need to read the information inside
# about the notification itself, i.e. the error message in case of rejection.
else:
attachment_file = response.get('file')
if not attachment_file: # It means there is no status update, so we can skip it
document = invoice.edi_document_ids.filtered(lambda d: d.edi_format_id.code == 'fattura_pa')
to_return[invoice] = {'error': document.error, 'blocking_level': document.blocking_level}
continue
xml = proxy_user._decrypt_data(attachment_file, response['key'])
response_tree = etree.fromstring(xml)
if state == 'notificaScarto':
elements = response_tree.xpath('//Errore')
error_codes = [element.find('Codice').text for element in elements]
errors = [element.find('Descrizione').text for element in elements]
# Duplicated invoice
if '00404' in error_codes:
idx = error_codes.index('00404')
invoice.message_post(body=_(
'This invoice number had already been submitted to the SdI, so it is'
' set as Sent. Please verify that the system is correctly configured,'
' because the correct flow does not need to send the same invoice'
' twice for any reason.\n'
' Original message from the SDI: %s', errors[idx]))
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else:
# Add helpful text if duplicated filename error
if '00002' in error_codes:
idx = error_codes.index('00002')
errors[idx] = _(
'The filename is duplicated. Try again (or adjust the FatturaPA Filename sequence).'
' Original message from the SDI: %s', [errors[idx]]
)
to_return[invoice] = {'error': self._format_error_message(_('The invoice has been refused by the Exchange System'), errors), 'blocking_level': 'error'}
invoice.l10n_it_edi_transaction = False
proxy_acks.append(id_transaction)
elif state == 'notificaEsito':
outcome = response_tree.find('Esito').text
if outcome == 'EC01':
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else: # ECO2
to_return[invoice] = {'error': _('The invoice was refused by the addressee.'), 'blocking_level': 'error'}
proxy_acks.append(id_transaction)
if proxy_acks:
try: