Skip to content

feat(sire): RVIE Ventas + RCE Compras automation#4

Merged
Railly merged 1 commit into
mainfrom
feat/sire-rvie-rce
Apr 29, 2026
Merged

feat(sire): RVIE Ventas + RCE Compras automation#4
Railly merged 1 commit into
mainfrom
feat/sire-rvie-rce

Conversation

@Railly
Copy link
Copy Markdown
Contributor

@Railly Railly commented Apr 29, 2026

Summary

End-to-end automation for SUNAT SIRE (Sistema Integrado de Registros Electrónicos) — the mandatory monthly electronic books filing system that all CPE emisores must complete since 2024.

Why this matters: SIRE is the biggest monthly tax dolor for any empresa peruana. Late filing = multa hasta 1 UIT (S/5,350 in 2026). Today contadores do this manually in the SOL portal every month. This PR automates 95% of the workflow.

What's in this PR

SIRE-specific OAuth (password grant)

SIRE client (src/sunat-rest/sire.ts)

  • listarPeriodos(codLibro) — GET ejercicios + periodos disponibles
  • descargarPropuesta({codLibro, perTributario}) — async, returns ticket
  • descargarRvie(perTributario) — async, returns ticket
  • aceptarPropuestaRvie(perTributario) — POST, returns ticket
  • consultarTicket(numTicket) — GET status (01/03/06/07/10)
  • descargarArchivo({...}) — streams the ZIP/TXT bytes
  • pollTicket({...}) — backoff 2s/4s/8s/16s/30s, max 5min
  • codLibro: 140000 RVIE | 080000 RCE
  • sireCredentials() builds the password-grant creds shape

Commands

# Setup once
export SUNAT_API_CLIENT_ID=...   # SOL → Credenciales API SUNAT, URI = "MIGE RCE y RVIE - SIRE"
export SUNAT_API_CLIENT_SECRET=...
export SUNAT_RUC=20XXXXXXXXX
export SUNAT_USER=MODDATOS
export SUNAT_PASSWORD='clave-sol'

# Monthly RVIE workflow
sunat sire ventas periodos
sunat sire ventas propuesta --periodo 202404 --wait --out propuesta.zip
sunat sire ventas aceptar --periodo 202404 --yes
sunat sire ventas descargar --periodo 202404 --wait --out rvie.zip

# RCE — same pattern
sunat sire compras periodos
sunat sire compras propuesta --periodo 202404 --wait --out compras.zip
sunat sire compras ticket --num 20240100000123 --wait

--wait polls getStatus until terminal (06=Terminado, 07/10=error). Without --wait, returns ticket for manual polling.

Tests

223 pass / 2 skip / 0 fail in 2.8s (was 209)

New unit tests (14):
- sireCredentials concat RUC+SOL_USER, scope sire
- OAuth password grant: posts to clientessol with grant_type=password
- listarPeriodos hits api-sire host with codLibro in path
- descargarPropuesta: RVIE vs RCE use different endpoints
- descargarRvie hits exportarregistropropuesta
- aceptarPropuestaRvie POSTs to /aceptapropuesta
- consultarTicket: maps registros[0], handles empty
- pollTicket: completed/error/still-processing transitions

What's NOT in this PR (deferred)

