Skip to main content

Descripción

Las Notas de Crédito (tipo documento 07) se utilizan para anular o reducir el valor de facturas o boletas previamente emitidas. El sistema genera automáticamente la serie según el documento afectado:
  • FC01: Para notas de crédito que afectan Facturas (01)
  • BC01: Para notas de crédito que afectan Boletas (03)

Flujo de Trabajo

  1. Crear nota de crédito: POST /api/notas-credito - Genera el registro y XML firmado
  2. Enviar a SUNAT: POST /api/notas-credito/{id}/enviar - Envío sincrónico vía SOAP
  3. Obtener CDR: GET /api/notas-credito/{id}/cdr - Descarga la constancia de recepción
Cuando una nota de crédito es aceptada por SUNAT, la venta original se marca automáticamente como anulada (estado = 2).

POST /api/notas-credito

Crea una nueva nota de crédito asociada a una venta existente.

Request Body

id_venta
integer
required
ID de la venta a afectar (factura o boleta existente)
motivo_id
integer
required
ID del motivo de la nota de crédito. Obtener lista desde GET /api/notas-credito/motivos
descripcion_motivo
string
Descripción personalizada del motivo (opcional, usa la descripción del motivo por defecto si se omite)

Motivos de Nota de Crédito (Catálogo SUNAT 09)

CódigoDescripción
01Anulación de la operación
02Anulación por error en el RUC
03Corrección por error en la descripción
04Descuento global
05Descuento por ítem
06Devolución total
07Devolución por ítem
08Bonificación
09Disminución en el valor
10Otros conceptos

Lógica del Servidor

  1. Valida que la venta exista y pertenezca a la empresa del usuario
  2. Determina la serie automáticamente:
    • Si tipo_doc_afectado = '01' (Factura) → Serie FC01
    • Si tipo_doc_afectado = '03' (Boleta) → Serie BC01
  3. Genera el número correlativo (máximo + 1) sincronizado con documentos_empresas
  4. Copia los montos de la venta original (subtotal, IGV, total)
  5. Genera el XML UBL 2.1 firmado digitalmente usando Greenter
  6. Calcula el hash CPE del XML
  7. Almacena el XML en storage/app/sunat/xml/{ruc}/

Response

success
boolean
Indica si la operación fue exitosa
data
object
Objeto de la nota de crédito creada
xml
object
Información sobre el XML generado

Ejemplo de Request

{
  "id_venta": 145,
  "motivo_id": 1,
  "descripcion_motivo": "Anulación por solicitud del cliente"
}

Ejemplo de Response (201 Created)

{
  "success": true,
  "data": {
    "id": 23,
    "id_venta": 145,
    "motivo_id": 1,
    "serie": "FC01",
    "numero": 34,
    "tipo_doc_afectado": "01",
    "serie_num_afectado": "F001-145",
    "descripcion_motivo": "Anulación por solicitud del cliente",
    "monto_subtotal": "84.75",
    "monto_igv": "15.25",
    "monto_total": "100.00",
    "moneda": "PEN",
    "fecha_emision": "2026-03-06",
    "estado": "pendiente",
    "hash_cpe": "tK8fZmQ7fZQ...",
    "xml_url": "sunat/xml/20612706702/20612706702-07-FC01-00000034.xml",
    "nombre_xml": "20612706702-07-FC01-00000034",
    "id_empresa": 1,
    "id_usuario": 5,
    "venta": {
      "id_venta": 145,
      "serie": "F001",
      "numero": 145,
      "total": "100.00",
      "cliente": {
        "documento": "20123456789",
        "datos": "EMPRESA EJEMPLO SAC"
      }
    },
    "motivo": {
      "id": 1,
      "tipo": "NC",
      "codigo_sunat": "01",
      "descripcion": "Anulación de la operación"
    }
  },
  "xml": {
    "success": true,
    "nombre_archivo": "20612706702-07-FC01-00000034",
    "hash": "tK8fZmQ7fZQ..."
  }
}

POST /api/notas-credito//enviar

Envía la nota de crédito generada a SUNAT para su aceptación.

Path Parameters

id
integer
required
ID de la nota de crédito a enviar

Flujo SUNAT (Sincrónico)

  1. Lee el XML firmado desde storage/app/sunat/xml/{ruc}/
  2. Se autentica con SUNAT usando credenciales SOL (RUC + usuario + clave)
  3. Envía el XML vía SOAP a la API de SUNAT
  4. Recibe el CDR (Constancia de Recepción) inmediatamente
  5. Almacena el CDR en storage/app/sunat/cdr/{ruc}/R-{nombre}.zip
  6. Actualiza el estado de la nota según la respuesta
  7. Si es aceptada, marca la venta original como anulada

