afipws 1.3.2 → 2.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 010fac0fb543b010564f089fc4625d0b45d7c7d5f81b713832e0765bc7620cdf
4
- data.tar.gz: acaa441393cd79fcbaac477d1c806e97988a93e3aef64fa9018693bff172b5c7
3
+ metadata.gz: a50ec977b4eb2da94edd06703a11d9ee46b6cfd847de01160a1527871712158e
4
+ data.tar.gz: 3c0aa5ab031957e66b2afea67af73ea1cd91026f67cfea9353877c1b43186dc2
5
5
  SHA512:
6
- metadata.gz: 71cc61d3c240a9832897bc0ee397679cd4c9b0d0867e83c2a7c7e9832c88a144dff0170b9ba7b17a3825e2a7233fec72f93e6badfee6a6ca69269326877448da
7
- data.tar.gz: fc5af32704571cfca4fda6ec345e4fc9d9237f7a00ac65a568f6116a1f09b15482f6af0c7fc5a6b845cf0eeb960c5eef60f92f836334cb49e79b0744e2191ca6
6
+ metadata.gz: 471e56e9c6ceab26acc42b4c2997c2ab9f611ad89bee70400ce5a5d68bc83dc3e89e348af5ce98e727bc6c2363af8936fbcd238f8d1f00942cae3a8a754dae02
7
+ data.tar.gz: d503f3f2df71fc503ddb10bf80985014da6cb03cabc77c5a8a98b4203269e5225c011f66fb125d248e785cb5c3b5b133b32ae8bd2b8c471b1946c8fc1f7dc1ad
@@ -46,6 +46,9 @@ Style/Next:
46
46
  Style/NumericPredicate:
47
47
  Enabled: false
48
48
 
49
+ Style/NumericLiterals:
50
+ Enabled: false
51
+
49
52
  Style/SymbolArray:
50
53
  Enabled: false
51
54
 
@@ -1,21 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- afipws (1.3.2)
4
+ afipws (2.0.0)
5
5
  activesupport
6
6
  builder
7
+ httpclient
7
8
  nokogiri
8
9
  savon (~> 2.11.0)
9
10
 
10
11
  GEM
11
12
  remote: http://rubygems.org/
12
13
  specs:
13
- activesupport (6.0.3.1)
14
+ activesupport (6.0.3.2)
14
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
16
  i18n (>= 0.7, < 2)
16
17
  minitest (~> 5.1)
17
18
  tzinfo (~> 1.1)
18
19
  zeitwerk (~> 2.2, >= 2.2.2)
20
+ addressable (2.7.0)
21
+ public_suffix (>= 2.0.2, < 5.0)
19
22
  akami (1.3.1)
20
23
  gyoku (>= 0.4.0)
21
24
  nokogiri
@@ -23,7 +26,7 @@ GEM
23
26
  builder (3.2.4)
24
27
  byebug (11.0.1)
25
28
  coderay (1.1.2)
26
- concurrent-ruby (1.1.6)
29
+ concurrent-ruby (1.1.7)
27
30
  diff-lcs (1.3)
28
31
  ffi (1.9.25)
29
32
  formatador (0.2.5)
@@ -43,10 +46,11 @@ GEM
43
46
  rspec (>= 2.99.0, < 4.0)
44
47
  gyoku (1.3.1)
45
48
  builder (>= 2.1.2)
46
- httpi (2.4.4)
49
+ httpclient (2.8.3)
50
+ httpi (2.4.5)
47
51
  rack
48
52
  socksify
49
- i18n (1.8.2)
53
+ i18n (1.8.5)
50
54
  concurrent-ruby (~> 1.0)
51
55
  jaro_winkler (1.5.1)
52
56
  listen (3.1.5)
@@ -56,11 +60,11 @@ GEM
56
60
  lumberjack (1.0.13)
57
61
  method_source (0.9.0)
58
62
  mini_portile2 (2.4.0)
59
- minitest (5.14.1)
63
+ minitest (5.14.2)
60
64
  mocha (0.9.10)
61
65
  rake
62
66
  nenv (0.3.0)
63
- nokogiri (1.10.9)
67
+ nokogiri (1.10.10)
64
68
  mini_portile2 (~> 2.4.0)
65
69
  nori (2.6.0)
66
70
  notiffany (0.1.1)
@@ -76,6 +80,7 @@ GEM
76
80
  pry-byebug (3.7.0)
