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/signa 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. # => 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
Billen una sola request.
License
MIT — ver LICENSE.txt.