Validaciones

  • La nota de crédito debe tener XML generado (nombre_xml no nulo)
  • El archivo XML debe existir en el servidor

Response (Éxito)

success
boolean
true si SUNAT aceptó la nota de crédito
codigo
string
Código de respuesta de SUNAT (ej: “0” = aceptado, “0100” = aceptado con observaciones)
mensaje
string
Descripción de la respuesta de SUNAT
cdr_url
string
Ruta al archivo CDR (ZIP) descargado desde SUNAT

Response (Error)

success
boolean
false si SUNAT rechazó la nota
codigo
string
Código de error de SUNAT
message
string
Descripción del error

Ejemplo de Response (Éxito)

{
  "success": true,
  "codigo": "0",
  "mensaje": "La Nota de Crédito numero FC01-00000034, ha sido aceptada",
  "cdr_url": "sunat/cdr/20612706702/R-20612706702-07-FC01-00000034.zip"
}

Ejemplo de Response (Error)

{
  "success": false,
  "codigo": "2324",
  "message": "El documento afectado por la nota no existe o no está aceptado"
}

GET /api/notas-credito

Obtiene lista paginada de notas de crédito de la empresa.

Query Parameters

page
integer
default:"1"
Número de página

Response

Objeto de paginación Laravel con 15 items por página.

GET /api/notas-credito/

Obtiene detalle completo de una nota de crédito específica.

Path Parameters

id
integer
required
ID de la nota de crédito

GET /api/notas-credito//cdr

Descarga el archivo CDR (Constancia de Recepción) en formato ZIP.

Path Parameters

id
integer
required
ID de la nota de crédito

Response

Archivo ZIP para descarga directa.

GET /api/notas-credito/xml/

Descarga el archivo XML de la nota de crédito.

Path Parameters

nombre
string
required
Nombre del archivo XML (con o sin extensión .xml)

Response

Archivo XML para visualización en navegador.

GET /api/notas-credito/buscar-venta

Busca una venta por serie y número para crear una nota de crédito.

Query Parameters

serie
string
required
Serie de la venta (ej: “F001”)
numero
string
required
Número correlativo de la venta

Response

Objeto de venta con cliente y productos relacionados.

GET /api/notas-credito/motivos

Obtiene la lista de motivos disponibles para notas de crédito.

Response

{
  "success": true,
  "data": [
    {
      "id": 1,
      "tipo": "NC",
      "codigo_sunat": "01",
      "descripcion": "Anulación de la operación",
      "estado": true
    },
    {
      "id": 2,
      "tipo": "NC",
      "codigo_sunat": "02",
      "descripcion": "Anulación por error en el RUC",
      "estado": true
    }
  ]
}

Notas Técnicas

Integración con Greenter

El sistema utiliza la librería Greenter para:
  • Construir objetos UBL 2.1 conformes a SUNAT
  • Firmar digitalmente el XML con certificado PEM
  • Enviar vía SOAP al webservice de SUNAT
  • Procesar la respuesta CDR
Ver SunatService::generarNotaCreditoXml() (línea 319) y enviarNotaCredito() (línea 381).

Certificados Digitales

Se buscan en este orden:
  1. storage/app/sunat/certificados/{ruc}-cert.pem (específico de empresa)
  2. Certificado de prueba configurado en config/sunat.php

Ambientes SUNAT

Si empresa.modo !== 'production':
  • Usa RUC de prueba: 20000000001
  • Usuario SOL: MODDATOS
  • Clave SOL: moddatos
  • Endpoint beta de SUNAT

Estado de la Venta Original

Cuando una nota de crédito es aceptada por SUNAT (código “0”), la venta afectada se actualiza automáticamente:
$nota->venta->update([
    'estado' => '2',           // Anulada
    'estado_sunat' => '2',     // Anulada en SUNAT
]);

Numeración Automática

El sistema sincroniza la numeración con la tabla documentos_empresas:
$ultimoNumero = max(
    NotaCredito::where('serie', $serie)->max('numero'),
    DB::table('documentos_empresas')->where('serie', $serie)->value('numero')
);
Esto permite configurar un número base desde la UI de administración.

Errores Comunes

CódigoDescripciónSolución
2324Documento afectado no existeVerificar que la factura/boleta esté aceptada por SUNAT
2800Serie no autorizadaVerificar configuración de series en SUNAT
1033Certificado inválidoRenovar certificado digital PEM
422XML no generadoLlamar primero a POST /api/notas-credito

Build docs developers (and LLMs) love