77
81
  byebug (~> 11.0)
78
82
  pry (~> 0.10)
83
+ public_suffix (4.0.5)
79
84
  rack (2.2.3)
80
85
  rainbow (3.0.0)
81
86
  rake (13.0.1)
@@ -120,10 +125,11 @@ GEM
120
125
  tzinfo (1.2.7)
121
126
  thread_safe (~> 0.1)
122
127
  unicode-display_width (1.4.0)
123
- wasabi (3.5.0)
128
+ wasabi (3.6.1)
129
+ addressable
124
130
  httpi (~> 2.0)
125
131
  nokogiri (>= 1.4.2)
126
- zeitwerk (2.3.0)
132
+ zeitwerk (2.4.0)
127
133
 
128
134
  PLATFORMS
129
135
  ruby
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency 'pry-byebug'
27
27
  s.add_dependency "builder"
28
28
  s.add_dependency "savon", '~> 2.11.0'
29
+ s.add_dependency "httpclient"
29
30
  s.add_dependency "nokogiri"
30
31
  s.add_dependency "activesupport"
31
32
  end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'afipws'
5
+
6
+ require 'pry'
7
+ Pry.start
@@ -0,0 +1,16 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
6
+
7
+ ## [2.0.0] - 2020-10-28
8
+
9
+ ### Added
10
+
11
+ - Long overdue CHANGELOG file.
12
+ - Include `httpclient` dependency so we can wrap network errors.
13
+
14
+ ### Changed
15
+
16
+ - Rename Afipws::WSError to Afipws::Error, and create subclasses to distinguish between different types of errors.
@@ -5,12 +5,16 @@ end
5
5
  require 'forwardable'
6
6
  require 'builder'
7
7
  require 'base64'
8
+ require 'httpclient'
8
9
  require 'savon'
9
10
  require 'nokogiri'
10
11
  require 'active_support'
11
12
  require 'active_support/core_ext'
12
13
  require 'afipws/core_ext/hash'
13
- require 'afipws/wserror'
14
+ require 'afipws/errors/error'
15
+ require 'afipws/errors/response_error'
16
+ require 'afipws/errors/server_error'
17
+ require 'afipws/errors/network_error'
14
18
  require 'afipws/type_conversions'
15
19
  require 'afipws/client'
16
20
  require 'afipws/wsaa'
@@ -1,15 +1,19 @@
1
1
  module Afipws
2
2
  class Client
3
3
  def initialize savon_options
4
- @client = Savon.client savon_options.reverse_merge(soap_version: 2, ssl_version: :TLSv1_2)
4
+ @savon = Savon.client savon_options.reverse_merge(soap_version: 2, ssl_version: :TLSv1_2)
5
5
  end
6
6
 
7
7
  def request action, body = nil
8
- @client.call action, message: body
8
+ @savon.call action, message: body
9
+ rescue Savon::SOAPFault => e
10
+ raise ServerError, e
11
+ rescue HTTPClient::TimeoutError => e
12
+ raise NetworkError, e
9
13
  end
10
14
 
11
15
  def operations
12
- @client.operations
16
+ @savon.operations
13
17
  end
14
18
  end
15
19
  end
@@ -4,10 +4,6 @@ module Afipws
4
4
  def select_keys *keys
5
5
  select { |k, _| keys.include? k }
6
6
  end
7
-
8
- def has_entries? entries
9
- entries.each_pair.all? { |k, v| self[k] == v }
10
- end
11
7
  end
12
8
  end
13
9
  end
@@ -0,0 +1,7 @@
1
+ module Afipws
2
+ class Error < StandardError
3
+ def code? _code
4
+ false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Afipws
2
+ class NetworkError < Error
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ module Afipws
2
+ class ResponseError < Error
3
+ attr_reader :errors
4
+
5
+ def initialize errors
6
+ raise ArgumentError, '`errors` must be an array of maps, each with :code and :msg keys' unless errors.is_a? Array
7
+
8
+ super errors.map { |e| "#{e[:code]}: #{e[:msg]}" }.join '; '
9
+ @errors = errors
10
+ end
11
+
12
+ def code? code
13
+ @errors.any? { |e| e[:code].to_s == code.to_s }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ module Afipws
2
+ class ServerError < Error
3
+ end
4
+ end
@@ -44,8 +44,6 @@ module Afipws
44
44
 
45
45
  def request action, body = nil
46
46
  @client.request(action, body).to_hash[:"#{action}_response"]
