afipws 1.0.3 → 1.0.4

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.
data/lib/afipws/wsfe.rb CHANGED
@@ -1,68 +1,69 @@
1
1
  module Afipws
2
- class WSFE
3
- extend Forwardable
2
+ class WSFE < WSBase
4
3
  include TypeConversions
5
- attr_reader :wsaa, :client, :env
6
- def_delegators :wsaa, :ta, :auth, :cuit
7
4
 
8
5
  WSDL = {
9
- development: "https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL",
10
- # production: "https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL",
11
- production: Root + "/lib/afipws/wsfev1.wsdl",
12
- test: Root + "/spec/fixtures/wsfe.wsdl"
13
- }
6
+ development: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL',
7
+ # production: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL',
8
+ production: Root + '/lib/afipws/wsdl/wsfev1.wsdl',
9
+ test: Root + '/spec/fixtures/wsfe.wsdl'
10
+ }.freeze
14
11
 
15
12
  def initialize options = {}
16
- @env = (options[:env] || :test).to_sym
17
- @wsaa = options[:wsaa] || WSAA.new(options.merge(service: 'wsfe'))
18
- @client = Client.new Hash(options[:savon]).reverse_merge(wsdl: WSDL[@env], ssl_version: :TLSv1, convert_request_keys_to: :camelcase)
13
+ super
14
+ @wsaa = WSAA.new options.merge(service: 'wsfe')
15
+ @client = Client.new Hash(options[:savon]).reverse_merge(wsdl: WSDL[env], convert_request_keys_to: :camelcase)
19
16
  end
20
17
 
21
18
  def dummy
22
- @client.fe_dummy
19
+ request :fe_dummy
23
20
  end
24
21
 
25
22
  def tipos_comprobantes
26
- r = @client.fe_param_get_tipos_cbte auth
23
+ r = request :fe_param_get_tipos_cbte, auth
27
24
  x2r get_array(r, :cbte_tipo), id: :integer, fch_desde: :date, fch_hasta: :date
28
25
  end
29
26
 
30
27
  def tipos_documentos
31
- r = @client.fe_param_get_tipos_doc auth
28
+ r = request :fe_param_get_tipos_doc, auth
32
29
  x2r get_array(r, :doc_tipo), id: :integer, fch_desde: :date, fch_hasta: :date
33
30
  end
34
31
 
35
32
  def tipos_monedas
36
- r = @client.fe_param_get_tipos_monedas auth
33
+ r = request :fe_param_get_tipos_monedas, auth
37
34
  x2r get_array(r, :moneda), fch_desde: :date, fch_hasta: :date
38
35
  end
39
36
 
40
37
  def tipos_iva
41
- r = @client.fe_param_get_tipos_iva auth
38
+ r = request :fe_param_get_tipos_iva, auth
42
39
  x2r get_array(r, :iva_tipo), id: :integer, fch_desde: :date, fch_hasta: :date
43
40
  end
44
41
 
45
42
  def tipos_tributos
46
- r = @client.fe_param_get_tipos_tributos auth
43
+ r = request :fe_param_get_tipos_tributos, auth
47
44
  x2r get_array(r, :tributo_tipo), id: :integer, fch_desde: :date, fch_hasta: :date
48
45
  end
49
46
 
50
47
  def puntos_venta
51
- r = @client.fe_param_get_ptos_venta auth
48
+ r = request :fe_param_get_ptos_venta, auth
52
49
  x2r get_array(r, :pto_venta), nro: :integer, fch_baja: :date, bloqueado: :boolean
53
50
  end
54
51
 
55
52
  def cotizacion moneda_id
56
- @client.fe_param_get_cotizacion(auth.merge(mon_id: moneda_id))[:result_get][:mon_cotiz].to_f
53
+ request(:fe_param_get_cotizacion, auth.merge(mon_id: moneda_id))[:result_get][:mon_cotiz].to_f
57
54
  end
58
55
 
59
56
  def autorizar_comprobantes opciones
60
57
  comprobantes = opciones[:comprobantes]
