0.0
Low commit activity in last 3 years
There's a lot of open issues
A long-lived project that still receives updates
Adaptador para Web Service de Facturación Electrónica Argentina (AFIP)
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

Runtime

~> 2.12.1
 Project Readme

snoopy_afip

Gema Ruby que adapta la Facturación Electrónica de AFIP (Argentina). Expone el módulo Snoopy y resuelve, vía SOAP (savon), los dos servicios de AFIP necesarios para emitir comprobantes electrónicos:

  • WSAA (autenticación): obtiene token/sign a partir de clave privada + certificado.
  • WSFE (facturación electrónica v1): autoriza facturas y notas de crédito, devuelve el CAE.

Versión actual: 4.3.0.

Instalación

# Gemfile
gem 'snoopy_afip'
bundle install
# o
gem install snoopy_afip

Setup (desarrollo)

bundle install
bundle exec rspec   # ver estado de la suite en docs/test/testing.md

Dependencia de runtime: savon ~> 2.12.1. Gestión de Ruby con chruby, dependencias con Bundler.

Configuración

La gema se configura en el host (típicamente config/initializers/snoopy.rb):

Snoopy.default_currency      = :peso
Snoopy.default_concept       = 'Servicios'
Snoopy.default_document_type = 'CUIT'           # alguna key de Snoopy::DOCUMENTS

# Homologación (testing)
Snoopy.auth_url    = 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl'
Snoopy.service_url = 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx?wsdl'
# Producción
# Snoopy.auth_url    = 'https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl'
# Snoopy.service_url = 'https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL'

Snoopy.pkey = '<path-o-PEM-de-la-clave-privada>'   # secreto — nunca commitear
Snoopy.cert = '<path-o-PEM-del-certificado-AFIP>'  # secreto — nunca commitear
Snoopy.cuit = '<CUIT-del-emisor>'

El inventario completo de opciones (tipos, defaults, secretos, failure-mode) está en docs/config/configuracion.md. Nunca incrustar CUIT, claves ni certificados reales en el código ni en el repo.

Uso

1. Autenticar (WSAA)

auth = Snoopy::AuthenticationAdapter.new(pkey: pkey_path, cert: cert_path)
credentials = auth.authenticate!
# => { token: "<token>", sign: "<sign>", expiration_time: <DateTime> }  (válido 12h)

Helpers para generar clave/CSR: Snoopy::AuthenticationAdapter.generate_pkey, .generate_certificate_request_with_ruby, .generate_certificate_request_with_bash. Trámite del certificado: AFIP — Obtener certificado.

2. Crear el comprobante (Bill)

bill = Snoopy::Bill.new(
  cuit:                   cuit,
  sale_point:             sale_point,
  concept:                'Servicios',
  document_type:          'CUIT',
  document_num:           '30710151543',
  issuer_iva_cond:        Snoopy::RESPONSABLE_INSCRIPTO,
  receiver_iva_cond:      :factura_a,           # key de Snoopy::BILL_TYPE → cbte_type
  receiver_iva_condition: :responsable_inscripto,
  total_net:             1000.0,
  alicivas: [ { id: 0.21, amount: 210.0, taxeable_base: 1000.0 } ]
)
bill.valid?   # corre validaciones → bill.errors

alicivas discrimina el IVA por ítem (id = porcentaje, amount = monto del impuesto, taxeable_base = neto sin IVA). Tasas soportadas: Snoopy::ALIC_IVA. Detalle de términos en docs/glossary/glossary.md.

3. Autorizar (WSFE)

adapter = Snoopy::AuthorizeAdapter.new(
  bill:  bill,
  pkey:  pkey, cert: cert, cuit: cuit,
  token: credentials[:token], sign: credentials[:sign]
)
adapter.set_bill_number!   # numera con el último autorizado + 1
adapter.authorize!         # => true/false; setea bill.cae, bill.result, ...

Lecturas de la respuesta: adapter.afip_errors, adapter.afip_events, adapter.afip_observations, adapter.errors, adapter.response. Estado del comprobante: bill.approved?, bill.partial_approved?, bill.rejected?.

Manejo de errores

  • Explota (raise): fallo de comunicación con AFIP (Snoopy::Exception::ServerTimeout, ClientError) → no se autorizó el comprobante.
  • No explota: AFIP respondió pero el parseo de la respuesta falló o devolvió errores de negocio → se acumulan en adapter.errors / afip_errors (parseo en 4 capas independientes para que un cambio de formato de AFIP no tumbe todo).

Catálogo y política completos en docs/errors/errors.md.

Índice de artefactos (docs/)

capa artefacto contenido
interfaz docs/interface/interface.md API Ruby pública (Snoopy, adapters, Bill, constantes)
comportamiento docs/behavior/behavior.md secuencias WSAA / WSFE
glosario docs/glossary/glossary.md términos AFIP (CAE, cbte_type, alicivas, IVA cond)
consumidas docs/consumed/afip.md contrato SOAP consumido (WSAA + WSFE)
errores docs/errors/errors.md jerarquía Snoopy::Exception::* + política
configuración docs/config/configuracion.md inventario de opciones runtime
topología docs/topology/topology.md dependencias + servicios externos
test docs/test/testing.md suite, gaps de cobertura
operaciones (api) n/a gema sin superficie HTTP/CLI propia
datos n/a sin base de datos
eventos · multi-tenancy n/a no aplica

Referencias AFIP

TO DO

  • Mejor parseo de los errores de AFIP (mapear cada código al atributo del Bill).
  • Batch: autorizar un pool de Bill en una sola request.

License

MIT — ver LICENSE.txt.