Ir al contenido

Comprobante de Percepción

El Comprobante de Percepción Electrónico (tipo 40) registra la percepción de IGV aplicada a ventas internas o adquisición de combustible. Es un documento adicional a la factura.

CampoTipoDescripciónValidación
seriestringSerie del comprobantePatrón ^P\d{3}$. Ej: P001, P002.
numerointegerCorrelativoMayor a 0.
fechaEmisiondateFecha de emisiónYYYY-MM-DD.
proveedorProveedorDatos del agente de percepción (emisor)ruc de 11 dígitos y razonSocial obligatorios.
clienteClienteDatos del perceptor (adquirente)Campos obligatorios del cliente.
importeTotalPercibidoDecimalTotal percibidoDebe ser positivo.
importeTotalCobradoDecimalTotal cobrado (incluye percepción)Debe ser positivo.
tipoRegimenstringRégimen de percepciónCatálogo N.° 22: 01, 02, 03.
tipoRegimenPorcentajeDecimalPorcentaje del régimenEj: 0.02 para 2%.
operacionesPercepcionRetencionOperacion[]Operaciones percibidasAl menos una operación.

El modelo Perception no tiene campos opcionales a nivel raíz.

Operación de percepción (PercepcionRetencionOperacion)

Sección titulada «Operación de percepción (PercepcionRetencionOperacion)»
CampoTipoDescripciónValidación
numeroOperacionintNúmero de operaciónMayor a 0.
fechaOperaciondateFecha de la operaciónYYYY-MM-DD.
importeOperacionDecimalImporte percibido en esta operaciónDebe ser positivo.
comprobanteComprobanteAfectadoComprobante afectadoVer detalle abajo.
CampoTipoDescripción
tipoComprobantestringTipo del comprobante afectado (Catálogo N.° 01).
serieNumerostringSerie-número del comprobante.
fechaEmisiondateFecha de emisión.
importeTotalDecimalImporte total del comprobante.
monedastringMoneda del comprobante.
Código / ReglaDescripción
2074 (Perception/Retention)UBLVersionID debe ser 2.0.
2072 (Perception/Retention)CustomizationID debe ser 1.0.
1001El ID debe tener formato serie-número.
1007schemeID del agente de percepción debe ser 6 (RUC).
1008RUC del agente de percepción debe tener 11 dígitos.
1037RegistrationName del agente es obligatorio.
SerieDebe iniciar con P.
TotalimporteTotalCobrado = importeTotalComprobante + importeTotalPercibido (debe cuadrar por operación).

Regímenes de percepción (Catálogo N.° 22)

Sección titulada «Regímenes de percepción (Catálogo N.° 22)»
CódigoDescripciónTasa típica
01Venta interna2%
02Adquisición de combustibleVariable
03Tasa 3%3%
from openubl.models import (
Perception, PercepcionRetencionOperacion,
ComprobanteAfectado, Proveedor, Cliente,
)
from openubl.renderer import render_perception
from openubl.validator import SunatValidator
from decimal import Decimal
from datetime import date
perception = Perception(
serie="P001",
numero=1,
fechaEmision=date(2025, 6, 10),
proveedor=Proveedor(
ruc="20100100100",
razonSocial="Mi Empresa S.A.C.",
),
cliente=Cliente(
nombre="Juan Pérez",
numeroDocumentoIdentidad="46779327",
tipoDocumentoIdentidad="1",
),
importeTotalPercibido=Decimal("53.10"),
importeTotalCobrado=Decimal("353.10"),
tipoRegimen="01",
tipoRegimenPorcentaje=Decimal("0.02"),
operaciones=[
PercepcionRetencionOperacion(
numeroOperacion=1,
fechaOperacion=date(2025, 6, 10),
importeOperacion=Decimal("53.10"),
comprobante=ComprobanteAfectado(
tipoComprobante="01",
serieNumero="F001-45",
fechaEmision=date(2025, 6, 10),
importeTotal=Decimal("300.00"),
moneda="PEN",
),
),
],
)
xml = render_perception(perception)
errors = SunatValidator().validate_schema(
xml,
"sunat_schemas/xsd_2.1/2.0/maindoc/UBLPE-Perception-1.0.xsd",
)
assert errors == []
print(xml)

ContentEnricher no enriquece Perception. Los totales y los importes por operación deben proporcionarse explícitamente.

from openubl.models import (
Perception, PercepcionRetencionOperacion,
ComprobanteAfectado, Proveedor, Cliente,
)
from openubl.renderer import render_perception
from decimal import Decimal
from datetime import date
perception = Perception(
serie="P001",
numero=1,
fechaEmision=date(2025, 6, 10),
proveedor=Proveedor(
ruc="20100100100",
razonSocial="Mi Empresa S.A.C.",
),
cliente=Cliente(
nombre="Juan Pérez",
numeroDocumentoIdentidad="46779327",
tipoDocumentoIdentidad="1",
),
importeTotalPercibido=Decimal("53.10"),
importeTotalCobrado=Decimal("353.10"),
tipoRegimen="01",
tipoRegimenPorcentaje=Decimal("0.02"),
operaciones=[
PercepcionRetencionOperacion(
numeroOperacion=1,
fechaOperacion=date(2025, 6, 10),
importeOperacion=Decimal("53.10"),
comprobante=ComprobanteAfectado(
tipoComprobante="01",
serieNumero="F001-45",
fechaEmision=date(2025, 6, 10),
importeTotal=Decimal("300.00"),
moneda="PEN",
),
),
],
)
xml = render_perception(perception)
print(xml)

Un comprobante de percepción puede consolidar varias operaciones sobre comprobantes afectados distintos.

from openubl.models import (
Perception, PercepcionRetencionOperacion,
ComprobanteAfectado, Proveedor, Cliente,
)
from openubl.renderer import render_perception
from openubl.validator import SunatValidator
from decimal import Decimal
from datetime import date
perception = Perception(
serie="P001",
numero=2,
fechaEmision=date(2025, 6, 10),
proveedor=Proveedor(ruc="20100100100", razonSocial="Mi Empresa S.A.C."),
cliente=Cliente(
nombre="Juan Pérez",
numeroDocumentoIdentidad="46779327",
tipoDocumentoIdentidad="1",
),
importeTotalPercibido=Decimal("106.00"),
importeTotalCobrado=Decimal("706.00"),
tipoRegimen="01",
tipoRegimenPorcentaje=Decimal("0.02"),
operaciones=[
PercepcionRetencionOperacion(
numeroOperacion=1,
fechaOperacion=date(2025, 6, 10),
importeOperacion=Decimal("53.10"),
comprobante=ComprobanteAfectado(
tipoComprobante="01",
serieNumero="F001-45",
fechaEmision=date(2025, 6, 10),
importeTotal=Decimal("300.00"),
moneda="PEN",
),
),
PercepcionRetencionOperacion(
numeroOperacion=2,
fechaOperacion=date(2025, 6, 10),
importeOperacion=Decimal("52.90"),
comprobante=ComprobanteAfectado(
tipoComprobante="01",
serieNumero="F001-46",
fechaEmision=date(2025, 6, 10),
importeTotal=Decimal("298.50"),
moneda="PEN",
),
),
],
)
xml = render_perception(perception)
errors = SunatValidator().validate_schema(
xml,
"sunat_schemas/xsd_2.1/2.0/maindoc/UBLPE-Perception-1.0.xsd",
)
assert errors == [], errors
print(xml)
{
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>..."
}

Si validate=true y el cuerpo no cumple las reglas, la API devuelve un error 422 con el detalle de la validación.