61
- request = { 'FeCAEReq' => {
62
- 'FeCabReq' => opciones.select_keys(:cbte_tipo, :pto_vta).merge(cant_reg: comprobantes.size),
63
- 'FeDetReq' => { 'FECAEDetRequest' => comprobantes.map { |comprobante| comprobante_to_request comprobante }
64
- }}}
65
- r = @client.fecae_solicitar auth.merge r2x(request, cbte_fch: :date)
58
+ mensaje = {
59
+ 'FeCAEReq' => {
60
+ 'FeCabReq' => opciones.select_keys(:cbte_tipo, :pto_vta).merge(cant_reg: comprobantes.size),
61
+ 'FeDetReq' => {
62
+ 'FECAEDetRequest' => comprobantes.map { |comprobante| comprobante_to_request comprobante }
63
+ }
64
+ }
65
+ }
66
+ r = request :fecae_solicitar, auth.merge(r2x(mensaje, cbte_fch: :date))
66
67
  r = Array.wrap(r[:fe_det_resp][:fecae_det_response]).map do |h|
67
68
  obs = Array.wrap(h[:observaciones] ? h[:observaciones][:obs] : nil)
68
69
  h.select_keys(:cae, :cae_fch_vto, :resultado).merge(cbte_nro: h[:cbte_desde], observaciones: obs)
@@ -78,7 +79,7 @@ module Afipws
78
79
  end
79
80
 
80
81
  def solicitar_caea
81
- convertir_rta_caea @client.fecaea_solicitar auth.merge(periodo_para_solicitud_caea)
82
+ convertir_rta_caea request(:fecaea_solicitar, auth.merge(periodo_para_solicitud_caea))
82
83
  rescue Afipws::WSError => e
83
84
  if e.errors.any? { |e| e[:code] == '15008' }
84
85
  consultar_caea fecha_inicio_quincena_siguiente
@@ -88,18 +89,22 @@ module Afipws
88
89
  end
89
90
 
90
91
  def consultar_caea fecha
91
- convertir_rta_caea @client.fecaea_consultar auth.merge(periodo_para_consulta_caea(fecha))
92
+ convertir_rta_caea request(:fecaea_consultar, auth.merge(periodo_para_consulta_caea(fecha)))
92
93
  end
93
94
 
94
95
  def informar_comprobantes_caea opciones
95
96
  comprobantes = opciones[:comprobantes]
96
- request = { 'FeCAEARegInfReq' => {
97
- 'FeCabReq' => opciones.select_keys(:cbte_tipo, :pto_vta).merge(cant_reg: comprobantes.size),
98
- 'FeDetReq' => { 'FECAEADetRequest' => comprobantes.map do |comprobante|
99
- comprobante_to_request comprobante.merge('CAEA' => comprobante.delete(:caea))
100
- end
101
- }}}
102
- r = @client.fecaea_reg_informativo auth.merge r2x(request, cbte_fch: :date)
97
+ mensaje = {
98
+ 'FeCAEARegInfReq' => {
99
+ 'FeCabReq' => opciones.select_keys(:cbte_tipo, :pto_vta).merge(cant_reg: comprobantes.size),
100
+ 'FeDetReq' => {
101
+ 'FECAEADetRequest' => comprobantes.map do |comprobante|
102
+ comprobante_to_request comprobante.merge('CAEA' => comprobante.delete(:caea))
103
+ end
104
+ }
105
+ }
106
+ }
107
+ r = request :fecaea_reg_informativo, auth.merge(r2x(mensaje, cbte_fch: :date))
103
108
  r = Array.wrap(r[:fe_det_resp][:fecaea_det_response]).map do |h|
104
109
  obs = Array.wrap(h[:observaciones] ? h[:observaciones][:obs] : nil)
105
110
  h.select_keys(:caea, :resultado).merge(cbte_nro: h[:cbte_desde], observaciones: obs)
@@ -108,19 +113,19 @@ module Afipws
108
113
  end
109
114
 
110
115
  def informar_caea_sin_movimientos caea, pto_vta
