afipws 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/.gitignore +0 -2
- data/AGENTS.md +35 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +1 -4
- data/README.md +132 -23
- data/lib/afipws/client.rb +16 -0
- data/lib/afipws/version.rb +1 -1
- data/lib/afipws/wsfe.rb +13 -1
- data/mise.toml +14 -0
- data/script/smoke_test.rb +102 -0
- data/spec/afipws/client_spec.rb +6 -0
- data/spec/afipws/wsfe_spec.rb +27 -1
- data/spec/fixtures/wsaa/login_cms/success.xml +1 -1
- data/spec/fixtures/wsfe/fecae_solicitar/autorizacion_1_cbte.xml +1 -1
- data/spec/fixtures/wsfe/fecae_solicitar/autorizacion_2_cbtes.xml +1 -1
- data/spec/fixtures/wsfe/fecae_solicitar/dos_observaciones.xml +1 -1
- data/spec/fixtures/wsfe/fecae_solicitar/una_observacion.xml +1 -1
- data/spec/fixtures/wsfe/fecaea_reg_informativo/informe_rtdo_parcial.xml +1 -1
- metadata +5 -7
- data/.envrc +0 -1
- data/spec/manual/generar_keys.txt +0 -8
- data/spec/manual/test.rb +0 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff3d04f1454611ad13f2c7eb6fd2e8c80f0d190bc2c6ae850bf0ec6ce16625cf
|
|
4
|
+
data.tar.gz: 17a1b14cb8f01aabe85a5e4b497082401592cbda4d7e4a03eae4eff1a40c1296
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2bdb1d9a44bd828b27bc4bb69078143fe7256f8dcd93dd3af8613a736f60d9bfc795e6677f88ba4e94577303bc61805988c2bd0d11ab57647dcf1cc77356fc88
|
|
7
|
+
data.tar.gz: ec6eb8132f4848a6413be77f6b2b854966a6858fdc4443a24dc164bd637532988f3267a132f7b84b30258bd56aeee6d191bcf6ddeef71ea5aa78810b4d5bb9ca
|
data/.github/workflows/ci.yml
CHANGED
data/.gitignore
CHANGED
data/AGENTS.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
|
+
|
|
5
|
+
This repository is the `afipws` Ruby gem, a client for AFIP web services. Public entry points and service implementations live in `lib/`: `lib/afipws.rb` loads the gem, while files such as `wsfe.rb`, `wsaa.rb`, and `persona_service_a100.rb` implement individual services. Shared errors and extensions belong in `lib/afipws/errors/` and `lib/afipws/core_ext/`.
|
|
6
|
+
|
|
7
|
+
RSpec tests mirror production paths under `spec/afipws/` (for example, `lib/afipws/client.rb` maps to `spec/afipws/client_spec.rb`). Keep recorded SOAP XML and WSDL inputs in the corresponding `spec/fixtures/` service directory. Use the README and manual smoke test for runnable usage examples.
|
|
8
|
+
|
|
9
|
+
## Build, Test, and Development Commands
|
|
10
|
+
|
|
11
|
+
Use mise to select Ruby 3.3.4 and run the supported development tasks:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
mise install # install the configured Ruby runtime
|
|
15
|
+
mise run setup # bundle install
|
|
16
|
+
mise run test # run the complete RSpec suite through Rake
|
|
17
|
+
mise run console # start an interactive console with lib/ loaded
|
|
18
|
+
bundle exec guard # rerun affected specs while editing
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
CI performs `bundle install` followed by `bundle exec rake`; run the same test command before submitting changes.
|
|
22
|
+
|
|
23
|
+
## Coding Style & Naming Conventions
|
|
24
|
+
|
|
25
|
+
Follow the existing Ruby style: two-space indentation, `snake_case` methods/files, `CamelCase` classes/modules, and namespaced implementation under `Afipws`. Keep service classes and fixture paths descriptive and aligned (for example, `WSFE` and `spec/fixtures/wsfe/`). Prefer the project’s established RSpec should-style assertions when editing nearby specs.
|
|
26
|
+
|
|
27
|
+
Rubocop configuration is in `.rubocop.yml`: line length is limited to 120 characters, and several modern-style cops are intentionally disabled to preserve the existing codebase style. Do not reformat unrelated files.
|
|
28
|
+
|
|
29
|
+
## Testing Guidelines
|
|
30
|
+
|
|
31
|
+
Add or update a `*_spec.rb` for every behavior change. Tests must not call live AFIP endpoints: use Savon mocks and XML fixtures via `fixture('service/action/result')`. Cover successful responses, SOAP/HTTP failures, and retry behavior when relevant. Run `mise run test` after fixture or service changes.
|
|
32
|
+
|
|
33
|
+
## Commit & Pull Request Guidelines
|
|
34
|
+
|
|
35
|
+
Use short, imperative commit subjects, as in `Add examples`, `Update ruby`, or `Handle Savon::HTTPError`; dependency updates use `Bump <gem> from <old> to <new>`. Keep commits focused. Pull requests should explain the service/API impact, list tests run, link the relevant issue when available, and include sanitized request/response examples when changing SOAP behavior. Never commit production certificates, private keys, or tokens.
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [2.3.1] - 2026-07-26
|
|
8
|
+
|
|
9
|
+
- Fix WSFE production TLS compatibility with ARCA's current endpoint.
|
|
10
|
+
- Preserve custom SSL cipher overrides and limit the compatibility setting to production WSFE.
|
|
11
|
+
- Improve documentation and add a manual production smoke test.
|
|
12
|
+
|
|
7
13
|
## [2.3.0] - 2024-07-13
|
|
8
14
|
|
|
9
15
|
- Upgrade Ruby to 3.3.4
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1,44 +1,153 @@
|
|
|
1
1
|
# Afipws
|
|
2
2
|
|
|
3
|
-
Ruby
|
|
3
|
+
Cliente Ruby para los web services de ARCA (antes AFIP), con soporte para los
|
|
4
|
+
entornos de homologación y producción.
|
|
4
5
|
|
|
5
6
|
[](https://github.com/eeng/afipws/actions/workflows/ci.yml)
|
|
6
7
|
|
|
7
|
-
## Servicios
|
|
8
|
+
## Servicios disponibles
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
| Clase | Servicio WSAA | Uso |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| `WSAA` | — | Autenticación y tickets de acceso |
|
|
13
|
+
| `WSFE` | `wsfe` | Facturación electrónica WSFEv1 |
|
|
14
|
+
| `WSConstanciaInscripcion` | `ws_sr_constancia_inscripcion` | Constancia de inscripción |
|
|
15
|
+
| `PersonaServiceA4` | `ws_sr_padron_a4` | Consultas de padrón |
|
|
16
|
+
| `PersonaServiceA5` | `ws_sr_padron_a5` | Consultas de padrón |
|
|
17
|
+
| `PersonaServiceA100` | `ws_sr_padron_a100` | Parámetros del padrón |
|
|
18
|
+
| `WConsDeclaracion` | `wconsdeclaracion` | Consultas de declaraciones aduaneras |
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
Cada servicio requiere que el certificado esté autorizado para ese servicio en
|
|
21
|
+
ARCA. Autorizar `wsfe` no autoriza automáticamente los servicios de padrón ni
|
|
22
|
+
los demás WSN.
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Luego hay que instalar la librería:
|
|
24
|
+
## Instalación
|
|
22
25
|
|
|
23
26
|
```
|
|
24
27
|
gem install afipws
|
|
25
28
|
```
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
## Certificados y autenticación
|
|
31
|
+
|
|
32
|
+
ARCA utiliza certificados X.509 para autenticar al cliente mediante WSAA.
|
|
33
|
+
Homologación y producción usan certificados y autorizaciones independientes:
|
|
34
|
+
|
|
35
|
+
1. Generá una clave privada y un CSR. El `serialNumber` del CSR debe contener
|
|
36
|
+
la misma CUIT con la que se ingresa a ARCA.
|
|
37
|
+
2. Para homologación, ingresá con clave fiscal a **WSASS – Autoservicio de
|
|
38
|
+
Acceso a APIs de Homologación**, elegí **Nuevo Certificado**, cargá el CSR y
|
|
39
|
+
guardá el certificado PEM devuelto.
|
|
40
|
+
3. En WSASS, creá una autorización para cada servicio que vayas a consumir.
|
|
41
|
+
4. Para producción, gestioná el certificado mediante el **Administrador de
|
|
42
|
+
Certificados Digitales** y asociá cada servicio desde el **Administrador de
|
|
43
|
+
Relaciones de Clave Fiscal**.
|
|
44
|
+
|
|
45
|
+
Documentación oficial: [WSAA](https://www.afip.gob.ar/ws/documentacion/wsaa.asp),
|
|
46
|
+
[certificados](https://www.arca.gob.ar/ws/documentacion/certificados.asp) y
|
|
47
|
+
[catálogo de web services](https://www.afip.gob.ar/ws/documentacion/catalogo.asp).
|
|
48
|
+
|
|
49
|
+
### Generar una clave y un CSR
|
|
50
|
+
|
|
51
|
+
No compartas ni commitees la clave privada.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
mkdir -p keys
|
|
55
|
+
openssl genrsa -out keys/development.key 2048
|
|
56
|
+
openssl req -new \
|
|
57
|
+
-key keys/development.key \
|
|
58
|
+
-subj "/C=AR/O=[organizacion]/CN=[alias]/serialNumber=CUIT [cuit]" \
|
|
59
|
+
-out keys/development.csr
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
El certificado y la clave deben corresponderse. Se puede verificar sin revelar
|
|
63
|
+
su contenido:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
openssl x509 -in keys/development.crt -noout -subject -dates
|
|
67
|
+
openssl x509 -in keys/development.crt -noout -modulus | openssl md5
|
|
68
|
+
openssl rsa -in keys/development.key -noout -modulus | openssl md5
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Ejemplo WSFE
|
|
72
|
+
|
|
73
|
+
El siguiente ejemplo consulta la cotización del dólar en homologación:
|
|
28
74
|
|
|
29
75
|
```ruby
|
|
30
76
|
require 'afipws'
|
|
31
|
-
|
|
77
|
+
|
|
78
|
+
ws = Afipws::WSFE.new(
|
|
79
|
+
env: :development,
|
|
80
|
+
cuit: 'YOUR_CUIT',
|
|
81
|
+
key: File.read('path/to/development.key'),
|
|
82
|
+
cert: File.read('path/to/development.crt')
|
|
83
|
+
)
|
|
84
|
+
|
|
32
85
|
puts ws.cotizacion 'DOL'
|
|
33
86
|
```
|
|
34
87
|
|
|
35
|
-
|
|
88
|
+
Para producción, cambiá `env: :development` por `env: :production` y usá el
|
|
89
|
+
certificado de producción correspondiente.
|
|
90
|
+
|
|
91
|
+
Los endpoints y WSDL se seleccionan automáticamente según `env`. La
|
|
92
|
+
documentación oficial de [facturación electrónica](https://www.afip.gob.ar/ws/documentacion/ws-factura-electronica.asp)
|
|
93
|
+
incluye el manual de WSFEv1 y sus operaciones.
|
|
94
|
+
|
|
95
|
+
### WSFE en producción
|
|
96
|
+
|
|
97
|
+
El endpoint de producción de WSFE utiliza actualmente parámetros Diffie-Hellman
|
|
98
|
+
que OpenSSL rechaza con su nivel de seguridad predeterminado. `WSFE` aplica
|
|
99
|
+
automáticamente una compatibilidad limitada a ese endpoint (`DEFAULT@SECLEVEL=1`):
|
|
100
|
+
mantiene TLS 1.2, la verificación del certificado y no modifica otros servicios.
|
|
101
|
+
El ajuste se puede reemplazar pasando `savon: {ssl_ciphers: '...'}`; debe usarse
|
|
102
|
+
únicamente mientras ARCA mantenga este problema de infraestructura.
|
|
103
|
+
|
|
104
|
+
## Desarrollo
|
|
105
|
+
|
|
106
|
+
Este proyecto usa [mise](https://mise.jdx.dev/) para seleccionar Ruby y ejecutar las tareas de desarrollo. El archivo `.ruby-version` fija la versión de Ruby requerida.
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
mise install
|
|
110
|
+
mise run setup
|
|
111
|
+
mise run test
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
También se puede abrir una consola interactiva con `mise run console`.
|
|
115
|
+
|
|
116
|
+
### Smoke test manual
|
|
36
117
|
|
|
37
|
-
|
|
118
|
+
El script `script/smoke_test.rb` ejecuta consultas de solo lectura contra el
|
|
119
|
+
entorno de homologación. Requiere un certificado vigente y autorizado para los
|
|
120
|
+
servicios consultados:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
ARCA_CUIT='...' \
|
|
124
|
+
ARCA_KEY='path/to/development.key' \
|
|
125
|
+
ARCA_CERT='path/to/development.crt' \
|
|
126
|
+
bundle exec ruby -Ilib script/smoke_test.rb
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
ARCA_ENV=production \
|
|
131
|
+
ARCA_CUIT='...' ARCA_KEY='path/to/production.key' \
|
|
132
|
+
ARCA_CERT='path/to/production.crt' \
|
|
133
|
+
bundle exec ruby -Ilib script/smoke_test.rb
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Las consultas de padrón requieren además `ARCA_PERSONA_ID`. La consulta de
|
|
137
|
+
`wconsdeclaracion` es opcional y requiere `ARCA_RUN_WCONS=1`, además de los
|
|
138
|
+
valores correctos para `ARCA_TIPO_AGENTE` y `ARCA_ROL`.
|
|
139
|
+
|
|
140
|
+
## Contribuir
|
|
141
|
+
|
|
142
|
+
Las contribuciones son bienvenidas. Consultá [AGENTS.md](AGENTS.md) para la
|
|
143
|
+
estructura del proyecto, estilo de código, pruebas y criterios para commits y
|
|
144
|
+
pull requests.
|
|
145
|
+
|
|
146
|
+
Antes de abrir un pull request, ejecutá la suite completa:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
mise run test
|
|
150
|
+
```
|
|
38
151
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
3. Test, test, test (`guard`)
|
|
42
|
-
4. Commit your changes (`git commit -am 'Add some feature'`)
|
|
43
|
-
5. Push to the branch (`git push origin my-new-feature`)
|
|
44
|
-
6. Create new Pull Request
|
|
152
|
+
Incluí una descripción breve del cambio y las pruebas realizadas. No incluyas
|
|
153
|
+
certificados, claves privadas ni credenciales en commits o ejemplos.
|
data/lib/afipws/client.rb
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
module Afipws
|
|
2
|
+
# HTTPI normally expands cipher strings into cipher names before handing
|
|
3
|
+
# them to HTTPClient, which drops OpenSSL security-level modifiers. This
|
|
4
|
+
# marker lets the affected client preserve an explicit cipher expression.
|
|
5
|
+
class RawSSLCipherString < String
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module PreserveRawSSLCipherString
|
|
9
|
+
def ciphers=(ciphers)
|
|
10
|
+
return @ciphers = ciphers.to_s if ciphers.is_a?(RawSSLCipherString)
|
|
11
|
+
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
HTTPI::Auth::SSL.prepend PreserveRawSSLCipherString
|
|
17
|
+
|
|
2
18
|
class Client
|
|
3
19
|
def initialize savon_options
|
|
4
20
|
@savon = Savon.client savon_options.reverse_merge(soap_version: 2, ssl_version: :TLSv1_2)
|
data/lib/afipws/version.rb
CHANGED
data/lib/afipws/wsfe.rb
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
module Afipws
|
|
2
2
|
class WSFE
|
|
3
|
+
# ARCA's production WSFE endpoint currently uses DH parameters rejected by
|
|
4
|
+
# OpenSSL's default security level. Keep this exception local to WSFE's
|
|
5
|
+
# production client; do not weaken TLS settings for other services.
|
|
6
|
+
PRODUCTION_SSL_CIPHERS = 'DEFAULT@SECLEVEL=1'.freeze
|
|
7
|
+
|
|
3
8
|
WSDL = {
|
|
4
9
|
development: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL',
|
|
5
10
|
production: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL',
|
|
@@ -13,7 +18,14 @@ module Afipws
|
|
|
13
18
|
def initialize options = {}
|
|
14
19
|
@cuit = options[:cuit]
|
|
15
20
|
@wsaa = WSAA.new options.merge(service: 'wsfe')
|
|
16
|
-
|
|
21
|
+
savon_options = Hash(options[:savon]).reverse_merge(
|
|
22
|
+
wsdl: WSDL[@wsaa.env],
|
|
23
|
+
convert_request_keys_to: :camelcase
|
|
24
|
+
)
|
|
25
|
+
if @wsaa.env == :production
|
|
26
|
+
savon_options[:ssl_ciphers] ||= RawSSLCipherString.new(PRODUCTION_SSL_CIPHERS)
|
|
27
|
+
end
|
|
28
|
+
@client = Client.new savon_options
|
|
17
29
|
end
|
|
18
30
|
|
|
19
31
|
def dummy
|
data/mise.toml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[tools]
|
|
2
|
+
ruby = "3.3.4"
|
|
3
|
+
|
|
4
|
+
[tasks.setup]
|
|
5
|
+
description = "Install Ruby dependencies"
|
|
6
|
+
run = "bundle install"
|
|
7
|
+
|
|
8
|
+
[tasks.test]
|
|
9
|
+
description = "Run the test suite"
|
|
10
|
+
run = "bundle exec rake"
|
|
11
|
+
|
|
12
|
+
[tasks.console]
|
|
13
|
+
description = "Open the project console"
|
|
14
|
+
run = "bundle exec ruby -Ilib bin/console"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
require 'afipws'
|
|
5
|
+
|
|
6
|
+
environment = (ENV['ARCA_ENV'] || 'development').to_sym
|
|
7
|
+
|
|
8
|
+
cuit = ENV.fetch('ARCA_CUIT')
|
|
9
|
+
key_path = ENV.fetch('ARCA_KEY', 'keys/arca-dev.key')
|
|
10
|
+
cert_path = ENV.fetch('ARCA_CERT', 'keys/arca-dev.crt')
|
|
11
|
+
|
|
12
|
+
options = {
|
|
13
|
+
env: environment,
|
|
14
|
+
cuit: cuit,
|
|
15
|
+
key: File.read(key_path),
|
|
16
|
+
cert: File.read(cert_path),
|
|
17
|
+
ta_path: "tmp/#{cuit}-#{environment}-smoke-wsaa-ta.dump"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
failures = 0
|
|
21
|
+
|
|
22
|
+
def summarize(value)
|
|
23
|
+
case value
|
|
24
|
+
when Array
|
|
25
|
+
"#{value.size} records"
|
|
26
|
+
when Hash
|
|
27
|
+
"#{value.size} fields"
|
|
28
|
+
else
|
|
29
|
+
value.inspect
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def run_check(name)
|
|
34
|
+
value = yield
|
|
35
|
+
puts "PASS #{name} (#{summarize(value)})"
|
|
36
|
+
rescue StandardError => e
|
|
37
|
+
message = e.message.gsub(/\s+/, ' ').strip
|
|
38
|
+
puts "FAIL #{name}: #{e.class}: #{message}"
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def check(name)
|
|
43
|
+
result = yield
|
|
44
|
+
result == false ? 1 : 0
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
wsfe = Afipws::WSFE.new(options.merge(ta_path: "tmp/#{cuit}-#{environment}-smoke-wsfe-ta.dump"))
|
|
48
|
+
failures += check('WSFE dummy') { run_check('WSFE dummy') { wsfe.dummy } }
|
|
49
|
+
failures += check('WSFE cotizacion') { run_check('WSFE cotizacion DOL') { wsfe.cotizacion('DOL') } }
|
|
50
|
+
failures += check('WSFE tipos_comprobantes') { run_check('WSFE tipos_comprobantes') { wsfe.tipos_comprobantes } }
|
|
51
|
+
failures += check('WSFE tipos_documentos') { run_check('WSFE tipos_documentos') { wsfe.tipos_documentos } }
|
|
52
|
+
failures += check('WSFE tipos_concepto') { run_check('WSFE tipos_concepto') { wsfe.tipos_concepto } }
|
|
53
|
+
failures += check('WSFE tipos_monedas') { run_check('WSFE tipos_monedas') { wsfe.tipos_monedas } }
|
|
54
|
+
failures += check('WSFE tipos_iva') { run_check('WSFE tipos_iva') { wsfe.tipos_iva } }
|
|
55
|
+
failures += check('WSFE tipos_tributos') { run_check('WSFE tipos_tributos') { wsfe.tipos_tributos } }
|
|
56
|
+
failures += check('WSFE tipos_opcional') { run_check('WSFE tipos_opcional') { wsfe.tipos_opcional } }
|
|
57
|
+
failures += check('WSFE cant_max_registros_x_lote') { run_check('WSFE cant_max_registros_x_lote') { wsfe.cant_max_registros_x_lote } }
|
|
58
|
+
|
|
59
|
+
a100 = Afipws::PersonaServiceA100.new(options.merge(ta_path: "tmp/#{cuit}-#{environment}-smoke-a100-ta.dump"))
|
|
60
|
+
failures += check('PersonaServiceA100 dummy') { run_check('PersonaServiceA100 dummy') { a100.dummy } }
|
|
61
|
+
failures += check('PersonaServiceA100 jurisdictions') { run_check('PersonaServiceA100 jurisdictions') { a100.jurisdictions } }
|
|
62
|
+
failures += check('PersonaServiceA100 company_types') { run_check('PersonaServiceA100 company_types') { a100.company_types } }
|
|
63
|
+
failures += check('PersonaServiceA100 public_organisms') { run_check('PersonaServiceA100 public_organisms') { a100.public_organisms } }
|
|
64
|
+
|
|
65
|
+
if (persona_id = ENV['ARCA_PERSONA_ID'])
|
|
66
|
+
a4 = Afipws::PersonaServiceA4.new(options.merge(ta_path: "tmp/#{cuit}-#{environment}-smoke-a4-ta.dump"))
|
|
67
|
+
failures += check('PersonaServiceA4 get_persona') { run_check('PersonaServiceA4 get_persona') { a4.get_persona(persona_id) } }
|
|
68
|
+
|
|
69
|
+
a5 = Afipws::PersonaServiceA5.new(options.merge(ta_path: "tmp/#{cuit}-#{environment}-smoke-a5-ta.dump"))
|
|
70
|
+
failures += check('PersonaServiceA5 get_persona') { run_check('PersonaServiceA5 get_persona') { a5.get_persona(persona_id) } }
|
|
71
|
+
|
|
72
|
+
constancia = Afipws::WSConstanciaInscripcion.new(options.merge(ta_path: "tmp/#{cuit}-#{environment}-smoke-constancia-ta.dump"))
|
|
73
|
+
failures += check('WSConstanciaInscripcion get_persona') do
|
|
74
|
+
run_check('WSConstanciaInscripcion get_persona') { constancia.get_persona(persona_id) }
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
puts 'SKIP persona lookups (set ARCA_PERSONA_ID to run them)'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if ENV['ARCA_RUN_WCONS'] == '1'
|
|
81
|
+
wcons = Afipws::WConsDeclaracion.new(
|
|
82
|
+
**options.merge(
|
|
83
|
+
tipo_agente: ENV.fetch('ARCA_TIPO_AGENTE', 'IMEX'),
|
|
84
|
+
rol: ENV.fetch('ARCA_ROL', 'IMEX'),
|
|
85
|
+
ta_path: "tmp/#{cuit}-#{environment}-smoke-wcons-ta.dump"
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
failures += check('WConsDeclaracion dummy') { run_check('WConsDeclaracion dummy') { wcons.dummy } }
|
|
89
|
+
failures += check('WConsDeclaracion list') do
|
|
90
|
+
run_check('WConsDeclaracion list') do
|
|
91
|
+
wcons.detallada_lista_declaraciones(
|
|
92
|
+
fecha_oficializacion_desde: Date.new(2020, 1, 1),
|
|
93
|
+
fecha_oficializacion_hasta: Date.today
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
puts 'SKIP WConsDeclaracion (set ARCA_RUN_WCONS=1 to run it)'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
abort "#{failures} smoke check(s) failed" unless failures.zero?
|
|
102
|
+
puts "Smoke test passed for #{environment}."
|
data/spec/afipws/client_spec.rb
CHANGED
|
@@ -2,6 +2,12 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
module Afipws
|
|
4
4
|
describe Client do
|
|
5
|
+
it 'preserva los modificadores de seguridad OpenSSL en cifrados explícitos' do
|
|
6
|
+
ssl = HTTPI::Auth::SSL.new
|
|
7
|
+
ssl.ciphers = RawSSLCipherString.new('DEFAULT@SECLEVEL=1')
|
|
8
|
+
ssl.ciphers.should == 'DEFAULT@SECLEVEL=1'
|
|
9
|
+
end
|
|
10
|
+
|
|
5
11
|
context 'errores' do
|
|
6
12
|
subject { Client.new(wsdl: Afipws::WSFE::WSDL[:test]) }
|
|
7
13
|
|
data/spec/afipws/wsfe_spec.rb
CHANGED
|
@@ -278,9 +278,35 @@ module Afipws
|
|
|
278
278
|
|
|
279
279
|
it 'debería usar las url para production cuando el env es production' do
|
|
280
280
|
Client.expects(:new).with(wsdl: 'https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl')
|
|
281
|
-
Client.expects(:new).with(
|
|
281
|
+
Client.expects(:new).with(
|
|
282
|
+
wsdl: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL',
|
|
283
|
+
convert_request_keys_to: :camelcase,
|
|
284
|
+
ssl_ciphers: WSFE::PRODUCTION_SSL_CIPHERS
|
|
285
|
+
)
|
|
282
286
|
WSFE.new env: 'production'
|
|
283
287
|
end
|
|
288
|
+
|
|
289
|
+
it 'permite reemplazar los cifrados de production explícitamente' do
|
|
290
|
+
Client.expects(:new).with(
|
|
291
|
+
wsdl: 'https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl',
|
|
292
|
+
ssl_ciphers: 'DEFAULT'
|
|
293
|
+
)
|
|
294
|
+
Client.expects(:new).with(
|
|
295
|
+
wsdl: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL',
|
|
296
|
+
convert_request_keys_to: :camelcase,
|
|
297
|
+
ssl_ciphers: 'DEFAULT'
|
|
298
|
+
)
|
|
299
|
+
WSFE.new env: :production, savon: {ssl_ciphers: 'DEFAULT'}
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it 'no debilita los cifrados fuera de production' do
|
|
303
|
+
Client.expects(:new).with(wsdl: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl')
|
|
304
|
+
Client.expects(:new).with(
|
|
305
|
+
wsdl: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL',
|
|
306
|
+
convert_request_keys_to: :camelcase
|
|
307
|
+
)
|
|
308
|
+
WSFE.new env: :development
|
|
309
|
+
end
|
|
284
310
|
end
|
|
285
311
|
|
|
286
312
|
context 'manejo de errores' do
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<loginTicketResponse version="1">
|
|
3
3
|
<header>
|
|
4
4
|
<source>CN=wsaahomo, O=AFIP, C=AR, SERIALNUMBER=CUIT 33693450239</source>
|
|
5
|
-
<destination>C=ar, O=
|
|
5
|
+
<destination>C=ar, O=john doe, SERIALNUMBER=CUIT 20123456783, CN=acme</destination>
|
|
6
6
|
<uniqueId>1318562072</uniqueId>
|
|
7
7
|
<generationTime>2011-01-12T18:57:04.0-03:00</generationTime>
|
|
8
8
|
<expirationTime>2011-01-13T06:57:04.0-03:00</expirationTime>
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: afipws
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emmanuel Nicolau
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-07-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|
|
@@ -144,12 +144,12 @@ executables:
|
|
|
144
144
|
extensions: []
|
|
145
145
|
extra_rdoc_files: []
|
|
146
146
|
files:
|
|
147
|
-
- ".envrc"
|
|
148
147
|
- ".github/workflows/ci.yml"
|
|
149
148
|
- ".gitignore"
|
|
150
149
|
- ".rspec"
|
|
151
150
|
- ".rubocop.yml"
|
|
152
151
|
- ".ruby-version"
|
|
152
|
+
- AGENTS.md
|
|
153
153
|
- CHANGELOG.md
|
|
154
154
|
- Gemfile
|
|
155
155
|
- Gemfile.lock
|
|
@@ -175,6 +175,8 @@ files:
|
|
|
175
175
|
- lib/afipws/ws_constancia_inscripcion.rb
|
|
176
176
|
- lib/afipws/wsaa.rb
|
|
177
177
|
- lib/afipws/wsfe.rb
|
|
178
|
+
- mise.toml
|
|
179
|
+
- script/smoke_test.rb
|
|
178
180
|
- spec/afipws/client_spec.rb
|
|
179
181
|
- spec/afipws/core_ext/hash_spec.rb
|
|
180
182
|
- spec/afipws/persona_service_a100_spec.rb
|
|
@@ -240,8 +242,6 @@ files:
|
|
|
240
242
|
- spec/fixtures/wsfe/fecaea_solicitar/error_distinto.xml
|
|
241
243
|
- spec/fixtures/wsfe/fecaea_solicitar/success.xml
|
|
242
244
|
- spec/fixtures/wsfe/wsfe.wsdl
|
|
243
|
-
- spec/manual/generar_keys.txt
|
|
244
|
-
- spec/manual/test.rb
|
|
245
245
|
- spec/spec_helper.rb
|
|
246
246
|
- spec/support/fake_wsaa.rb
|
|
247
247
|
- spec/support/matchers.rb
|
|
@@ -334,8 +334,6 @@ test_files:
|
|
|
334
334
|
- spec/fixtures/wsfe/fecaea_solicitar/error_distinto.xml
|
|
335
335
|
- spec/fixtures/wsfe/fecaea_solicitar/success.xml
|
|
336
336
|
- spec/fixtures/wsfe/wsfe.wsdl
|
|
337
|
-
- spec/manual/generar_keys.txt
|
|
338
|
-
- spec/manual/test.rb
|
|
339
337
|
- spec/spec_helper.rb
|
|
340
338
|
- spec/support/fake_wsaa.rb
|
|
341
339
|
- spec/support/matchers.rb
|
data/.envrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
use ruby-version
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Generar clave privada
|
|
2
|
-
openssl genrsa -out test.key 1024
|
|
3
|
-
|
|
4
|
-
# Generar el CSR para enviar a la AFIP
|
|
5
|
-
openssl req -new -key test.key -subj "/C=AR/O=[empresa]/CN=[nombre]/serialNumber=CUIT [nro_cuit]" -out test.csr
|
|
6
|
-
|
|
7
|
-
# Genera un certificado firmado por nosotros, sería el paso que tiene que hacer la AFIP en base al CSR
|
|
8
|
-
openssl req -x509 -days 1095 -out test.crt -verify -key test.key -sha1 -new -batch
|
data/spec/manual/test.rb
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
$LOAD_PATH.unshift(File.expand_path('lib')); require 'afipws'
|
|
2
|
-
|
|
3
|
-
ws = Afipws::WSFE.new env: :development, cuit: '20300032673', cert: File.read('spec/manual/test.crt'), key: File.read('spec/manual/test.key'), savon: {log: true}
|
|
4
|
-
|
|
5
|
-
def obtener_ta ws
|
|
6
|
-
ws.cotizacion 'DOL'
|
|
7
|
-
xml = Builder::XmlMarkup.new indent: 2
|
|
8
|
-
xml.ar :Auth do
|
|
9
|
-
xml.ar :Token, ws.ta[:token]
|
|
10
|
-
xml.ar :Sign, ws.ta[:sign]
|
|
11
|
-
xml.ar :Cuit, ws.cuit
|
|
12
|
-
end
|
|
13
|
-
puts xml.target!
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def autorizar_comprobante ws
|
|
17
|
-
ultimo = ws.ultimo_comprobante_autorizado pto_vta: 1, cbte_tipo: 1
|
|
18
|
-
# cant_informar = ws.cant_max_registros_x_lote
|
|
19
|
-
cant_informar = 2
|
|
20
|
-
puts "Informando #{cant_informar} comprobantes"
|
|
21
|
-
comprobantes = (1..cant_informar).to_a.map do |i|
|
|
22
|
-
{
|
|
23
|
-
cbte_nro: ultimo + i, concepto: 1, doc_nro: 30521189203, doc_tipo: 80, cbte_fch: Date.today,
|
|
24
|
-
imp_total: 1270.48, imp_neto: 1049.98, imp_iva: 220.50, mon_id: 'PES', mon_cotiz: 1,
|
|
25
|
-
iva: { alic_iva: [{ id: 5, base_imp: 1049.98, importe: 220.50 }]}
|
|
26
|
-
}
|
|
27
|
-
end
|
|
28
|
-
puts ws.autorizar_comprobantes(cbte_tipo: 1, pto_vta: 1, comprobantes: comprobantes)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def consultar_caea ws
|
|
32
|
-
ws.consultar_caea Date.new(2011,2,3)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
obtener_ta ws
|