Volver al inicio

Procesador RO

Registro de Operaciones · UIF-Perú / SBS

Rol
Desarrollador
Estado
Entregado

El problema

Una casa de cambio supervisada por la SBS debe reportar cada mes toda operación de divisas por USD 5.000 o más, en un formato oficial de 26 columnas. La información vivía en tres sitios que no se hablaban: un PDF mensual de facturas —entre 48 y 81 páginas, una por página, con formato irregular—, el portal de consulta RUC de SUNAT, de donde salen los datos del representante legal que la factura no trae, y una plantilla Excel con filas ya cargadas a mano que no se podían perder ni duplicar. El proceso era abrir página por página, decidir si supera el umbral, transcribir diez campos, ir a SUNAT a buscar el representante legal, copiar cinco campos más, rellenar once códigos fijos y, al final, reordenar por fecha y renumerar el correlativo.

Arquitectura

pipelinetext
PDF mensual (48–81 páginas)
   └─ pdfplumber ────────► extracción de texto por página
      └─ ¿factura válida? ──no──► omitida, con el motivo al log
         └─ parser tolerante ──► 41 formas estructurales
            └─ ¿≥ USD 5.000? ──no──► descartada, con su monto al log
               └─ InvoiceRecord
                  └─ SUNAT headless ──► representante legal
                     └─ merge idempotente + backfill
                        └─ orden por fecha + renumeración
                           └─ Excel UIF · 26 columnas
Nada se descarta en silencio: cada omisión deja su motivo registrado.

Decisiones

01

El formato de la factura no existe

La línea que describe la operación la escribe a mano quien emite la factura. Al analizar las 386 páginas aparecieron 365 líneas distintas que se reducen a 41 formas estructurales. No hay un formato: hay una distribución de improvisaciones. El parser las cubre todas sin adivinar, incluido un typo del emisor y una factura que ni menciona si es compra o venta.

95 ×  COMPRA DE N $ TC N N              forma dominante
85 ×  COMPRA $N A TC N N
59 ×  COMPRA DE N $ AL TC N N
27 ×  VENTA DE N $ TC N N
 4 ×  COMPRA $N A TC N DE LA FECHA N DE SEPTIEMBRE N
 2 ×  COMPRA DE N $ TC N CON LA FECHA DE N/N/N N
 1 ×  COMOPRA DE N $ TC N N                typo del emisor
 1 ×  COMPRA DE N EUROS AL TC N N          otra divisa
 1 ×  VENTA $N ATC N N                     sin espacio
 1 ×  $N A TC N N                          sin COMPRA ni VENTA
      …y 25 formas más de aparición única
N = cualquier número. 365 líneas únicas, 41 formas.
02

Degradación explícita, nunca adivinanza silenciosa