47
- rescue Savon::SOAPFault => f
48
- raise WSError, f.message
49
47
  end
50
48
  end
51
49
  end
@@ -27,8 +27,6 @@ module Afipws
27
27
 
28
28
  def request action, body = nil
29
29
  @client.request(action, body).to_hash[:"#{action}_response"]
30
- rescue Savon::SOAPFault => f
31
- raise WSError, f.message
32
30
  end
33
31
  end
34
32
  end
@@ -28,8 +28,6 @@ module Afipws
28
28
 
29
29
  def request action, body = nil
30
30
  @client.request(action, body).to_hash[:"#{action}_response"]
31
- rescue Savon::SOAPFault => f
32
- raise WSError, f.message
33
31
  end
34
32
  end
35
33
  end
@@ -1,3 +1,3 @@
1
1
  module Afipws
2
- VERSION = '1.3.2'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -40,12 +40,10 @@ module Afipws
40
40
  def request action, body = nil
41
41
  response = @client.request(action, body).to_hash[:"#{action}_response"][:"#{action}_result"]
42
42
  if response[:lista_errores] && response[:lista_errores][:detalle_error][:codigo] != '0'
43
- raise WSError, Array.wrap(response[:lista_errores][:detalle_error]).map { |e| {code: e[:codigo], msg: e[:descripcion]} }
43
+ raise ResponseError, Array.wrap(response[:lista_errores][:detalle_error]).map { |e| {code: e[:codigo], msg: e[:descripcion]} }
44
44
  else
45
45
  response
46
46
  end
47
- rescue Savon::SOAPFault => f
48
- raise WSError, f.message
49
47
  end
50
48
 
51
49
  def auth
@@ -27,8 +27,6 @@ module Afipws
27
27
 
28
28
  def request action, body = nil
29
29
  @client.request(action, body).to_hash[:"#{action}_response"]
30
- rescue Savon::SOAPFault => f
31
- raise WSError, f.message
32
30
  end
33
31
  end
34
32
  end
@@ -55,8 +55,6 @@ module Afipws
55
55
  generation_time: from_xsd_datetime(ta.css('generationTime').text),
56
56
  expiration_time: from_xsd_datetime(ta.css('expirationTime').text)
57
57
  }
58
- rescue Savon::SOAPFault => f
59
- raise WSError, f.message
60
58
  end
61
59
 
62
60
  def auth
@@ -92,8 +92,8 @@ module Afipws
92
92
 
93
93
  def solicitar_caea
94
94
  convertir_rta_caea request(:fecaea_solicitar, auth.merge(periodo_para_solicitud_caea))
95
- rescue Afipws::WSError => e
96
- if e.errors.any? { |e| e[:code] == '15008' }
95
+ rescue Afipws::ResponseError => e
96
+ if e.code? 15008
97
97
  consultar_caea fecha_inicio_quincena_siguiente
98
98
  else
99
99
  raise
@@ -163,7 +163,7 @@ module Afipws
163
163
  def request action, body = nil
164
164
  response = @client.request(action, body).to_hash[:"#{action}_response"][:"#{action}_result"]
165
165
  if response[:errors]
166
- raise WSError, Array.wrap(response[:errors][:err])
166
+ raise ResponseError, Array.wrap(response[:errors][:err])
167
167
  else
168
168
  response
169
169
  end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module Afipws
4
+ describe Client do
5
+ context 'manejo de errores' do
6
+ subject { Client.new(wsdl: Afipws::WSFE::WSDL[:test]) }
7
+
8
+ it 'Savon::SOAPFault se encapsulan en ServerError' do
9
+ savon.expects(:fe_dummy).returns(fixture('wsaa/login_cms/fault'))
10
+ -> { subject.request :fe_dummy }.should raise_error ServerError, /CMS no es valido/
11
+ end
12
+
13
+ it 'HTTPClient::TimeoutError se encapsulan en NetworkError' do
14
+ # Hack to mock exceptions on Savon
15
+ subject.instance_eval('@savon').expects(:call).raises(HTTPClient::ReceiveTimeoutError, 'execution expired')
16
+ -> { subject.request :fe_dummy }.should raise_error NetworkError, /execution expired/
17
+ end
18
+ end
19
+ end
20
+ end
@@ -10,14 +10,4 @@ describe Hash do
10
10
  hash.select_keys(5, 3).should == {3 => 4}
11
11
  end