Capability Why Path forward
Reemplazar propuesta + Importar comprobantes Uses TUS.IO resumable upload protocol; SUNAT's own note says "deben ser desarrollados en JAVA" Next PR with TUS.IO TS client
Reportes complementarios (resumen, inconsistencias, CAR, casillas) Same async ticket pattern as propuesta, easy adds Add as needed
Tipo de cambio masivo JSON POST, easy add Add when relevant
End-to-end smoke against real SIRE RUC test 20000000001 (PR #1) doesn't have RVIE periods Verify with real RUC + cert post-merge

SUNAT specs honored

All endpoints follow Manual de Servicios Web Api SIRE Ventas v22 (March 2024) verified shapes. See src/sunat-rest/SIRE-RESEARCH.md for full mapping.

SUNAT's note: "los servicios del API SIRE no deben ser consumidos desde un cliente Web, en caso de utilizar un cliente Web se producirá error de CORS". CLI is server-side, no CORS concern.

Test plan

  • bun test green (223 pass / 2 skip / 0 fail)
  • sunat sire --help lists ventas + compras
  • sunat sire ventas --help lists 5 verbs
  • OAuth password grant unit-tested with mocks
  • All endpoints unit-tested with mocks (request URL + method + body shape)
  • CI green on push
  • Verify with real RUC + cert post-merge

Adds end-to-end automation for SUNAT SIRE — the mandatory monthly
electronic books filing system that all CPE emisores must complete
since 2024 (multa up to 1 UIT per late filing).

Replaces the painful SOL portal SIRE workflow with scriptable commands.

What's new:

1. OAuth password grant (SIRE-specific)
   - oauth.ts extended: when username/password provided, switches from
     client_credentials to password grant + clientessol endpoint
   - Same in-process token cache + 401 retry as PR #3
   - CPE consulta (PR #3) keeps working unchanged
   - Three host families now: api-seguridad, api, api-sire

2. SIRE client (src/sunat-rest/sire.ts)
   - listarPeriodos(codLibro) — GET ejercicios y periodos disponibles
   - descargarPropuesta({codLibro, perTributario}) — async, returns ticket
   - descargarRvie(perTributario) — async, returns ticket
   - aceptarPropuestaRvie(perTributario) — POST, returns ticket
   - consultarTicket(numTicket) — GET status (01/03/06/07/10)
   - descargarArchivo({...}) — streams the actual ZIP/TXT bytes
   - pollTicket({...}) — backoff 2s/4s/8s/16s/30s, max 5min
   - codLibro: 140000 RVIE, 080000 RCE
   - sireCredentials() helper builds the password-grant creds shape

3. Commands: sunat sire {ventas|compras} ...
   - periodos          — listar ejercicios y periodos
   - propuesta         — descargar propuesta SUNAT (async, --wait, --out)
   - ticket --num X    — consultar/poll cualquier ticket
   - archivo --nombre  — bajar archivo por nombre + tipo
   - aceptar (ventas)  — T2, requires --yes, accepts SUNAT proposal as-is
   - descargar (ventas)— bajar el RVIE ya generado del periodo

4. Tests (14 nuevos, 223 total)
   - sireCredentials concat RUC+SOL_USER, scope sire
   - OAuth password grant: posts to clientessol with grant_type=password
   - listarPeriodos hits api-sire host with codLibro in path
   - descargarPropuesta: RVIE vs RCE use different endpoints
   - descargarRvie hits exportarregistropropuesta
   - aceptarPropuestaRvie POSTs to /aceptapropuesta
   - consultarTicket: maps registros[0], handles empty
   - pollTicket: completed/error/still-processing transitions

What's deliberately NOT shipped (deferred to follow-up PRs):
- Reemplazar propuesta + Importar comprobantes — TUS.IO resumable upload
  protocol, SUNAT's own note says JAVA client required. Needs TUS.IO
  client work as separate PR.
- Reportes complementarios (resumen, inconsistencias, CAR, casillas, etc) —
  same async pattern, easy adds when needed.
- Tipo de cambio masivo — JSON POST, easy add.
- Smoke test — RUC test 20000000001 (PR #1) doesn't have RVIE periods,
  needs real RUC with billing history. Verify after first prod run.

See src/sunat-rest/SIRE-RESEARCH.md for full endpoint specs.
@Railly Railly marked this pull request as ready for review April 29, 2026 05:28
@Railly Railly merged commit df605c8 into main Apr 29, 2026
1 check passed
Railly added a commit that referenced this pull request Apr 29, 2026
docs: consolidate LIMITATIONS.md across PRs #1-#4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant