Nota de Crédito
La Nota de Crédito Electrónica (tipo 07) anula o reduce el valor de una factura o boleta previamente emitida. Se emite por devoluciones, descuentos o errores en el comprobante original.
Campos requeridos
Sección titulada «Campos requeridos»| Campo | Tipo | Descripción | Validación |
|---|---|---|---|
serie | string | Serie de la nota | Patrón ^[BCbc][A-Za-z0-9]{2,3}$. Ej: FC01 para factura, BC01 para boleta. |
numero | integer | Correlativo | Mayor a 0. |
comprobanteAfectadoSerieNumero | string | Serie-número del comprobante afectado | Ej: F001-123. |
sustentoDescripcion | string | Motivo de la nota de crédito | Texto libre. |
proveedor | Proveedor | Datos del emisor | ruc de 11 dígitos y razonSocial obligatorios. |
cliente | Cliente | Datos del adquirente | nombre, numeroDocumentoIdentidad y tipoDocumentoIdentidad (Catálogo N.° 06). |
detalles | DocumentoVentaDetalle[] | Líneas de ajuste | Al menos una línea. |
Campos opcionales
Sección titulada «Campos opcionales»| Campo | Tipo | Default | Descripción |
|---|---|---|---|
moneda | string | PEN | Moneda del comprobante (Catálogo N.° 02). |
fechaEmision | date | hoy | Fecha de emisión (YYYY-MM-DD). |
igvTotal | Decimal | auto | Suma de IGV de las líneas. |
valorVentaTotal | Decimal | auto | Suma de valores de venta. |
importeTotal | Decimal | auto | valorVentaTotal + igvTotal. |
Línea de detalle (DocumentoVentaDetalle)
Sección titulada «Línea de detalle (DocumentoVentaDetalle)»| Campo | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
descripcion | string | Sí | — | Descripción del bien o servicio. |
cantidad | Decimal | Sí | — | Cantidad. |
precio | Decimal | Sí | — | Valor unitario (sin IGV). Use negativo para reducciones. |
unidadMedida | string | No | NIU | Unidad de medida (Catálogo N.° 03). |
tipoAfectacionIGV | string | No | 10 | Tipo de afectación del IGV (Catálogo N.° 07). |
igv | Decimal | No | auto | IGV de la línea. |
valorVenta | Decimal | No | auto | cantidad × precio. |
precioVenta | Decimal | No | auto | valorVenta + igv. |
Reglas SUNAT aplicables
Sección titulada «Reglas SUNAT aplicables»| Código / Regla | Descripción |
|---|---|
2074 | UBLVersionID debe ser 2.1. |
2072 | CustomizationID debe ser 2.0. |
1001 | El ID del comprobante debe tener formato serie-número. |
2070 | DocumentCurrencyCode es obligatorio. |
1007 | schemeID del emisor debe ser 6 (RUC). |
1008 | RUC del emisor debe tener 11 dígitos. |
1037 | RegistrationName del emisor es obligatorio. |
2015 | schemeID del cliente es obligatorio. |
2062 | PayableAmount debe ser mayor a 0. |
3305 | TaxInclusiveAmount es obligatorio. |
3294 | El TaxTotal debe cuadrar con la sumatoria de líneas. |
3278 | El LineExtensionAmount debe cuadrar con la sumatoria de líneas. |
| Serie | Debe iniciar con B/C o F/E según el tipo de comprobante afectado. |
| Total | El importe total de la nota no puede exceder el importe del comprobante original. |
Ejemplo completo
Sección titulada «Ejemplo completo»from openubl.models import ( CreditNote, Proveedor, Cliente, DocumentoVentaDetalle,)from openubl.renderer import render_credit_notefrom openubl.validator import SunatValidatorfrom decimal import Decimal
credit_note = CreditNote( serie="FC01", numero=1, comprobanteAfectadoSerieNumero="F001-45", sustentoDescripcion="Descuento por pronto pago", proveedor=Proveedor( ruc="20100100100", razonSocial="Mi Empresa S.A.C.", ), cliente=Cliente( nombre="Juan Pérez", numeroDocumentoIdentidad="46779327", tipoDocumentoIdentidad="1", ), detalles=[ DocumentoVentaDetalle( descripcion="Descuento pronto pago - Laptop Dell XPS 13", cantidad=Decimal("1"), precio=Decimal("-250.00"), ), ],)
xml = render_credit_note(credit_note)errors = SunatValidator().validate_credit_note(xml)assert errors == []print(xml)import { createCreditNote } from "@openubl/sdk";import { zCreditNote } from "@openubl/sdk/zod.gen";
const creditNote = zCreditNote.parse({ serie: "FC01", numero: 1, comprobanteAfectadoSerieNumero: "F001-45", sustentoDescripcion: "Descuento por pronto pago", proveedor: { ruc: "20100100100", razonSocial: "Mi Empresa S.A.C.", }, cliente: { nombre: "Juan Pérez", numeroDocumentoIdentidad: "46779327", tipoDocumentoIdentidad: "1", }, detalles: [ { descripcion: "Descuento pronto pago - Laptop Dell XPS 13", cantidad: "1", precio: "-250.00", }, ],});
const { data, error } = await createCreditNote({ query: { validate: true }, body: creditNote });
if (error) { throw new Error(JSON.stringify(error));}
console.log(data.xml);curl -X POST "http://localhost:8000/api/v1/credit-note/create?validate=true" \-H "Content-Type: application/json" \-d '{ "serie": "FC01", "numero": 1, "comprobanteAfectadoSerieNumero": "F001-45", "sustentoDescripcion": "Descuento por pronto pago", "proveedor": { "ruc": "20100100100", "razonSocial": "Mi Empresa S.A.C." }, "cliente": { "nombre": "Juan Pérez", "numeroDocumentoIdentidad": "46779327", "tipoDocumentoIdentidad": "1" }, "detalles": [{ "descripcion": "Descuento pronto pago", "cantidad": "1", "precio": "-250.00" }]}'Ejemplo con enriquecimiento automático
Sección titulada «Ejemplo con enriquecimiento automático»ContentEnricher calcula fechaEmision, valorVenta, igv, precioVenta, valorVentaTotal, igvTotal e importeTotal.
from openubl.models import ( CreditNote, Proveedor, Cliente, DocumentoVentaDetalle,)from openubl.enricher import ContentEnricherfrom openubl.renderer import render_credit_notefrom openubl.validator import SunatValidatorfrom decimal import Decimal
credit_note = CreditNote( serie="FC01", numero=1, comprobanteAfectadoSerieNumero="F001-45", sustentoDescripcion="Descuento por pronto pago", proveedor=Proveedor( ruc="20100100100", razonSocial="Mi Empresa S.A.C.", ), cliente=Cliente( nombre="Juan Pérez", numeroDocumentoIdentidad="46779327", tipoDocumentoIdentidad="1", ), detalles=[ DocumentoVentaDetalle( descripcion="Descuento pronto pago - Laptop Dell XPS 13", cantidad=Decimal("1"), precio=Decimal("-250.00"), # No se envían igv, valorVenta ni precioVenta ), ],)
ContentEnricher().enrich(credit_note)xml = render_credit_note(credit_note)errors = SunatValidator().validate_credit_note(xml)assert errors == [], errorsprint(xml)import { createCreditNote } from "@openubl/sdk";import { zCreditNote } from "@openubl/sdk/zod.gen";
const creditNote = zCreditNote.parse({ serie: "FC01", numero: 1, comprobanteAfectadoSerieNumero: "F001-45", sustentoDescripcion: "Descuento por pronto pago", proveedor: { ruc: "20100100100", razonSocial: "Mi Empresa S.A.C." }, cliente: { nombre: "Juan Pérez", numeroDocumentoIdentidad: "46779327", tipoDocumentoIdentidad: "1" }, detalles: [ { descripcion: "Descuento pronto pago - Laptop Dell XPS 13", cantidad: "1", precio: "-250.00" }, ],});
const { data, error } = await createCreditNote({ body: creditNote });
if (error) { throw new Error(JSON.stringify(error));}
console.log(data.xml);curl -X POST "http://localhost:8000/api/v1/credit-note/create" \ -H "Content-Type: application/json" \ -d '{ "serie": "FC01", "numero": 1, "comprobanteAfectadoSerieNumero": "F001-45", "sustentoDescripcion": "Descuento por pronto pago", "proveedor": { "ruc": "20100100100", "razonSocial": "Mi Empresa S.A.C." }, "cliente": { "nombre": "Juan Pérez", "numeroDocumentoIdentidad": "46779327", "tipoDocumentoIdentidad": "1" }, "detalles": [{ "descripcion": "Descuento pronto pago - Laptop Dell XPS 13", "cantidad": "1", "precio": "-250.00" }] }'Endpoint
Sección titulada «Endpoint»from openubl.models import ( CreditNote, Proveedor, Cliente, DocumentoVentaDetalle,)from openubl.renderer import render_credit_notefrom decimal import Decimal
credit_note = CreditNote( serie="FC01", numero=1, comprobanteAfectadoSerieNumero="F001-45", sustentoDescripcion="Descuento por pronto pago", proveedor=Proveedor( ruc="20100100100", razonSocial="Mi Empresa S.A.C.", ), cliente=Cliente( nombre="Juan Pérez", numeroDocumentoIdentidad="46779327", tipoDocumentoIdentidad="1", ), detalles=[ DocumentoVentaDetalle( descripcion="Descuento pronto pago", cantidad=Decimal("1"), precio=Decimal("-250.00"), ), ],)
xml = render_credit_note(credit_note)print(xml)import { createCreditNote } from "@openubl/sdk";import { zCreditNote } from "@openubl/sdk/zod.gen";
const creditNote = zCreditNote.parse({ serie: "FC01", numero: 1, comprobanteAfectadoSerieNumero: "F001-45", sustentoDescripcion: "Descuento por pronto pago", proveedor: { ruc: "20100100100", razonSocial: "Mi Empresa S.A.C.", }, cliente: { nombre: "Juan Pérez", numeroDocumentoIdentidad: "46779327", tipoDocumentoIdentidad: "1", }, detalles: [ { descripcion: "Descuento pronto pago", cantidad: "1", precio: "-250.00", }, ],});
const { data, error } = await createCreditNote({ body: creditNote });
if (error) { throw new Error(JSON.stringify(error));}
console.log(data.xml);curl -X POST "http://localhost:8000/api/v1/credit-note/create?validate=true" \-H "Content-Type: application/json" \-d '{ "serie": "FC01", "numero": 1, "comprobanteAfectadoSerieNumero": "F001-45", "sustentoDescripcion": "Descuento por pronto pago", "proveedor": { "ruc": "20100100100", "razonSocial": "Mi Empresa S.A.C." }, "cliente": { "nombre": "Juan Pérez", "numeroDocumentoIdentidad": "46779327", "tipoDocumentoIdentidad": "1" }, "detalles": [{ "descripcion": "Descuento pronto pago", "cantidad": "1", "precio": "-250.00" }]}'Casos especiales
Sección titulada «Casos especiales»Anulación total
Sección titulada «Anulación total»Si la nota de crédito anula el importe total de la factura original, el precio de la línea debe ser igual al importe total del comprobante afectado con signo negativo.
from openubl.models import ( CreditNote, Proveedor, Cliente, DocumentoVentaDetalle,)from openubl.enricher import ContentEnricherfrom openubl.renderer import render_credit_notefrom openubl.validator import SunatValidatorfrom decimal import Decimal
credit_note = CreditNote( serie="FC01", numero=1, comprobanteAfectadoSerieNumero="F001-1", sustentoDescripcion="Anulación de venta", proveedor=Proveedor(ruc="20100100100", razonSocial="Mi Empresa S.A.C."), cliente=Cliente( nombre="Cliente Ejemplo S.A.C.", numeroDocumentoIdentidad="98765432101", tipoDocumentoIdentidad="6", ), detalles=[ DocumentoVentaDetalle( descripcion="Anulación total - Factura F001-1", cantidad=Decimal("1"), precio=Decimal("-300.00"), ), ],)
ContentEnricher().enrich(credit_note)xml = render_credit_note(credit_note)errors = SunatValidator().validate_credit_note(xml)assert errors == [], errorsprint(xml)import { createCreditNote } from "@openubl/sdk";import { zCreditNote } from "@openubl/sdk/zod.gen";
const creditNote = zCreditNote.parse({ serie: "FC01", numero: 1, comprobanteAfectadoSerieNumero: "F001-1", sustentoDescripcion: "Anulación de venta", proveedor: { ruc: "20100100100", razonSocial: "Mi Empresa S.A.C." }, cliente: { nombre: "Cliente Ejemplo S.A.C.", numeroDocumentoIdentidad: "98765432101", tipoDocumentoIdentidad: "6", }, detalles: [ { descripcion: "Anulación total - Factura F001-1", cantidad: "1", precio: "-300.00" }, ],});
const { data, error } = await createCreditNote({ body: creditNote });
if (error) { throw new Error(JSON.stringify(error));}
console.log(data.xml);curl -X POST "http://localhost:8000/api/v1/credit-note/create" \ -H "Content-Type: application/json" \ -d '{ "serie": "FC01", "numero": 1, "comprobanteAfectadoSerieNumero": "F001-1", "sustentoDescripcion": "Anulación de venta", "proveedor": { "ruc": "20100100100", "razonSocial": "Mi Empresa S.A.C." }, "cliente": { "nombre": "Cliente Ejemplo S.A.C.", "numeroDocumentoIdentidad": "98765432101", "tipoDocumentoIdentidad": "6" }, "detalles": [{ "descripcion": "Anulación total - Factura F001-1", "cantidad": "1", "precio": "-300.00" }] }'Respuesta esperada
Sección titulada «Respuesta esperada»{ "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>..."}Si validate=true y el XML no cumple las reglas SUNAT, la API devuelve un error 422 con la lista de errores.