En comparación con la obtención del ticket de sesión, generar el CAE es fácil.

  •  En nuestro sistema, el objeto que representa a este servicio recibe una lista de comprobantes y hace una solicitud al web service por cada combinación de CUIT, letra y tipo de comprobante. Esto me simplifico bastante el ordenamiento de los mismos, y además permite que mis selects empleen los índices que existen actualmente en las tablas. Sin embargo, también se puede hacer una solicitud por cada CUIT. Aunque en ese caso, desconozco cómo tiene que ser el ordenamiento de los comprobantes.
  • Por cada una de las combinaciones anteriores, busca el máximo comprobante que no tiene CAE y trae ese y todos los anteriores que no tienen CAE.
  • Durante esa etapa realizamos las siguientes validaciones:
    • La fecha del comprobante no puede ser anterior en mas de cinco días consecutivos de la fecha de remisión del archivo (Error 5): En el caso de las empresas de servicios el plazo es 10 días. Por eso, validamos que el comprobante no tenga mas de 10 días de antigüedad, ni que falten mas de 10 días para la fecha del mismo. De esta manera, al ocurrir este error, el usuario nos tiene que llamar. Todavía no tenemos una forma fácil de corregirlo.
    • Para la clase de comprobante solicitado -comprobante clase A- deberá consignar en el campo código de documento identificatorio del comprador el código 80. (Error 7): Los comprobantes A necesitan el CUIT del comprador.
    • Además hacemos algunas validaciones básicas de consistencia de cada comprobante como que el total sea igual a la sumatoria de los otros campos con importes y que si el importe gravado es mayor a cero, la factura tenga IVA. Esto lo tuve que hacer porque mientras desarrollaba esto, estuvimos realizando cambios al proceso de facturación y nuestra base de datos tenia bastantes comprobantes inconsistentes.

Lista completa de errores que puede informar la AFIP

  • Para cada comprobante creamos un registro teniendo en cuenta lo siguiente. El tipo de documento y de comprobante son los informados por el RECE en el menú Tablas. El campo fecha_venc_pago, en el caso de las notas de crédito que no tienen una fecha de vencimiento, lo complete con la fecha de hoy más un día. Esto en realidad lo hice debido al problema que tuve con el RECE. No probé qué pasa si envío este tag vacío al WS.
  • Posteriormente agrupamos en un solo registro los comprobantes B siempre que en conjunto no sumen $1000 (o el limite parametrizado). Al unir los comprobantes seteamos el tipo de documento a '99' y el numero a cero.
  • A continuación realizo otro conjunto de validaciones:
    • El punto de venta se debe encontrar entre 1 y 9998
    • El numero de comprobante desde debe ser igual al número. de comprobante hasta en comprobantes tipo A.
    • Comprobantes tipo B mayores a $1000 en pedido múltiple.
    • El total es menor a la sumatoria de los otros campos con importes.
  • Buscamos un ticket de sesión para el CUIT. Si no existe o venció, lo solicitamos al WSAA.
  • Creamos el envelope SOAP:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ar="http://ar.gov.afip.dif.facturaelectronica/">
