WhyNotHugo / django-afip

⚖️ AFIP invoice integration for django.
https://django-afip.readthedocs.io/
ISC License
48 stars 24 forks source link

Validating receipts #16

Open gusarg81 opened 4 years ago

gusarg81 commented 4 years ago

Hi,

I have a doubt about validating receipts in batch.

Let suppose that I'm trying to validated receipts from a queryset. If I understand, errors on the validation process are appended to a list, but that list does not contain a reference to which receipt failed to validate. Or I am wrong?

I am asking this because In my view I generate ReceiptPDF once is validated, but If I validate a batch of receipts and one of them failed, I need to know which one in that process.

Thanks in advance.

WhyNotHugo commented 4 years ago

Hmmm... why you're saying makes sense. Let me double check if the response from Afip actually has this information and if can somehow map it (in case it is).

gusarg81 commented 4 years ago

Hola,

¿Se pudo ver algo sobre esto? Es lo único que me impide enviar por batches (lo hago en baches en mi aplicación, pero no hacia la AFIP. De esta manera si se dá un error, sé a que comprobante pertenece).

Salvo que lo pueda hacer de otra manera.

Gracias!

WhyNotHugo commented 4 years ago

Este es el resultado que devuelve el AFIP (convertido de XML a algo legible).

Es de un escenario donde se intentan validar tres comprobantes, pero el segundo es inválido.

{
    'FeCabResp': {
        'Cuit': 20329642330,
        'PtoVta': 1,
        'CbteTipo': 6,
        'FchProceso': '20200612165914',
        'CantReg': 3,
        'Resultado': 'P',
        'Reproceso': 'N'
    },
    'FeDetResp': {
        'FECAEDetResponse': [
            {
                'Concepto': 1,
                'DocTipo': 96,
                'DocNro': 203012345,
                'CbteDesde': 6468,
                'CbteHasta': 6468,
                'CbteFch': '20200612',
                'Resultado': 'A',
                'Observaciones': None,
                'CAE': '70240840044538',
                'CAEFchVto': '20200622'
            },
            {
                'Concepto': 1,
                'DocTipo': 80,
                'DocNro': 203012345,
                'CbteDesde': 6469,
                'CbteHasta': 6469,
                'CbteFch': '20200612',
                'Resultado': 'R',
                'Observaciones': {
                    'Obs': [
                        {
                            'Code': 10015,
                            'Msg': 'Factura B (CbteDesde igual a CbteHasta), DocTipo: 80, DocNro 203012345 no se encuentra registrado en los padrones de AFIP y no corresponde a una cuit pais.'
                        }
                    ]
                },
                'CAE': None,
                'CAEFchVto': None
            },
            {
                'Concepto': 1,
                'DocTipo': 96,
                'DocNro': 203012345,
                'CbteDesde': 6470,
                'CbteHasta': 6470,
                'CbteFch': '20200612',
                'Resultado': 'R',
                'Observaciones': None,
                'CAE': None,
                'CAEFchVto': None
            }
        ]
    },
    'Events': None,
    'Errors': None
}

En principio, AFIP intenta validarlos en order, así que:

  1. Cuando validás N comprobantes y hay una falla, el error aplica al primero no-validado.
  2. Los anteriores son validados exitosamente (y les queda el CAE y número de comprobante).
  3. Todos los no-validados no tienen ni CAE ni número de comprobante.

El parseo del resultado se hace en esta función.

Creo que sería perfectamente aceptable cambiar el resultado de una lista de errores a un dict con Receipt como clave y listas de errores como valores (Dict[Receipt, List[str]]).

El cambio es backwards-incompatible, pero master ya tiene otros cambios que también lo son, así que sería aceptable agregar esto; iría para v8.0.0.

gusarg81 commented 4 years ago

Hugo,

Hoy estoy empezando a ver el tema de validar por batches (dado que de a uno veo que tarda bastante cuando son muchos).

Por ejemplo, mi duda es: es como identificar a qué receipt pertenece el error, por ejemplo, en una excepción (en este caso, que lo hice adrede):

django_afip.exceptions.AfipException: Error 10069: Campo DocNro no puede ser igual al del emisor.

Más allá de que los que no dan error se validan, acorde a lo que me explicás, me interesaría saber en el momento de la excepción específicamente el receipt que no fue validado/dió error de ser posible.

En este ejemplo, fue aplicado validate() en el queryset.

Gracias.

WhyNotHugo commented 4 years ago

Tengo curiosidad en que tipo de errores de devuelve frequentemente.

Al estar desarrollado capaz uno ve errores seguidos, pero en producción sería raro -- implica que estás mandando datos inconsitentes. Qué tipo de error te cruzás frequemente?

gusarg81 commented 4 years ago

En realidad no me cruzo con errores frecuentes (quizás un poco más en el entorno de testing, pero no tanto). Pero sí me gustaría tener el control exacto de que cuando valido por batches, identificar qué comprobante en cuestión dió error, dado que el error no informa el comprobante en cuestión.

WhyNotHugo commented 4 years ago

Implemente devoler un Dict[Receipt, List[str]] (la lista es una lista de errores), pero, la verdad, me parece una API pésima.

Voy a guardar en la DB los intentos fallidos junto con los errores, y tras validar, devolver simplemente todos los receipts. Se pueden reconocer los exisotos porque receipt.validation.result == RESULT_APPROVED (tal como es ahora), y el resto tendrá las observaciones que corresponda.

Esto da más libertad a como se manejan los errores -- incluyendo que una persona los audite en otro momento, etc. Además, encaja bien en el modelo actual.