12
12
  end
13
-
14
- context 'has_entries?' do
15
- subject { Hash[1, 2, 3, 4] }
16
-
17
- it 'debería devolver true cuando self incluye todas las entries del hash parametro' do
18
- should include 1 => 2
19
- should include 3 => 4, 1 => 2
20
- should_not include 1 => 3
21
- end
22
- end
23
13
  end
@@ -55,7 +55,7 @@ module Afipws
55
55
  savon.expects(:detallada_lista_declaraciones).with(message: :any)
56
56
  .returns(fixture('wconsdeclaracion/detallada_lista_declaraciones/por_id_inexistente'))
57
57
  -> { ws.detallada_lista_declaraciones identificador_declaracion: '...' }
58
- .should raise_error WSError, '21248: Declaracion 19093SIMI000434. inexistente o invalida'
58
+ .should raise_error ResponseError, '21248: Declaracion 19093SIMI000434. inexistente o invalida'
59
59
  end
60
60
  end
61
61
 
@@ -51,7 +51,7 @@ module Afipws
51
51
  savon.expects(:get_persona)
52
52
  .with(message: message.merge(id_persona: '123'))
53
53
  .returns(fixture('ws_sr_constancia_inscripcion/get_persona/fault'))
54
- -> { ws.get_persona '123' }.should raise_error WSError, /No existe persona con ese Id/
54
+ -> { ws.get_persona '123' }.should raise_error ServerError, /No existe persona con ese Id/
55
55
  end
56
56
  end
57
57
 
@@ -39,12 +39,6 @@ module Afipws
39
39
  ta[:generation_time].should == Time.new(2011, 1, 12, 18, 57, 4, '-03:00')
40
40
  ta[:expiration_time].should == Time.new(2011, 1, 13, 6, 57, 4, '-03:00')
41
41
  end
42
-
43
- it 'debería encapsular SOAP Faults' do
44
- subject.stubs(:tra).returns('')
45
- savon.expects(:login_cms).with(message: :any).returns(fixture('wsaa/login_cms/fault'))
46
- -> { subject.login }.should raise_error WSError, /CMS no es valido/
47
- end
48
42
  end
49
43
 
50
44
  context 'auth' do
@@ -70,7 +70,7 @@ module Afipws
70
70
  it 'cuando la moneda no existe' do
71
71
  savon.expects(:fe_param_get_cotizacion).with(message: auth.merge(mon_id: 'PES')).returns(fixture('wsfe/fe_param_get_cotizacion/inexistente'))
