afipws 1.3.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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