111
- @client.fecaea_sin_movimiento_informar(auth.merge('CAEA' => caea, 'PtoVta' => pto_vta))
116
+ request :fecaea_sin_movimiento_informar, auth.merge('CAEA' => caea, 'PtoVta' => pto_vta)
112
117
  end
113
118
 
114
119
  def ultimo_comprobante_autorizado opciones
115
- @client.fe_comp_ultimo_autorizado(auth.merge(opciones))[:cbte_nro].to_i
120
+ request(:fe_comp_ultimo_autorizado, auth.merge(opciones))[:cbte_nro].to_i
116
121
  end
117
122
 
118
123
  def consultar_comprobante opciones
119
- @client.fe_comp_consultar(auth.merge(fe_comp_cons_req: opciones))[:result_get]
124
+ request(:fe_comp_consultar, auth.merge(fe_comp_cons_req: opciones))[:result_get]
120
125
  end
121
126
 
122
127
  def cant_max_registros_x_lote
123
- @client.fe_comp_tot_x_request(auth)[:reg_x_req].to_i
128
+ request(:fe_comp_tot_x_request, auth)[:reg_x_req].to_i
124
129
  end
125
130
 
126
131
  def periodo_para_solicitud_caea
@@ -137,7 +142,21 @@ module Afipws
137
142
  hoy.day <= 15 ? hoy.change(day: 16) : hoy.next_month.change(day: 1)
138
143
  end
139
144
 
145
+ def auth
146
+ {auth: wsaa.auth.merge(cuit: cuit)}
147
+ end
148
+
140
149
  private
150
+
151
+ def request action, body = nil
152
+ response = @client.request(action, body).to_hash[:"#{action}_response"][:"#{action}_result"]
153
+ if response[:errors]
154
+ raise WSError, Array.wrap(response[:errors][:err])
155
+ else
156
+ response
157
+ end
158
+ end
159
+
141
160
  def get_array response, array_element
142
161
  Array.wrap response[:result_get][array_element]
143
162
  end
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hash do
4
- context "select_keys" do
5
- it "debería tomar los values de las keys indicadas" do
4
+ context 'select_keys' do
5
+ it 'debería tomar los values de las keys indicadas' do
6
6
  hash = Hash[1, 2, 3, 4]
7
7
  hash.select_keys(1).should == {1 => 2}
8
8
  hash.select_keys(1, 3).should == {1 => 2, 3 => 4}
@@ -10,11 +10,11 @@ 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
13
+
14
+ context 'has_entries?' do
15
15
  subject { Hash[1, 2, 3, 4] }
16
-
17
- it "debería devolver true cuando self incluye todas las entries del hash parametro" do
16
+
17
+ it 'debería devolver true cuando self incluye todas las entries del hash parametro' do
18
18
  should have_entries 1 => 2
19
19
  should have_entries 3 => 4, 1 => 2
20
20
  should_not have_entries 1 => 3
@@ -1,29 +1,28 @@
1
- # coding: utf-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe Afipws::TypeConversions do
5
4
  include Afipws::TypeConversions
6
-
7
- context "r2x" do
8
- it "debería convertir values de hashes a xml types" do
9
- r2x({fecha: Date.new(2011, 1, 2), :id => 1}, fecha: :date).should == {fecha: '20110102', id: 1}
5
+
6
+ context 'r2x' do
7
+ it 'debería convertir values de hashes a xml types' do
8
+ r2x({fecha: Date.new(2011, 1, 2), id: 1}, fecha: :date).should == {fecha: '20110102', id: 1}
10
9
  r2x({container: {fecha: Date.new(2011, 1, 2)}}, fecha: :date).should == {container: {fecha: '20110102'}}
11
10
  end
12
11
 
13
- it "debería convertir values de hashes aunque estén en arrays" do
12
+ it 'debería convertir values de hashes aunque estén en arrays' do
14
13
  r2x([{fecha: Date.new(2011, 1, 2)}], fecha: :date).should == [{fecha: '20110102'}]
15
14
  r2x({container: [{fecha: Date.new(2011, 1, 2)}, {fecha: Date.new(2011, 1, 3)}]}, fecha: :date).should == {container: [{fecha: '20110102'}, {fecha: '20110103'}]}