Si la línea no dice COMPRA ni VENTA, el parser asume COMPRA y emite un aviso nominal para revisión humana. Prefiere una fila marcada a una fila inventada. Lo mismo con las operaciones bajo el umbral: se descartan, pero quedan en el log con su monto. En un reporte fiscalizable, un dato que desaparece sin dejar rastro es peor que un dato erróneo señalado.

  [202510]   OK E001-4628 | 13/10/2025 | COMPRA USD 20,000.00 | TRANSPORTES ANDINA EXPRESS S.A.C.
  [202510]   OMITIDA E001-4629 | USD 500.00 < 5,000
  [202510]   OK E001-4639 | 02/10/2025 | VENTA  USD 25,000.00 | COMERCIALIZADORA TEXTIL GAMARRA S.A.C.
  [202510] AVISO  E001-4642: Operación COMPRA/VENTA no indicada explícitamente
  [202510]        — se asumió COMPRA. Verificar.
  [202510] AVISO  OMITIDA página 4: N° de comprobante no encontrado (patrón E###-####)
  [202510] Total facturas extraídas: 18
  [202510] Consultando representantes legales en SUNAT...
  [202510]   Consultando SUNAT [1/16]: RUC 20902887752
  [202510] Total filas en el archivo: 23 (18 facturas + boletas existentes)
Fragmento de una corrida real. El log completo tiene 590 líneas.
03

El dato clave no está en el documento

Cinco de las 26 columnas —tipo y número de documento del representante legal, apellidos y nombres— no figuran en ninguna factura: solo existen en el portal de SUNAT. Hay que ir a buscarlas empresa por empresa. La consulta va cacheada y deduplicada, con una sola sesión de navegador para todo el lote y una pausa cortés entre consultas: las 148 filas se resolvieron con 33 consultas.

04

Idempotencia sobre trabajo humano previo

La plantilla llega con filas cargadas a mano que no se pueden perder ni duplicar. La deduplicación es por comprobante normalizado, así que E001-4801 y 4801-E001 son el mismo: un mes se puede reprocesar las veces que haga falta sin duplicar una sola fila. Y si la plantilla ya tenía filas con RUC pero sin representante legal, el procesador también las completa. No solo agrega lo nuevo: repara lo que estaba a medias.

plantilla de octubre     5 filas  (una incompleta)
   + 18 facturas nuevas
   + backfill de la fila incompleta
   + reorden por fecha y renumeración
   ─────────────────────────────────
   salida                  23 filas
05

Quien lo usa no abre una terminal

El operador que presenta el reporte no es programador. El pipeline vive detrás de una aplicación de escritorio en Tkinter: se eligen las carpetas, se pulsa un botón y el log aparece en la misma ventana, con cada página, cada aviso y cada consulta a SUNAT a la vista. Sin esa capa, la automatización habría vuelto a depender de alguien de sistemas cada mes.

┌──────────────────────────────────────────────────────────┐
│  Procesador de Registro de Operaciones                   │
│  Casa de cambio · Formato UIF-Perú / SBS                 │
├──────────────────────────────────────────────────────────┤
│  Carpeta con PDFs      [ .../input        ]  [Examinar]  │
│  Plantilla Excel       [ .../template     ]  [Examinar]  │
│  Carpeta de salida     [ .../output       ]  [Examinar]  │
│  Nombre del archivo    [ RO_202510.xlsx   ]              │
│                                                          │
│                    [      PROCESAR      ]                │
├──────────────────────────────────────────────────────────┤
│  Log de procesamiento                                    │
│  cada página, aviso y consulta a SUNAT queda registrada  │
│                                                          │
│    [202510]   OK E001-4628 | COMPRA USD 20,000.00        │
│    [202510]   OMITIDA E001-4629 | USD 500.00 < 5,000     │
│    [202510] AVISO E001-4642: se asumió COMPRA. Verificar │
│                                                          │
│  [Abrir archivo generado]  [Limpiar log]        Listo.   │
└──────────────────────────────────────────────────────────┘
La ventana del operador, redibujada: no hay captura de la aplicación.
06

El resultado, con su reserva

Los volúmenes de este caso están medidos sobre los archivos del proyecto. Los tiempos del proceso manual no: son estimaciones de quien lo hacía, no cronometrajes. Lo digo porque la diferencia entre «medido» y «estimado» es exactamente el tipo de distinción que este proyecto existe para respetar.

                                        manual        automatizado
  triaje de 386 páginas                 ≈ 1,5 h        automático
  transcripción de 148 × 26 campos      ≈ 7 h          automático
  33 consultas de representante legal   ≈ 1 h          ≈ 3–4 min
  dedupe, orden por fecha, renumerado   ≈ 2,5 h        automático
  ────────────────────────────────────────────────────────────────
  total                                 ≈ 10–12 h      ≈ 5 min
                                        (1,5 jornadas) + 15 min de revisión
Reducción aproximada del 97–99 % del tiempo humano. Los tiempos manuales son estimados.
07

La operación decide la dirección del dinero

En una COMPRA la divisa entra y salen soles; en una VENTA es al revés. Invertirlo produce un reporte espejado, perfectamente presentable y completamente falso. El mapeo DE → A se deriva del tipo de operación, no de la posición de los importes en la factura.

El producto

La entrada. Toda la operación vive en una sola línea escrita a mano: «COMPRA DE 6,200 $ AL TC 3.646 22,605.20». Ni el representante legal ni su documento aparecen por ningún lado — y son cinco de las 26 columnas del reporte.

La salida

La salida, para el mismo comprobante

N° Reg.FechaDepto.Prov.Dist.Tipo doc.N° doc.Ap. paternoAp. maternoNombresPaísOcupaciónA favor deN° RUCAp. paterno o razón socialAp. maternoNombresOcupaciónTipo de fondoTipo de op.OrigenMonedaMontoMonedaMontoN° comprobante
101/10/202515012200143••••••BENITESOCHOAGABRIELAPE00400320901996640CONSULTORA FINANCIERA ANDES S.A.C.001003099USD6000.00PEN20388.00B001-1242
201/10/202515012200143••••••BENITESOCHOAGABRIELAPE00400320901996640CONSULTORA FINANCIERA ANDES S.A.C.001003099USD21000.00PEN79569.00B001-1245
301/10/202515012200144••••••HERRERAVALDEZMONICA PATRICIAPE00400320902114428COMERCIALIZADORA TEXTIL GAMARRA S.A.C.001003099USD6200.00PEN22605.20E001-4627
401/10/202515012200107••••••PAREDESCORDOVALUIS ALBERTOPE00400320901224317AGENCIA DE ADUANAS MERIDIANO S.A.001003099USD5000.00PEN17100.00E001-4635
502/10/202515012200144••••••HERRERAVALDEZMONICA PATRICIAPE00400320902114428COMERCIALIZADORA TEXTIL GAMARRA S.A.C.001003099PEN93450.00USD25000.00E001-4639
603/10/202515012200146••••••SALAZARDUENASVERONICAPE00400320903114428REPRESENTACIONES COMERCIALES ORIENTE S.A.C.001003099USD18000.00PEN62478.00E001-4613
704/10/202515012200106••••••FLORESAGUILARCESAR AUGUSTOPE00400320900884751DISTRIBUIDORA COMERCIAL LOS OLIVOS S.A.C.001003099USD8500.00PEN30328.00E001-4602
805/10/202515012200110••••••TAPIABUSTAMANTEOSCAR RAULPE00400320902887752EXPORTACIONES CAFETALERAS CHANCHAMAYO E.I.R.L.001003099PEN33354.00USD9000.00E001-4596

8 de las 23 filas de octubre. Las cinco columnas resaltadas no existen en la factura: salieron de una consulta a SUNAT. Datos sintéticos — los RUC llevan dígito verificador inválido a propósito y los documentos van enmascarados, como corresponde a una demo de compliance.

Cifras

386
páginas de PDF procesadasBRIEF — medido sobre los archivos
41
formas estructurales de la línea de operaciónBRIEF — 365 líneas únicas
148
filas generadas en 6 mesesBRIEF — 28/34/24/23/22/17
33
consultas a SUNAT para 148 filasBRIEF — deduplicadas y cacheadas
87
facturas sobre el umbral de USD 5.000BRIEF — medido
~1 500
líneas de códigoBRIEF — medido

Lo que demuestra

HabilidadEvidencia
Datos sucios del mundo real41 formas estructurales cubiertas sin adivinar, incluido un typo del emisor y facturas sin COMPRA/VENTA explícito
Criterio en dominio fiscalizableNada se descarta en silencio: cada omisión y cada suposición queda en el log con su motivo
IdempotenciaDeduplicación por comprobante normalizado y backfill de filas incompletas cargadas a mano
Integración con lo que no se puede pedirCinco columnas que solo existen en SUNAT, resueltas con scraping cacheado: 148 filas con 33 consultas
Reglas de negocio codificadasUmbral, mapeo DE → A según operación y once códigos fijos aplicados de forma reproducible

Deuda técnica reconocida

Un case study que solo lista aciertos no resiste una pregunta difícil.

  • El scraper depende del DOM del portal SUNAT: un rediseño lo rompe. Falla con aviso, sin corromper la salida.
  • Los códigos fijos están cableados para esta empresa —ubigeo, ocupación, tipo de fondo—. Para otra casa de cambio habría que parametrizarlos.
  • Las facturas sin COMPRA o VENTA explícito se asumen COMPRA y quedan marcadas para validación manual.

Stack

  • Python 3
  • pdfplumber
  • openpyxl
  • Selenium + Chrome headless
  • Tkinter
  • ThreadPoolExecutor
  • Decimal