Hay una categoría de errores en facturación electrónica que desespera: el comprobante se ve perfecto en pantalla, el XML valida contra el esquema, la firma es correcta, y SUNAT lo rechaza igual.
Casi siempre es un detalle de formato que nadie documenta con claridad. Estos son los que nos costaron tiempo real.
El correlativo no lleva ceros a la izquierda
Tu comprobante se muestra como F001-00000123. Es lo correcto: así se imprime y así lo espera el cliente.
Pero en el XML que viaja a SUNAT, el número de comprobante se arma con la serie y el correlativo sin relleno de ceros:
<cbc:ID>F001-123</cbc:ID>
No F001-00000123. El relleno con ceros es una convención de presentación, no del documento electrónico. Si envías el correlativo relleno, el rechazo que recibes habla de formato del identificador y es fácil interpretarlo como un problema del esquema UBL, cuando en realidad es esto.
La regla práctica: guarda el correlativo como número entero, formatéalo con ceros solo al momento de mostrarlo o imprimirlo. Si lo guardas como texto ya rellenado, tarde o temprano ese texto termina en el XML.
El IGV es 18% y no es configurable
Puede parecer obvio, pero se ve seguido: sistemas con el IGV como parámetro editable. En Perú es 18% fijo, compuesto por 16% de IGV más 2% de IPM.
Hacerlo configurable no agrega flexibilidad, agrega una forma de equivocarse. Y si alguien lo cambia por error, todos los comprobantes emitidos con ese valor quedan mal ante SUNAT.
La leyenda del monto en letras entra al XML sin escapar
El monto en letras («CIENTO VEINTITRÉS CON 45/100 SOLES») y las leyendas adicionales se insertan como texto dentro del XML. Si tu generador no escapa los caracteres especiales, un & en una razón social o en una observación rompe el documento.
Es un error que aparece de golpe con un cliente cuyo nombre comercial tiene &, después de meses funcionando bien.
Los códigos de error de SUNAT no se leen por rango
Circula la idea de que los códigos del 100 al 999 son errores de autenticación. Es una simplificación peligrosa, porque hay códigos en ese rango que son de otra naturaleza. El 0157, por ejemplo, cae dentro de ese rango y no es un problema de credenciales.
Si tu manejo de errores clasifica por rango numérico, vas a mostrarle al usuario «revisa tu clave SOL» cuando el problema es otro. La forma correcta es interpretar los códigos específicos que te importan y tratar el resto como genérico, no deducir la categoría del número.
Un caso especial de la respuesta SOAP
Este es más técnico pero vale la pena. Los faults SOAP de SUNAT vienen en elementos con prefijo de espacio de nombres, y ese prefijo puede contener guiones.
Si tu expresión regular usa \w para capturar el prefijo, no va a coincidir: \w incluye letras, números y guion bajo, pero no el guion medio. El resultado es que ningún fault de SUNAT se interpreta, y todos los errores se ven como respuestas vacías o inesperadas.
Nos pasó, y es de los errores más difíciles de encontrar porque el código «funciona» en las pruebas con faults sintéticos.
En resumen
| Síntoma | Causa probable |
|---|---|
| Rechazo por formato del identificador | Correlativo enviado con ceros a la izquierda |
| XML inválido con un cliente específico | Caracteres especiales sin escapar |
| «Revisa tu clave SOL» sin razón | Clasificación de errores por rango numérico |
| Errores de SUNAT que llegan vacíos | Regex del fault SOAP que no admite guiones |
Todo esto está resuelto en ZMRFact. No porque lo leyéramos en un manual, sino porque cada uno de estos puntos nos costó una tarde.