72
72
  -> { ws.cotizacion('PES') }.should raise_error { |error|
73
- error.should be_a WSError
73
+ error.should be_a ResponseError
74
74
  error.message.should match /602: Sin Resultados/
75
75
  error.code?(602).should be true
76
76
  error.code?(603).should be false
@@ -195,9 +195,9 @@ module Afipws
195
195
  ws.solicitar_caea.should include caea: '21043476341977', fch_vig_desde: Date.new(2011, 0o2, 0o1)
196
196
  end
197
197
 
198
- it 'cuando hay otro error debería burbujearlo' do
198
+ it 'cuando encapsular errores' do
199
199
  savon.expects(:fecaea_solicitar).with(message: :any).returns(fixture('wsfe/fecaea_solicitar/error_distinto'))
200
- -> { ws.solicitar_caea }.should raise_error WSError, /15007/
200
+ -> { ws.solicitar_caea }.should raise_error ResponseError, /15007/
201
201
  end
202
202
  end
203
203
 
@@ -287,7 +287,7 @@ module Afipws
287
287
  it 'cuando hay un error' do
288
288
  savon.expects(:fe_param_get_tipos_cbte).with(message: :any).returns(fixture('wsfe/fe_param_get_tipos_cbte/failure_1_error'))
289
289
  -> { ws.tipos_comprobantes }.should raise_error { |e|
290
- e.should be_a WSError
290
+ e.should be_a ResponseError
291
291
  e.errors.should == [{ code: '600', msg: 'No se corresponden token con firma' }]
292
292
  e.message.should == '600: No se corresponden token con firma'
293
293
  }
@@ -296,8 +296,11 @@ module Afipws
296
296
  it 'cuando hay varios errores' do
297
297
  savon.expects(:fe_param_get_tipos_cbte).with(message: :any).returns(fixture('wsfe/fe_param_get_tipos_cbte/failure_2_errors'))
298
298
  -> { ws.tipos_comprobantes }.should raise_error { |e|
299
- e.should be_a WSError
300
- e.errors.should == [{ code: '600', msg: 'No se corresponden token con firma' }, { code: '601', msg: 'CUIT representada no incluida en token' }]
299
+ e.should be_a ResponseError
300
+ e.errors.should == [
301
+ { code: '600', msg: 'No se corresponden token con firma' },
302
+ { code: '601', msg: 'CUIT representada no incluida en token' }
303
+ ]
301
304
  e.message.should == '600: No se corresponden token con firma; 601: CUIT representada no incluida en token'
302
305
  }
303
306
  end
@@ -24,14 +24,14 @@ end
24
24
 
25
25
  class HasXPath
26
26
  include RSpec::Matchers
27
-
27
+
28
28
  def initialize(paths)
29
29
  @paths = paths
30
30
  end
31
-
31
+
32
32
  def verify! xml
33
33
  @actual_xml = xml
34
- @paths.each do |(path, value)|
34
+ @paths.each do |(path, value)|
35
35
  @expected_xpath, @expected_value = path, value
36
36
  @actual_xml.should match_xpath @expected_xpath, @expected_value
37
37
  end
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: 1.3.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emmanuel Nicolau
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-15 00:00:00.000000000 Z
11
+ date: 2020-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.11.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: httpclient
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: nokogiri
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -153,7 +167,8 @@ dependencies:
153
167
  description: ''
154
168
  email:
155
169
  - emmanicolau@gmail.com
156
- executables: []
170
+ executables:
171
+ - console
157
172
  extensions: []
158
173
  extra_rdoc_files: []
159
174
  files:
@@ -169,9 +184,15 @@ files:
169
184
  - README.md
170
185
  - Rakefile
171
186
  - afipws.gemspec
187
+ - bin/console
188
+ - lib/CHANGELOG.md
172
189
  - lib/afipws.rb
173
190
  - lib/afipws/client.rb
174
191
  - lib/afipws/core_ext/hash.rb
192
+ - lib/afipws/errors/error.rb
193
+ - lib/afipws/errors/network_error.rb
194
+ - lib/afipws/errors/response_error.rb
195
+ - lib/afipws/errors/server_error.rb
175
196
  - lib/afipws/persona_service_a100.rb
176
197
  - lib/afipws/persona_service_a4.rb
177
198
  - lib/afipws/persona_service_a5.rb
@@ -180,8 +201,8 @@ files:
180
201
  - lib/afipws/w_cons_declaracion.rb
181
202
  - lib/afipws/ws_constancia_inscripcion.rb
182
203
  - lib/afipws/wsaa.rb
183
- - lib/afipws/wserror.rb
184
204
  - lib/afipws/wsfe.rb
205
+ - spec/afipws/client_spec.rb
185
206
  - spec/afipws/core_ext/hash_spec.rb
186
207
  - spec/afipws/persona_service_a100_spec.rb
187
208
  - spec/afipws/persona_service_a4_spec.rb
@@ -255,7 +276,7 @@ files:
255
276
  homepage: https://github.com/eeng/afipws
256
277
  licenses: []
257
278
  metadata: {}
258
- post_install_message:
279
+ post_install_message:
259
280
  rdoc_options: []
260
281
  require_paths:
261
282
  - lib
@@ -272,10 +293,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
272
293
  requirements: []
273
294
  rubyforge_project: afipws
274
295
  rubygems_version: 2.7.3
275
- signing_key:
296
+ signing_key:
276
297
  specification_version: 4
277
298
  summary: Ruby client para los web services de la AFIP
278
299
  test_files:
300
+ - spec/afipws/client_spec.rb
279
301
  - spec/afipws/core_ext/hash_spec.rb
280
302
  - spec/afipws/persona_service_a100_spec.rb
281
303
  - spec/afipws/persona_service_a4_spec.rb
@@ -1,19 +0,0 @@
1
- module Afipws
2
- class WSError < StandardError
3
- attr_reader :errors
4
-
5
- def initialize errors
6
- if errors.is_a? Array
7
- super errors.map { |e| "#{e[:code]}: #{e[:msg]}" }.join '; '
8
- @errors = errors
9
- else
10
- super
11
- @errors = []
12
- end
13
- end
14
-
15
- def code? code
16
- @errors.any? { |e| e[:code].to_s == code.to_s }
17
- end
18
- end
19
- end