16
15
  end
17
16
  end
18
-
19
- context "x2r" do
20
- it "deberia convertir values de hashes de xml types a ruby" do
17
+
18
+ context 'x2r' do
19
+ it 'deberia convertir values de hashes de xml types a ruby' do
21
20
  x2r({fecha: '20110102', id: '1', total: '1.23', obs: 'algo'}, fecha: :date, id: :integer, total: :float)
22
- .should == {fecha: Date.new(2011,1,2), id: 1, total: 1.23, obs: 'algo'}
21
+ .should == {fecha: Date.new(2011, 1, 2), id: 1, total: 1.23, obs: 'algo'}
23
22
  x2r({container: {id: '1'}}, id: :integer).should == {container: {id: 1}}
24
23
  end
25
-
26
- it "debería hacer la conversión en arrays también" do
24
+
25
+ it 'debería hacer la conversión en arrays también' do
27
26
  x2r({container: [{id: '1'}, {id: '2'}]}, id: :integer).should == {container: [{id: 1}, {id: 2}]}
28
27
  end
29
28
  end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Afipws
4
+ describe WSConstanciaInscripcion do
5
+ let(:ta) { {token: 't', sign: 's'} }
6
+ let(:ws) { WSConstanciaInscripcion.new(cuit: '1').tap { |ws| ws.wsaa.stubs auth: ta } }
7
+ let(:auth) { ta.merge(cuit_representada: '1') }
8
+
9
+ context 'métodos API' do
10
+ it 'dummy' do
11
+ savon.expects(:dummy).returns(fixture('constancia_inscripcion_dummy/success'))
12
+ ws.dummy.should == { appserver: 'OK', dbserver: 'OK', authserver: 'OK' }
13
+ end
14
+
15
+ it 'debería devolver un hash con los datos generales y regímenes impositivos' do
16
+ savon.expects(:get_persona)
17
+ .with(message: auth.merge(id_persona: '20294834487'))
18
+ .returns(fixture('constancia_inscripcion_get_persona/success'))
19
+ r = ws.get_persona '20294834487'
20
+ r[:datos_generales].should have_entries(
21
+ estado_clave: 'ACTIVO', mes_cierre: '6', razon_social: 'LA REGALERIA S A',
22
+ tipo_clave: 'CUIT', tipo_persona: 'JURIDICA'
23
+ )
24
+ r[:datos_generales][:domicilio_fiscal].should have_entries(
25
+ cod_postal: '2300', descripcion_provincia: 'SANTA FE',
26
+ direccion: 'AV SIEMPRE VIVA 123', localidad: 'NUEVA YORK', tipo_domicilio: 'FISCAL'
27
+ )
28
+ r[:datos_regimen_general][:actividad].should have_entries(
29
+ id_actividad: '477330', nomenclador: '883', orden: '2', periodo: '201311'
30
+ )
31
+ r[:datos_regimen_general][:impuesto][1].should have_entries(
32
+ descripcion_impuesto: 'IVA', id_impuesto: '30', periodo: '198903'
33
+ )
34
+ r[:datos_regimen_general][:regimen].should have_entries(
35
+ id_impuesto: '208', id_regimen: '159', periodo: '199403'
36
+ )
37
+ end
38
+
39
+ it 'cuando hay errores en la constancia sigue la misma lógica' do
40
+ savon.expects(:get_persona).with(message: auth).returns(fixture('constancia_inscripcion_get_persona/failure'))
41
+ r = ws.get_persona '20294834487'
42
+ r[:error_regimen_general].should have_entries(
43
+ error: 'El contribuyente cuenta con impuestos con baja de oficio por Decreto 1299/98',
44
+ mensaje: 'No cumple con las condiciones para enviar datos del regimen general'
45
+ )
46
+ end
47
+ end
48
+
49
+ context 'entorno' do
50
+ it 'debería usar las url para development cuando el env es development' do
51
+ Client.expects(:new).with(wsdl: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl')
52
+ Client.expects(:new).with(wsdl: 'https://awshomo.afip.gov.ar/sr-padron/webservices/personaServiceA5?WSDL', soap_version: 1)
53
+ wsci = WSConstanciaInscripcion.new env: :development
54
+ wsci.env.should == :development
55
+ end
56
+
57
+ it 'debería usar las url para production cuando el env es production' do
58
+ Client.expects(:new).with(wsdl: 'https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl')
59
+ Client.expects(:new).with(wsdl: 'https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA5?WSDL', soap_version: 1)
60
+ wsci = WSConstanciaInscripcion.new env: 'production'
61
+ wsci.env.should == :production
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,75 +1,83 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Afipws::WSAA do
4
- context "generación documento tra" do
5
- it "debería generar xml" do
6
- Time.stubs(:now).returns Time.new(2001, 12, 31, 12, 0, 0, '-03:00')
7
- xml = subject.generar_tra 'wsfe', 2400
8
- xml.should match_xpath "/loginTicketRequest/header/uniqueId", Time.now.to_i.to_s
9
- xml.should match_xpath "/loginTicketRequest/header/generationTime", "2001-12-31T11:20:00-03:00"
10
- xml.should match_xpath "/loginTicketRequest/header/expirationTime", "2001-12-31T12:40:00-03:00"
11
- xml.should match_xpath "/loginTicketRequest/service", "wsfe"
3
+ module Afipws
4
+ describe WSAA do
5
+ context 'generación documento tra' do
6
+ it 'debería generar xml' do
7
+ Time.stubs(:now).returns Time.new(2001, 12, 31, 12, 0, 0, '-03:00')
8
+ xml = subject.generar_tra 'wsfe', 2400
9
+ xml.should match_xpath '/loginTicketRequest/header/uniqueId', Time.now.to_i.to_s
10
+ xml.should match_xpath '/loginTicketRequest/header/generationTime', '2001-12-31T11:20:00-03:00'
11
+ xml.should match_xpath '/loginTicketRequest/header/expirationTime', '2001-12-31T12:40:00-03:00'
12
+ xml.should match_xpath '/loginTicketRequest/service', 'wsfe'
13
+ end
12
14
  end
13
- end
14
15
 
15
- context "firmado del tra" do
16
- it "debería firmar el tra usando el certificado y la clave privada" do
17
- key = File.read(File.dirname(__FILE__) + '/test.key')
18
- crt = File.read(File.dirname(__FILE__) + '/test.crt')
19
- tra = subject.generar_tra 'wsfe', 2400
20
- subject.firmar_tra(tra, key, crt).to_s.should =~ /BEGIN PKCS7/
16
+ context 'firmado del tra' do
17
+ it 'debería firmar el tra usando el certificado y la clave privada' do
18
+ key = File.read(File.dirname(__FILE__) + '/test.key')
19
+ crt = File.read(File.dirname(__FILE__) + '/test.crt')
20
+ tra = subject.generar_tra 'wsfe', 2400
21
+ subject.firmar_tra(tra, key, crt).to_s.should =~ /BEGIN PKCS7/
22
+ end
21
23
  end
22
- end
23
24
 
24
- context "codificación del tra" do
25
- it "debería quitarle el header y footer" do
26
- subject.codificar_tra(OpenSSL::PKCS7.new).should_not include 'BEGIN', 'END'
25
+ context 'codificación del tra' do
26
+ it 'debería quitarle el header y footer' do
27
+ subject.codificar_tra(OpenSSL::PKCS7.new).should_not include 'BEGIN', 'END'
28
+ end
27
29
  end
28
- end
29
30
 
30
- context "login" do
31
- it "debería mandar el TRA al WS y obtener el TA" do
32
- ws = Afipws::WSAA.new key: 'key', cert: 'cert'
33
- ws.expects(:tra).with('key', 'cert', 'wsfe', 2400).returns('tra')
34
- savon.expects(:login_cms).with(message: {in0: 'tra'}).returns(fixture('login_cms/success'))
35
- ta = ws.login
36
- ta[:token].should == 'PD94='
37
- ta[:sign].should == 'i9xDN='
38
- ta[:generation_time].should == Time.new(2011, 1, 12, 18, 57, 4, '-03:00')
39
- ta[:expiration_time].should == Time.new(2011, 1, 13, 6, 57, 4, '-03:00')
40
- end
31
+ context 'login' do
32
+ it 'debería mandar el TRA al WS y obtener el TA' do
33
+ ws = WSAA.new key: 'key', cert: 'cert'
34
+ ws.expects(:tra).with('key', 'cert', 'wsfe', 2400).returns('tra')
35
+ savon.expects(:login_cms).with(message: {in0: 'tra'}).returns(fixture('login_cms/success'))
36
+ ta = ws.login
37
+ ta[:token].should == 'PD94='
38
+ ta[:sign].should == 'i9xDN='
39
+ ta[:generation_time].should == Time.new(2011, 1, 12, 18, 57, 4, '-03:00')
40
+ ta[:expiration_time].should == Time.new(2011, 1, 13, 6, 57, 4, '-03:00')
41
+ end
41
42
 
42
- it "debería encapsular SOAP Faults" do
43
- subject.stubs(:tra).returns('')
44
- savon.expects(:login_cms).with(message: :any).returns(fixture('login_cms/fault'))
45
- lambda { subject.login }.should raise_error Afipws::WSError, /CMS no es valido/
43
+ it 'debería encapsular SOAP Faults' do
44
+ subject.stubs(:tra).returns('')
45
+ savon.expects(:login_cms).with(message: :any).returns(fixture('login_cms/fault'))
46
+ -> { subject.login }.should raise_error WSError, /CMS no es valido/
47
+ end
46
48
  end
47
- end
48
49
 
49
- context "auth" do
50
- before do
51
- FileUtils.rm_rf Dir.glob('tmp/*ta.dump')
52
- Time.stubs(:now).returns(Time.local(2010, 1, 1))
53
- end
50
+ context 'auth' do
51
+ before do
52
+ FileUtils.rm_rf Dir.glob('tmp/*ta.dump')
53
+ Time.stubs(:now).returns(Time.local(2010, 1, 1))
54
+ end
54
55
 
55
- it "debería cachear TA en la instancia y disco" do
56
- ws = Afipws::WSAA.new
57
- ws.expects(:login).once.returns(ta = {token: 'token', sign: 'sign', expiration_time: Time.now + 60})
58
- ws.auth
59
- ws.auth
60
- ws.ta.should equal ta
56
+ it 'debería devolver hash con token y sign' do
57
+ ws = WSAA.new
58
+ ws.expects(:login).once.returns(token: 'token', sign: 'sign', expiration_time: Time.now + 60)
59
+ ws.auth.should == {token: 'token', sign: 'sign'}
60
+ end
61
61
 
62
- ws = Afipws::WSAA.new
63
- ws.auth
64
- ws.ta.should == ta
65
- end
62
+ it 'debería cachear TA en la instancia y disco' do
63
+ ws = WSAA.new
64
+ ws.expects(:login).once.returns(ta = {token: 'token', sign: 'sign', expiration_time: Time.now + 60})
65
+ ws.auth
66
+ ws.auth
67
+ ws.ta.should equal ta
68
+
69
+ ws = WSAA.new
70
+ ws.auth
71
+ ws.ta.should == ta
72
+ end
66
73
 
67
- it "si el TA expiró debería ejecutar solicitar uno nuevo" do
68
- subject.expects(:login).twice.returns(token: 't1', expiration_time: Time.now - 2).then.returns(token: 't2')
69
- subject.auth
70
- subject.ta[:token].should == 't1'
71
- subject.auth
72
- subject.ta[:token].should == 't2'
74
+ it 'si el TA expiró debería ejecutar solicitar uno nuevo' do
75
+ subject.expects(:login).twice.returns(token: 't1', expiration_time: Time.now - 2).then.returns(token: 't2')
76
+ subject.auth
77
+ subject.ta[:token].should == 't1'
78
+ subject.auth
79
+ subject.ta[:token].should == 't2'
80
+ end
73
81
  end
74
82
  end
75
83
  end