<soapenv:Header/>
<soapenv:Body>
  <ar:FEAutRequest>
    <ar:argAuth>
      <ar:Token>(token)</ar:Token>
        <ar:Sign>(Sign)</ar:Sign>
    <ar:cuit>(CUIT)</ar:cuit>
    </ar:argAuth>
    <ar:Fer>
      <ar:Fecr>
        <ar:id>(nro_solicitud)</ar:id>
    <ar:cantidadreg>(cantidad_comprob)</ar:cantidadreg>
    <ar:presta_serv>1</ar:presta_serv>
      </ar:Fecr>
      <ar:Fedr>
       <ar:FEDetalleRequest>
         <ar:tipo_doc>(tipo_doc)</ar:tipo_doc>
         <ar:nro_doc>(nro_doc)</ar:nro_doc>
         <ar:tipo_cbte>(compro_tipo_afip)</ar:tipo_cbte>
         <ar:punto_vta>(punto)</ar:punto_vta>
         <ar:cbt_desde>(compro_nro_afip_desde)</ar:cbt_desde>
         <ar:cbt_hasta>(compro_nro_afip_hasta)</ar:cbt_hasta>
         <ar:imp_total>string(total, '########0.00')</ar:imp_total>
         <ar:imp_tot_conc>string(concep_no_grava, '########0.00')</ar:imp_tot_conc>
         <ar:imp_neto>string(grava, '########0.00')</ar:imp_neto>
         <ar:impto_liq>string(impuestos, '########0.00')</ar:impto_liq>
         <ar:impto_liq_rni>string(impuestos_rni, '########0.00')</ar:impto_liq_rni>
         <ar:imp_op_ex>string(exen, '########0.00')</ar:imp_op_ex>
         <ar:fecha_cbte>string(fecha, 'yyyymmdd')</ar:fecha_cbte>
         <ar:fecha_serv_desde>string(factu_desde, 'yyyymmdd'</ar:fecha_serv_desde>
         <ar:fecha_serv_hasta>string(factu_hasta, 'yyyymmdd')</ar:fecha_serv_hasta>
         <ar:fecha_venc_pago>string(vto(), 'yyyymmdd')</ar:fecha_venc_pago>
       </ar:FEDetalleRequest>
      </ar:Fedr>
    </ar:Fer>
  </ar:FEAutRequest>
</soapenv:Body>
</soapenv:Envelope>

Archivos de ejemplo:
ejemploFEAutRequestEnvio0.xml
ejemploFEAutRequestEnvio1.xml
ejemploFEAutRequestEnvio2.xml
ejemploFEAutRequestEnvio3.xml

<soap:Envelope>
  <soap:Body>
    <FEAutRequestResponse>
      <FEAutRequestResult>
        <RError>
          <percode>1000</percode>
          <perrmsg>
FEAutRequest@SRV_RA: Usuario no autorizado a realizar esta operacion. ValidacionDeToken: No apareció CUIT en lista de relaciones: 20305949125
          </perrmsg>
        </RError>
      </FEAutRequestResult>
    </FEAutRequestResponse>
  </soap:Body>
</soap:Envelope>

Agregue los caracteres char(10) y char(13) para que se lea mejor el XML, en el siguiente link encontrara el archivo original: ejemploFEAutRequestRespuesta2.xml.

  • Si no hay errores recibimos algo similar a:
<soap:Envelope>
  <soap:Body>
    <FEAutRequestResponse>
      <FEAutRequestResult>
        <FecResp>
          <id>1</id>
          <cuit>20305949125</cuit>
          <fecha_cae>20071107</fecha_cae>
          <cantidadreg>1</cantidadreg>
          <resultado>A</resultado>
          <motivo>00</motivo>
          <reproceso>N</reproceso>
          <presta_serv>1</presta_serv>
        </FecResp>
        <FedResp>
       <FEDetalleResponse>
            <tipo_doc>80</tipo_doc>
            <nro_doc>20044720591</nro_doc>
            <tipo_cbte>1</tipo_cbte>
            <punto_vta>33</punto_vta>
            <cbt_desde>1</cbt_desde>
            <cbt_hasta>1</cbt_hasta>
            <imp_total>112.5</imp_total>
            <imp_tot_conc>0</imp_tot_conc>
            <imp_neto>100</imp_neto>
            <impto_liq>10.5</impto_liq>
            <impto_liq_rni>0</impto_liq_rni>
            <imp_op_ex>0</imp_op_ex>
            <resultado>A</resultado>
            <cae>57456448947494</cae>
            <fecha_cbte>20071107</fecha_cbte>
            <fecha_vto>20071117</fecha_vto>
            <motivo>00</motivo>
            <fecha_serv_desde>20070101</fecha_serv_desde>
            <fecha_serv_hasta>20070131</fecha_serv_hasta>
            <fecha_venc_pago>20071210</fecha_venc_pago>
          </FEDetalleResponse>
        </FedResp>
    <RError>
          <percode>0</percode>
          <perrmsg/>
        </RError>
      </FEAutRequestResult>
    </FEAutRequestResponse>
  </soap:Body>
</soap:Envelope>

Agregue los caracteres char(10) y char(13) para que se lea mejor el XML, en el siguiente link encontrara el archivo original: ejemploFEAutRequestRespuesta0.xml.

Otros ejemplos:
ejemploFEAutRequestRespuesta1.xml Comprobante aprobado con Observaciones.
ejemploFEAutRequestRespuesta3.xml Varios comprobantes aprobados.
ejemploFEAutRequestRespuesta4.xml Comprobantes rechazados con múltiples errores.

  • En este momento, nuestro sistema hace una grabación del XML recibido. De esta manera, si por alguna razón existe un problema en el procesamiento, no perdemos el CAE. Cuando estemos en producción y hayan pasado algunos meses, apagaremos esto.

Creo que lo correcto hubiera sido preguntar a la AFIP cuál es el número del ultimo comprobante con CAE para cada CUIT y punto de venta, y hacer que el sistema continúe a partir de ese comprobante. En el caso que existan comprobantes menores a ese numero sin CAE, nuestro sistema tendría que pedirlos a la AFIP.

  • Si la solicitud es rechazada completamente, le mostramos un mensaje de error al usuario.
  • Por cada comprobante, obtenemos los valores de los tags tipo_cbte, punto_vta, cbt_desde, cbt_hasta y recuperamos los números de los comprobantes originales. Además analizamos los valores de los campos resultado, cae, fecha_vto y motivo. Los campos cae y fecha_vto vienen con el string 'NULL' si ocurrió un error. Hay que considerar que el campo motivo puede incluir varios códigos de rechazo. Ver ejemploFEAutRequestRespuesta4.xml
  • En el caso de los comprobantes aceptados, guardamos el CAE y su fecha de vencimiento.
  • Para los rechazados, solo guardamos en la auditoria el motivo de rechazo.