snoopy_afip 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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/CHANGELOG ADDED
@@ -0,0 +1,23 @@
1
+ *Bravo 0.4.0 (March 21, 2011)*
2
+
3
+ * Auth service URL is now part of the config [miloops]
4
+
5
+ *Bravo 0.3.6 (March 14, 2011)*
6
+
7
+ * Hash extensions play nice with Rails 2.3 ruby 1.9 [miloops]
8
+
9
+ *Bravo 0.3.5 (March 11, 2011)* (in version 0.3.5 this is listed as 0.4.0)
10
+
11
+ * Errors will be raised if CERT or PKEY are not present [miloops]
12
+ * Removed various hardcoded options [leanucci]
13
+ * Added support for more iva conditions [miloops]
14
+ * Log verbosity switch [leanucci]
15
+ * Better spec coverage
16
+
17
+ *Bravo 0.3.0 (March 07, 2011)*
18
+
19
+ * Bill#response returns the full list of parameters passed and returned by FECAESolicitar [leanucci]
20
+
21
+ *Bravo 0.2.0 (March 04, 2011)*
22
+
23
+ * Bill#response returns a complete hash from WSFE response [leanucci]
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ platforms :ruby_18 do
5
+ gem "ruby-debug"
6
+ end
7
+ platforms :ruby_19 do
8
+ gem 'ruby-debug-base19', "0.11.24"
9
+ gem 'ruby-debug19', "0.11.6"
10
+ end
11
+ gem "rspec", "~> 2.4.0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.1"
14
+ gem "rcov", ">= 0"
15
+ end
16
+
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ archive-tar-minitar (0.5.2)
5
+ builder (3.0.0)
6
+ columnize (0.3.2)
7
+ crack (0.1.8)
8
+ diff-lcs (1.1.2)
9
+ git (1.2.5)
10
+ jeweler (1.5.2)
11
+ bundler (~> 1.0.0)
12
+ git (>= 1.2.5)
13
+ rake
14
+ linecache (0.43)
15
+ linecache19 (0.5.11)
16
+ ruby_core_source (>= 0.1.4)
17
+ rake (0.8.7)
18
+ rcov (0.9.9)
19
+ rspec (2.4.0)
20
+ rspec-core (~> 2.4.0)
21
+ rspec-expectations (~> 2.4.0)
22
+ rspec-mocks (~> 2.4.0)
23
+ rspec-core (2.4.0)
24
+ rspec-expectations (2.4.0)
25
+ diff-lcs (~> 1.1.2)
26
+ rspec-mocks (2.4.0)
27
+ ruby-debug (0.10.4)
28
+ columnize (>= 0.1)
29
+ ruby-debug-base (~> 0.10.4.0)
30
+ ruby-debug-base (0.10.4)
31
+ linecache (>= 0.3)
32
+ ruby-debug-base19 (0.11.24)
33
+ columnize (>= 0.3.1)
34
+ linecache19 (>= 0.5.11)
35
+ ruby_core_source (>= 0.1.4)
36
+ ruby-debug19 (0.11.6)
37
+ columnize (>= 0.3.1)
38
+ linecache19 (>= 0.5.11)
39
+ ruby-debug-base19 (>= 0.11.19)
40
+ ruby_core_source (0.1.4)
41
+ archive-tar-minitar (>= 0.5.2)
42
+ savon (0.8.6)
43
+ builder (>= 2.1.2)
44
+ crack (>= 0.1.4)
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ bundler (~> 1.0.0)
51
+ jeweler (~> 1.5.1)
52
+ rcov
53
+ rspec (~> 2.4.0)
54
+ ruby-debug
55
+ ruby-debug-base19 (= 0.11.24)
56
+ ruby-debug19 (= 0.11.6)
57
+ savon (= 0.8.6)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Leandro Marcucci & Vurbia Technologies International Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "snoopy_afip"
16
+ gem.homepage = "http://github.com/Vurbia/Bravo"
17
+ gem.license = "MIT"
18
+ gem.summary = "Adaptador AFIP wsfe."
19
+ gem.description = "Adaptador para el Web Service de Facturacion Electronica de AFIP"
20
+ gem.email = "eserdio@sequre.com.ar"
21
+ gem.authors = ["Enrique Serdio"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "snoopy_afip #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" } #added according to rspec2 book
@@ -0,0 +1,46 @@
1
+ # coding: utf-8
2
+ module Snoopy
3
+ module AuthData
4
+ def generate_auth_file
5
+ Snoopy::AuthData.generate_auth_file(:cuit => cuit, :pkey => pkey, :cert => cert)
6
+
7
+ todays_datafile = "/tmp/snoopy_afip_#{cuit}_#{Time.new.strftime('%d_%m_%Y')}.yml"
8
+ current_token_sign_file = YAML.load_file(todays_datafile)
9
+
10
+ { "Token" => current_token_sign_file["token"], "Sign" => current_token_sign_file["sign"], "Cuit" => cuit }
11
+ end
12
+
13
+ def self.generate_auth_file invoicing_firm
14
+ raise "Debe definir el cuit del emisor" unless invoicing_firm[:cuit]
15
+ raise "Archivo de llave privada no encontrado en #{pkey}" unless File.exists?(invoicing_firm[:pkey])
16
+ raise "Archivo certificado no encontrado en #{cert}" unless File.exists?(invoicing_firm[:cert])
17
+
18
+ todays_datafile = "/tmp/snoopy_afip_#{invoicing_firm[:cuit]}_#{Time.new.strftime('%d_%m_%Y')}.yml"
19
+ opts = "-u #{Snoopy.auth_url} -k #{invoicing_firm[:pkey]} -c #{invoicing_firm[:cert]} -i #{invoicing_firm[:cuit]}"
20
+
21
+ %x(#{File.dirname(__FILE__)}/../../wsaa-client.sh #{opts}) unless File.exists?(todays_datafile)
22
+ end
23
+
24
+ def self.generate_pkey file
25
+ begin
26
+ %x(openssl genrsa -out #{file} 8192)
27
+ rescue => e
28
+ raise "command fail: 'openssl genrsa -out #{file} 8192' error al generar pkey: #{e.message}, error: #{e.message}"
29
+ end
30
+ end
31
+
32
+ # pkey: clave privada generada por el metodo generate_pkey.
33
+ # subj_o: Nombre de la empresa, registrado en AFIP.
34
+ # subj_cn: hostname del servidor que realizara la comunicación con AFIP.
35
+ # subj_cuit: Cuit registado en AFIP.
36
+ # out_path: donde se almacenara el certificado generado.
37
+ # Snoopy::AuthData.generate_certificate_request(generate_pkey, subj_o, subj_cn, subj_cuit, tmp_cert_req_path)
38
+ def generate_certificate_request(pkey, subj_o, subj_cn, subj_cuit, out_path)
39
+ begin
40
+ %x(openssl req -new -key #{pkey} -subj "/C=AR/O=#{subj_o}/CN=#{subj_cn}/serialNumber=CUIT #{subj_cuit}" -out #{out_path})
41
+ rescue => e
42
+ raise "command fail: openssl req -new -key #{pkey} -subj /C=AR/O=#{subj_o}/CN=#{subj_cn}/serialNumber=CUIT #{subj_cuit} -out #{out_path}, error: #{e.message}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module Snoopy
2
+ class Authorizer
3
+ attr_reader :pkey, :cert
4
+
5
+ def initialize
6
+ @pkey = Snoopy.pkey
7
+ @cert = Snoopy.cert
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,235 @@
1
+ module Snoopy
2
+ class Bill
3
+ include AuthData
4
+
5
+ attr_reader :base_imp, :total
6
+
7
+ attr_accessor :neto_total, :numero_documento, :condicion_iva_receptor, :tipo_documento, :concepto, :moneda,
8
+ :due_date, :aliciva_id, :fecha_servicio_desde, :fecha_servicio_hasta, :body,
9
+ :response, :cbte_asoc_num, :cbte_asoc_pto_venta, :numero, :alicivas,
10
+ :pkey, :cert, :cuit, :punto_venta, :condicion_iva_emisor, :auth, :errors,
11
+ :cae, :resultado, :fecha_proceso, :vencimiento_cae, :fecha_comprobante,
12
+ :observaciones, :events, :imp_iva
13
+
14
+ def initialize(attrs={})
15
+ @pkey = attrs[:pkey]
16
+ @cert = attrs[:cert]
17
+ @cuit = attrs[:cuit]
18
+ @punto_venta = attrs[:sale_point]
19
+ @condicion_iva_emisor = attrs[:own_iva_cond]
20
+ @neto_total = attrs[:net] || 0
21
+ @tipo_documento = attrs[:documento] || Snoopy.default_documento
22
+ @moneda = attrs[:moneda] || Snoopy.default_moneda
23
+ @concepto = attrs[:concepto] || Snoopy.default_concepto
24
+ @numero_documento = attrs[:doc_num]
25
+ @fecha_servicio_desde = attrs[:fch_serv_desde]
26
+ @fecha_servicio_hasta = attrs[:fch_serv_hasta]
27
+ @cbte_asoc_pto_venta = attrs[:cbte_asoc_pto_venta] # Esto es el punto de venta de la factura para la nota de credito
28
+ @cbte_asoc_num = attrs[:cbte_asoc_num] # Esto es el numero de factura para la nota de credito
29
+ @condicion_iva_receptor = attrs[:iva_cond]
30
+ @alicivas = attrs[:alicivas]
31
+ @imp_iva = attrs[:imp_iva] # Monto total de impuestos
32
+ @response = nil
33
+ @errors = []
34
+ @observaciones = []
35
+ @events = []
36
+ end
37
+
38
+ def exchange_rate
39
+ return 1 if moneda == :peso
40
+ response = client.fe_param_get_cotizacion do |soap|
41
+ soap.namespaces["xmlns"] = "http://ar.gov.afip.dif.FEV1/"
42
+ soap.body = body.merge!({"MonId" => Snoopy::MONEDAS[moneda][:codigo]})
43
+ end
44
+ response.to_hash[:fe_param_get_cotizacion_response][:fe_param_get_cotizacion_result][:result_get][:mon_cotiz].to_f
45
+ end
46
+
47
+ def total
48
+ @total = neto_total.zero? ? 0 : (neto_total + iva_sum).round(2)
49
+ end
50
+
51
+ def iva_sum
52
+ @iva_sum = alicivas.collect{|aliciva| aliciva[:importe] }.sum.to_f.round_with_precision(2)
53
+ end
54
+
55
+ def cbte_type
56
+ Snoopy::BILL_TYPE[condicion_iva_receptor.to_sym] || raise(NullOrInvalidAttribute.new, "Please choose a valid document type.")
57
+ end
58
+
59
+ def client
60
+ Savon.client( :wsdl => Snoopy.service_url,
61
+ :ssl_cert_key_file => pkey,
62
+ :ssl_cert_file => cert,
63
+ :ssl_version => :TLSv1,
64
+ :read_timeout => 90,
65
+ :open_timeout => 90,
66
+ :headers => { "Accept-Encoding" => "gzip, deflate", "Connection" => "Keep-Alive" },
67
+ :pretty_print_xml => true,
68
+ :namespaces => {"xmlns" => "http://ar.gov.afip.dif.FEV1/"} )
69
+ end
70
+
71
+ def set_bill_number!
72
+ resp = client.call( :fe_comp_ultimo_autorizado,
73
+ :message => { "Auth" => generate_auth_file, "PtoVta" => punto_venta, "CbteTipo" => cbte_type })
74
+
75
+ resp_errors = resp.hash[:envelope][:body][:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:errors]
76
+ resp_errors.each_value { |value| @errors << "Código #{value[:code]}: #{value[:msg]}" } unless resp_errors.nil?
77
+ @numero = resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1 if @errors.empty?
78
+ end
79
+
80
+ def build_header
81
+ { "CantReg" => "1", "CbteTipo" => cbte_type, "PtoVta" => punto_venta }
82
+ end
83
+
84
+ def build_body_request
85
+ today = Time.new.in_time_zone('Buenos Aires').strftime('%Y%m%d')
86
+
87
+ fecaereq = {"FeCAEReq" => {
88
+ "FeCabReq" => build_header,
89
+ "FeDetReq" => {
90
+ "FECAEDetRequest" => {
91
+ "Concepto" => Snoopy::CONCEPTOS[concepto],
92
+ "DocTipo" => Snoopy::DOCUMENTOS[tipo_documento],
93
+ "CbteFch" => today,
94
+ "ImpTotConc" => 0.00,
95
+ "MonId" => Snoopy::MONEDAS[moneda][:codigo],
96
+ "MonCotiz" => exchange_rate,
97
+ "ImpOpEx" => 0.00,
98
+ "ImpTrib" => 0.00 }}}}
99
+
100
+ unless condicion_iva_emisor == :responsable_monotributo
101
+ _alicivas = alicivas.collect do |aliciva|
102
+ { "Id" => Snoopy::ALIC_IVA[aliciva[:id]],
103
+ "BaseImp" => aliciva[:base_imp],
104
+ "Importe" => aliciva[:importe] }
105
+ end
106
+ fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]["Iva"] = { "AlicIva" => _alicivas }
107
+ end
108
+
109
+ detail = fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
110
+
111
+ detail["DocNro"] = numero_documento
112
+ detail["ImpNeto"] = neto_total.to_f
113
+ detail["ImpIVA"] = iva_sum
114
+ detail["ImpTotal"] = total
115
+
116
+ detail["CbteDesde"] = detail["CbteHasta"] = numero
117
+
118
+ unless concepto == "Productos"
119
+ detail.merge!({ "FchServDesde" => fecha_servicio_desde || today,
120
+ "FchServHasta" => fecha_servicio_hasta || today,
121
+ "FchVtoPago" => due_date || today})
122
+ end
123
+
124
+ if self.condicion_iva_receptor.to_s.include?("nota_credito")
125
+ detail.merge!({"CbtesAsoc" => {"CbteAsoc" => {"Nro" => cbte_asoc_num,
126
+ "PtoVta" => cbte_asoc_pto_venta,
127
+ "Tipo" => cbte_type }}})
128
+ end
129
+
130
+ @body = { "Auth" => generate_auth_file }.merge!(fecaereq)
131
+ end
132
+
133
+ def cae_request
134
+ begin
135
+ set_bill_number!
136
+ if @errors.empty?
137
+ build_body_request
138
+ @response = client.call( :fecae_solicitar, :message => body ).body
139
+ parse_fecae_solicitar_response
140
+ end
141
+ rescue => e #Curl::Err::GotNothingError, Curl::Err::TimeoutError
142
+ @errors = e.message
143
+ end
144
+ !@response.nil?
145
+ end
146
+
147
+ def aprobada?; @resultado == "A"; end
148
+ def parcial?; @resultado == "P"; end
149
+ def rechazada?; @resultado == "R"; end
150
+
151
+ def parse_observations(fecae_observations)
152
+ begin
153
+ fecae_observations.each_value do |obs|
154
+ [obs].flatten.each { |ob| @observaciones << "Código #{ob[:code]}: #{ob[:msg]}" }
155
+ end
156
+ rescue
157
+ @observaciones << "Ocurrió un error al parsear las observaciones de AFIP"
158
+ end
159
+ end
160
+
161
+ def parse_errors(fecae_errors)
162
+ begin
163
+ fecae_errors.each_value do |errores|
164
+ [errores].flatten.each { |error| @errors << "Código #{error[:code]}: #{error[:msg]}" }
165
+ end
166
+ rescue
167
+ @errors << "Ocurrió un error al parsear los errores de AFIP"
168
+ end
169
+ end
170
+
171
+ def parse_events(fecae_events)
172
+ begin
173
+ fecae_events.each_value do |events|
174
+ [events].flatten.each { |event| @events << "Código #{event[:code]}: #{event[:msg]}" }
175
+ end
176
+ rescue
177
+ @events << "Ocurrió un error al parsear los eventos de AFIP"
178
+ end
179
+ end
180
+
181
+ def parse_fecae_solicitar_response
182
+ fecae_response = @response[:fecae_solicitar_response][:fecae_solicitar_result][:fe_det_resp][:fecae_det_response] rescue {}
183
+ fe_cab_resp = @response[:fecae_solicitar_response][:fecae_solicitar_result][:fe_cab_resp] rescue {}
184
+ fecae_result = @response[:fecae_solicitar_response][:fecae_solicitar_result] rescue {}
185
+
186
+ @cae = fecae_response[:cae]
187
+ @numero = fecae_response[:cbte_desde]
188
+ @resultado = fecae_response[:resultado]
189
+ @vencimiento_cae = fecae_response[:cae_fch_vto]
190
+ @fecha_comprobante = fecae_response[:cbte_fch]
191
+
192
+ @fecha_proceso = fe_cab_resp[:fch_proceso]
193
+
194
+ parse_observations(fecae_response.delete(:observaciones)) if fecae_response.has_key? :observaciones
195
+ parse_errors(fecae_result[:errors]) if fecae_result.has_key? :errors
196
+ parse_events(fecae_result[:events]) if fecae_result.has_key? :events
197
+ end
198
+
199
+ def parse_fe_comp_consultar_response
200
+ result_get = @response.to_hash[:fe_comp_consultar_response][:fe_comp_consultar_result][:result_get]
201
+ fe_comp_consultar_result = @response.to_hash[:fe_comp_consultar_response][:fe_comp_consultar_result]
202
+
203
+ unless result_get.nil?
204
+ @cae = result_get[:cod_autorizacion]
205
+ @imp_iva = result_get[:imp_iva]
206
+ @numero = result_get[:cbte_desde]
207
+ @resultado = result_get[:resultado]
208
+ @fecha_proceso = result_get[:fch_proceso]
209
+ @vencimiento_cae = result_get[:fch_vto]
210
+ @numero_documento = result_get[:doc_numero]
211
+ @fecha_comprobante = result_get[:cbte_fch]
212
+ @fecha_servicio_desde = result_get[:fch_serv_desde]
213
+ @fecha_servicio_hasta = result_get[:fch_serv_hasta]
214
+ parse_events(result_get[:observaciones]) if result_get.has_key? :observaciones
215
+ end
216
+
217
+ self.parse_events(fe_comp_consultar_result[:errors]) if fe_comp_consultar_result and fe_comp_consultar_result.has_key? :errors
218
+ self.parse_events(fe_comp_consultar_result[:events]) if fe_comp_consultar_result and fe_comp_consultar_result.has_key? :events
219
+ end
220
+
221
+ def self.bill_request(numero, attrs={})
222
+ bill = new(attrs)
223
+ begin
224
+ bill.response = bill.client.call( :fe_comp_consultar,
225
+ :message => {"Auth" => bill.generate_auth_file,
226
+ "FeCompConsReq" => {"CbteTipo" => bill.cbte_type, "PtoVta" => bill.punto_venta, "CbteNro" => numero.to_s}})
227
+ bill.parse_fe_comp_consultar_response
228
+ rescue => e
229
+ bill.errors << e.message
230
+ end
231
+ bill
232
+ end
233
+
234
+ end
235
+ end
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+ module Snoopy
3
+ CBTE_TIPO = { "01" => "Factura A",
4
+ "02" => "Nota de Débito A",
5
+ "03" => "Nota de Crédito A",
6
+ "04" => "Recibos A",
7
+ "05" => "Notas de Venta al contado A",
8
+ "06" => "Factura B",
9
+ "07" => "Nota de Debito B",
10
+ "08" => "Nota de Credito B",
11
+ "09" => "Recibos B",
12
+ "10" => "Notas de Venta al contado B",
13
+ "11" => "Factura C",
14
+ "13" => "Nota de Crédito C",
15
+ "34" => "Cbtes. A del Anexo I, Apartado A,inc.f),R.G.Nro. 1415",
16
+ "35" => "Cbtes. B del Anexo I,Apartado A,inc. f),R.G. Nro. 1415",
17
+ "39" => "Otros comprobantes A que cumplan con R.G.Nro. 1415",
18
+ "40" => "Otros comprobantes B que cumplan con R.G.Nro. 1415",
19
+ "60" => "Cta de Vta y Liquido prod. A",
20
+ "61" => "Cta de Vta y Liquido prod. B",
21
+ "63" => "Liquidacion A",
22
+ "64" => "Liquidacion B" }
23
+
24
+ CONCEPTOS = { "Productos" => "01",
25
+ "Servicios" => "02",
26
+ "Productos y Servicios" => "03" }
27
+
28
+ DOCUMENTOS = { "CUIT" => "80",
29
+ "CUIL" => "86",
30
+ "CDI" => "87",
31
+ "LE" => "89",
32
+ "LC" => "90",
33
+ "CI Extranjera" => "91",
34
+ "en tramite" => "92",
35
+ "Acta Nacimiento" => "93",
36
+ "CI Bs. As. RNP" => "95",
37
+ "DNI" => "96",
38
+ "Pasaporte" => "94",
39
+ "Doc. (Otro)" => "99",
40
+ "CI Policía Federal" => '00',
41
+ "CI Buenos Aires" => '01',
42
+ "CI Catamarca" => '02',
43
+ "CI Córdoba" => '03',
44
+ "CI Corrientes" => '04',
45
+ "CI Entre Ríos" => '05',
46
+ "CI Jujuy" => '06',
47
+ "CI Mendoza" => '07',
48
+ "CI La Rioja" => '08',
49
+ "CI Salta" => '09',
50
+ "CI San Juan" => '10',
51
+ "CI San Luis" => '11',
52
+ "CI Santa Fe" => '12',
53
+ "CI Santiago del Estero" => '13',
54
+ "CI Tucumán" => '14',
55
+ "CI Chaco" => '16',
56
+ "CI Chubut" => '17',
57
+ "CI Formosa" => '18',
58
+ "CI Misiones" => '19',
59
+ "CI Neuquén" => '20',
60
+ "CI La Pampa" => '21',
61
+ "CI Río Negro" => '22',
62
+ "CI Santa Cruz" => '23',
63
+ "CI Tierra del Fuego" => '24' }
64
+
65
+ MONEDAS = { :peso => { :codigo => 'PES', :nombre => 'Pesos Argentinos' },
66
+ :dolar => { :codigo => 'DOL', :nombre => 'Dolar Estadounidense' },
67
+ :real => { :codigo => '012', :nombre => 'Real' },
68
+ :euro => { :codigo => '060', :nombre => 'Euro' },
69
+ :oro => { :codigo => '049', :nombre => 'Gramos de Oro Fino' } }
70
+
71
+ ALIC_IVA = { 0 => '3',
72
+ 0.025 => '9',
73
+ 0.05 => '8',
74
+ 0.105 => '4',
75
+ 0.21 => '5',
76
+ 0.27 => '6' }
77
+
78
+ BILL_TYPE = { :factura_a => '01',
79
+ :factura_b => '06',
80
+ :factura_c => '11',
81
+ :nota_credito_a => '03',
82
+ :nota_credito_b => '08',
83
+ :nota_credito_c => '13' }
84
+
85
+ end
@@ -0,0 +1,8 @@
1
+ class Float
2
+ def round_with_precision(precision = nil)
3
+ precision.nil? ? round : (self * (10 ** precision)).round / (10 ** precision).to_f
4
+ end
5
+ def round_up_with_precision(precision = nil)
6
+ precision.nil? ? round : ((self * (10 ** precision)).round + 1) / (10 ** precision).to_f
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ class Hash
2
+ def symbolize_keys!
3
+ keys.each do |key|
4
+ self[(key.to_sym rescue key) || key] = delete(key)
5
+ end
6
+ self
7
+ end unless method_defined?(:symbolize_keys!)
8
+
9
+ def symbolize_keys
10
+ dup.symbolize_keys!
11
+ end unless method_defined?(:symbolize_keys)
12
+
13
+ def underscore_keys!
14
+ keys.each do |key|
15
+ self[(key.underscore rescue key) || key] = delete(key)
16
+ end
17
+ self
18
+ end unless method_defined?(:underscore_keys!)
19
+
20
+ def underscore_keys
21
+ dup.underscore_keys!
22
+ end unless method_defined?(:underscore_keys)
23
+ end
@@ -0,0 +1,12 @@
1
+ # Stolen from activesupport/lib/active_support/inflector/methods.rb, line 48
2
+ class String
3
+ def underscore
4
+ word = self.to_s.dup
5
+ word.gsub!(/::/, '/')
6
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
7
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
8
+ word.tr!("-", "_")
9
+ word.downcase!
10
+ word
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module Snoopy
2
+ VERSION = "2.0.0"
3
+ end
@@ -0,0 +1,58 @@
1
+ require "bundler/setup"
2
+ require "snoopy_afip/version"
3
+ require "snoopy_afip/constants"
4
+ require "savon"
5
+ require "snoopy_afip/core_ext/float"
6
+ require "snoopy_afip/core_ext/hash"
7
+ require "snoopy_afip/core_ext/string"
8
+ module Snoopy
9
+
10
+ class NullOrInvalidAttribute < StandardError; end
11
+
12
+ autoload :Authorizer, "snoopy_afip/authorizer"
13
+ autoload :AuthData, "snoopy_afip/auth_data"
14
+ autoload :Bill, "snoopy_afip/bill"
15
+ autoload :Constants, "snoopy_afip/constants"
16
+
17
+
18
+ extend self
19
+ attr_accessor :cuit, :sale_point, :service_url, :default_documento, :pkey, :cert,
20
+ :default_concepto, :default_moneda, :own_iva_cond, :verbose, :auth_url
21
+
22
+ def auth_hash
23
+ {"Token" => Snoopy::TOKEN, "Sign" => Snoopy::SIGN, "Cuit" => Snoopy.cuit}
24
+ end
25
+
26
+ def bill_types
27
+ [
28
+ ["Factura A", "01"],
29
+ # ["Nota de Débito A", "02"],
30
+ ["Nota de Crédito A", "03"],
31
+ # ["Recibos A", "04"],
32
+ # ["Notas de Venta al contado A", "05"],
33
+ ["Factura B", "06"],
34
+ # ["Nota de Debito B", "07"],
35
+ ["Nota de Credito B", "08"],
36
+ # ["Recibos B", "09"],
37
+ # ["Notas de Venta al contado B", "10"],
38
+ ["Factura C", "11"],
39
+ ["Nota de Crédito C", "13"],
40
+ # ["Cbtes. A del Anexo I, Apartado A,inc.f),R.G.Nro. 1415", "34"],
41
+ # ["Cbtes. B del Anexo I,Apartado A,inc. f),R.G. Nro. 1415", "35"],
42
+ # ["Otros comprobantes A que cumplan con R.G.Nro. 1415", "39"],
43
+ # ["Otros comprobantes B que cumplan con R.G.Nro. 1415", "40"],
44
+ # ["Cta de Vta y Liquido prod. A", "60"],
45
+ # ["Cta de Vta y Liquido prod. B", "61"],
46
+ # ["Liquidacion A", "63"],
47
+ # ["Liquidacion B, "64""]
48
+ ]
49
+ end
50
+
51
+ # Savon::Request.log = false unless (Snoopy.verbose == "true") || (ENV["VERBOSE"] == true)
52
+
53
+ # Savon.configure do |config|
54
+ # config.log = Snoopy.log?
55
+ # config.log_level = :debug
56
+ # end
57
+
58
+ end
@@ -0,0 +1,68 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'snoopy_afip/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "snoopy_afip"
8
+ s.version = Snoopy::VERSION
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["g.edera, eserdio"]
12
+ s.date = "2016-09-12"
13
+ s.description = "Adaptador para el Web Service de Facturacion Electronica de AFIP"
14
+ s.email = ["gab.edera@gmail.com"]
15
+ s.extra_rdoc_files = ["LICENSE.txt", "README.textile"]
16
+ s.files = [".document", "CHANGELOG", "Gemfile", "Gemfile.lock", "LICENSE.txt", "README.textile", "Rakefile", "VERSION", "autotest/discover.rb", "snoopy_afip.gemspec", "lib/snoopy_afip.rb", "lib/snoopy_afip/auth_data.rb", "lib/snoopy_afip/authorizer.rb", "lib/snoopy_afip/bill.rb", "lib/snoopy_afip/constants.rb", "lib/snoopy_afip/core_ext/float.rb", "lib/snoopy_afip/core_ext/hash.rb", "lib/snoopy_afip/core_ext/string.rb", "lib/snoopy_afip/version.rb", "spec/snoopy_afip/auth_data_spec.rb", "spec/snoopy_afip/authorizer_spec.rb", "spec/snoopy_afip/bill_spec.rb", "spec/spec_helper.rb", "wsaa-client.sh"]
17
+ s.homepage = "https://github.com/enriserdio/snoopy_afip"
18
+ s.licenses = ["MIT"]
19
+ s.require_paths = ["lib"]
20
+ s.rubygems_version = "1.8.25"
21
+ s.summary = "Adaptador AFIP wsfe."
22
+ s.test_files = ["spec/snoopy_afip/auth_data_spec.rb", "spec/snoopy_afip/authorizer_spec.rb", "spec/snoopy_afip/bill_spec.rb", "spec/spec_helper.rb"]
23
+
24
+ if s.respond_to? :specification_version then
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<savon>, ["= 2.4.0"])
29
+ s.add_runtime_dependency(%q<nokogiri>, ["~> 1.6"])
30
+ s.add_runtime_dependency(%q<wasabi>, ["~> 3.2.3"])
31
+ s.add_runtime_dependency(%q<akami>, ["~> 1.1"])
32
+ s.add_runtime_dependency(%q<nori>, ["~> 2.3.0"])
33
+ # s.add_development_dependency(%q<ruby-debug>, [">= 0"])
34
+ # s.add_development_dependency(%q<ruby-debug-base19>, ["= 0.11.24"])
35
+ # s.add_development_dependency(%q<ruby-debug19>, ["= 0.11.6"])
36
+ # s.add_development_dependency(%q<rspec>, ["~> 2.4.0"])
37
+ # s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
38
+ # s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
39
+ # s.add_development_dependency(%q<rcov>, [">= 0"])
40
+ else
41
+ s.add_dependency(%q<savon>, ["= 2.4.0"])
42
+ s.add_dependency(%q<nokogiri>, ["~> 1.6"])
43
+ s.add_dependency(%q<wasabi>, ["~> 3.2.3"])
44
+ s.add_dependency(%q<akami>, ["~> 1.1"])
45
+ s.add_dependency(%q<nori>, ["~> 2.3.0"])
46
+ # s.add_dependency(%q<ruby-debug>, [">= 0"])
47
+ # s.add_dependency(%q<ruby-debug-base19>, ["= 0.11.24"])
48
+ # s.add_dependency(%q<ruby-debug19>, ["= 0.11.6"])
49
+ # s.add_dependency(%q<rspec>, ["~> 2.4.0"])
50
+ # s.add_dependency(%q<bundler>, ["~> 1.0.0"])
51
+ # s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
52
+ # s.add_dependency(%q<rcov>, [">= 0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<savon>, ["= 2.4.0"])
56
+ s.add_dependency(%q<nokogiri>, ["~> 1.6"])
57
+ s.add_dependency(%q<wasabi>, ["~> 3.2.3"])
58
+ s.add_dependency(%q<akami>, ["~> 1.1"])
59
+ s.add_dependency(%q<nori>, ["~> 2.3.0"])
60
+ # s.add_dependency(%q<ruby-debug>, [">= 0"])
61
+ # s.add_dependency(%q<ruby-debug-base19>, ["= 0.11.24"])
62
+ # s.add_dependency(%q<ruby-debug19>, ["= 0.11.6"])
63
+ # s.add_dependency(%q<rspec>, ["~> 2.4.0"])
64
+ # s.add_dependency(%q<bundler>, ["~> 1.0.0"])
65
+ # s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
66
+ # s.add_dependency(%q<rcov>, [">= 0"])
67
+ end
68
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "AuthData" do
4
+ it "should create constants for todays data" do
5
+ Snoopy::AuthData.fetch
6
+ if RUBY_VERSION >= "1.9"
7
+ Snoopy.constants.should include(:TOKEN, :SIGN)
8
+ else
9
+ Snoopy.constants.should include("TOKEN", "SIGN")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "Authorizer" do
4
+ it "should read credentials on initialize" do
5
+ authorizer = Snoopy::Authorizer.new
6
+ authorizer.pkey.should == 'spec/fixtures/pkey'
7
+ authorizer.cert.should == 'spec/fixtures/cert.crt'
8
+ end
9
+ end
@@ -0,0 +1,106 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "Bill" do
4
+ it "should setup a header hash" do
5
+ @header = Snoopy::Bill.header(0)
6
+ @header.size.should == 3
7
+ ["CantReg", "CbteTipo", "PtoVta"].each do |key|
8
+ @header.has_key?(key).should == true
9
+ end
10
+ end
11
+
12
+ describe "instance" do
13
+ before(:each) {@bill = Snoopy::Bill.new}
14
+
15
+ it "should initialize according to Snoopy defaults" do
16
+ @bill.client.class.name.should == "Savon::Client"
17
+ ["Token", "Sign", "Cuit"].each do |key|
18
+ @bill.body["Auth"][key].should_not == nil
19
+ end
20
+ @bill.documento.should == Snoopy.default_documento
21
+ @bill.moneda.should == Snoopy.default_moneda
22
+ end
23
+
24
+ it "should calculate it's cbte_tipo for Responsable Inscripto" do
25
+ @bill.iva_cond = :responsable_inscripto
26
+ @bill.cbte_type.should == "01"
27
+ end
28
+
29
+ it "should calculate it's cbte_tipo for Consumidor Final" do
30
+ @bill.iva_cond = :consumidor_final
31
+ @bill.cbte_type.should == "06"
32
+ end
33
+
34
+ it "raise error on nil iva cond" do
35
+ @bill.iva_cond = 12
36
+ expect{@bill.cbte_type}.to raise_error(Snoopy::NullOrInvalidAttribute)
37
+ end
38
+
39
+ it "should fetch non Peso currency's exchange rate" do
40
+ @bill.moneda = :dolar
41
+ @bill.exchange_rate.to_i.should be > 0
42
+ end
43
+
44
+ it "should return 1 for Peso currency" do
45
+ @bill.moneda = :peso
46
+ @bill.exchange_rate.should == 1
47
+ end
48
+
49
+ it "should calculate the IVA array values" do
50
+ @bill.iva_cond = :responsable_inscripto
51
+ @bill.moneda = :peso
52
+ @bill.net = 100.89
53
+ @bill.aliciva_id = 2
54
+
55
+ @bill.iva_sum.should be_within(0.05).of(21.18)
56
+ @bill.total.should be_within(0.05).of(122.07)
57
+ end
58
+
59
+ it "should use give due date an service dates, or todays date" do
60
+ @bill.net = 100
61
+ @bill.aliciva_id = 2
62
+ @bill.doc_num = "30710151543"
63
+ @bill.iva_cond = :responsable_inscripto
64
+ @bill.concepto = "Servicios"
65
+
66
+ @bill.setup_bill
67
+
68
+ detail = @bill.body["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
69
+
70
+ detail["FchServDesde"].should == Time.new.strftime('%Y%m%d')
71
+ detail["FchServHasta"].should == Time.new.strftime('%Y%m%d')
72
+ detail["FchVtoPago"].should == Time.new.strftime('%Y%m%d')
73
+
74
+ @bill.due_date = Date.new(2011, 12, 10).strftime('%Y%m%d')
75
+ @bill.fch_serv_desde = Date.new(2011, 11, 01).strftime('%Y%m%d')
76
+ @bill.fch_serv_hasta = Date.new(2011, 11, 30).strftime('%Y%m%d')
77
+
78
+ @bill.setup_bill
79
+
80
+ detail = @bill.body["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
81
+
82
+ detail["FchServDesde"].should == "20111101"
83
+ detail["FchServHasta"].should == "20111130"
84
+ detail["FchVtoPago"].should == "20111210"
85
+ end
86
+
87
+ Snoopy::BILL_TYPE[Snoopy.own_iva_cond].keys.each do |target_iva_cond|
88
+ it "should authorize a valid bill for #{target_iva_cond.to_s}" do
89
+ @bill.net = 1000000
90
+ @bill.aliciva_id = 2
91
+ @bill.doc_num = "30710151543"
92
+ @bill.iva_cond = target_iva_cond
93
+ @bill.concepto = "Servicios"
94
+
95
+ @bill.authorized?.should == false
96
+ @bill.authorize.should == true
97
+ @bill.authorized?.should == true
98
+
99
+ response = @bill.response
100
+
101
+ response.length.should == 28
102
+ response.cae.length.should == 14
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,23 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'snoopy'
3
+ require 'rspec'
4
+ require 'ruby-debug'
5
+
6
+ class SpecHelper
7
+ include Savon::Logger
8
+ end
9
+
10
+ # Requires supporting files with custom matchers and macros, etc,
11
+ # in ./support/ and its subdirectories.
12
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
+
14
+ Snoopy.pkey = "spec/fixtures/pkey"
15
+ Snoopy.cert = "spec/fixtures/cert.crt"
16
+ Snoopy.cuit = ENV["CUIT"] || raise(Snoopy::NullOrInvalidAttribute.new, "Please set CUIT env variable.")
17
+ Snoopy.sale_point = "0002"
18
+ Snoopy.auth_url = "https://wsaahomo.afip.gov.ar/ws/services/LoginCms"
19
+ Snoopy.service_url = "http://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL"
20
+ Snoopy.default_concepto = "Productos y Servicios"
21
+ Snoopy.default_documento = "CUIT"
22
+ Snoopy.default_moneda = :peso
23
+ Snoopy.own_iva_cond = :responsable_inscripto
data/wsaa-client.sh ADDED
@@ -0,0 +1,174 @@
1
+ #!/bin/bash
2
+ # FUNCTION: Bash script to get a TA from WSAA
3
+ # AUTHOR: Gerardo Fisanotti - AFIP/SDG-SI/DIITEC/DEARIN - 15-nov-2010
4
+ # Dependencies: curl, openssl >= 1.0, xmllint
5
+ #
6
+ # Modify following definitions according to your environment:
7
+ #
8
+ # URL=https://wsaahomo.afip.gov.ar/ws/services/LoginCms # WSAA URL
9
+ # KEY=spec/fixtures/pkey # file containing the private key in PEM format
10
+ # CRT=spec/fixtures/cert.crt # file containing the X.509 certificate in PEM format
11
+ TAFN="TA.xml" # file name of the output file
12
+ # modify next line if you need a proxy to get to the Internet or comment it out
13
+ # if you don't need a proxy
14
+ # export https_proxy="http://10.20.152.112:80"
15
+ #
16
+ # No further modifications should be needed below this line
17
+ #==============================================================================
18
+ function MakeTRA()
19
+ #
20
+ # Generate the XML containing the Access Ticket Request (TRA)
21
+ #
22
+ {
23
+ # FROM=$(date -j -f "%a %b %d %T %Z %Y" "`date -v0H -v0M -v0S`" "+%s")
24
+ # TO=$(date -j -f "%a %b %d %T %Z %Y" "`date -v23H -v59M -v59S`" "+%s")
25
+ FROM=$(date "+%Y-%m-%dT00:00:00-03:00")
26
+ TO=$(date "+%Y-%m-%dT23:59:59-03:00")
27
+ ID=$(date "+%s")
28
+ TRA=$(cat <<EOF
29
+ <?xml version="1.0" encoding="UTF-8"?>
30
+ <loginTicketRequest version="1.0">
31
+ <header>
32
+ <uniqueId>$ID</uniqueId>
33
+ <generationTime>$FROM</generationTime>
34
+ <expirationTime>$TO</expirationTime>
35
+ </header>
36
+ <service>wsfe</service>
37
+ </loginTicketRequest>
38
+ EOF
39
+ )
40
+ }
41
+ #------------------------------------------------------------------------------
42
+ function MakeCMS()
43
+ #
44
+ # Generate de CMS container (TRA + sign + certificate)
45
+ #
46
+ {
47
+ OPENSSL=$(which openssl)
48
+ CMS=$(
49
+ echo "$TRA" |
50
+ $OPENSSL cms -sign -in /dev/stdin -signer $CRT -inkey $KEY -nodetach \
51
+ -outform der |
52
+ $OPENSSL base64 -e
53
+ )
54
+ }
55
+ #------------------------------------------------------------------------------
56
+ function MakeSOAPrequest()
57
+ #
58
+ # Generate the SOAP request XML
59
+ #
60
+ {
61
+ REQUEST=$(cat <<EOF
62
+ <?xml version="1.0" encoding="UTF-8"?>
63
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://wsaa.view.sua.dvadac.desein.afip.gov">
64
+ <SOAP-ENV:Body>
65
+ <ns1:loginCms>
66
+ <ns1:in0>
67
+ $CMS
68
+ </ns1:in0>
69
+ </ns1:loginCms>
70
+ </SOAP-ENV:Body>
71
+ </SOAP-ENV:Envelope>
72
+ EOF
73
+ )
74
+ }
75
+ #------------------------------------------------------------------------------
76
+ function CallWSAA()
77
+ #
78
+ # Invoke WSAA sending SOAP request XML to LoginCMS method
79
+ #
80
+ {
81
+ RESPONSE=$(
82
+ echo "$REQUEST" |
83
+ curl -k -H 'Content-Type: application/soap+xml; action=""' -d @- $URL
84
+ )
85
+ echo "$REQUEST"
86
+ }
87
+ #------------------------------------------------------------------------------
88
+ function ParseTA()
89
+ #
90
+ # Try to parse the results obtained from WSAA
91
+ #
92
+ {
93
+ TOKEN=$(
94
+ echo "$RESPONSE" |
95
+ grep token |
96
+ sed -e 's/&lt;token&gt;//' |
97
+ sed -e 's/&lt;\/token&gt;//' |
98
+ sed -e 's/ //g'
99
+ )
100
+ SIGN=$(
101
+ echo "$RESPONSE" |
102
+ grep sign |
103
+ sed -e 's/&lt;sign&gt;//' |
104
+ sed -e 's/&lt;\/sign&gt;//' |
105
+ sed -e 's/ //g'
106
+ )
107
+ # If we did not get TOKEN, then it was a SOAP Fault, show the error message
108
+ # and exit
109
+ #
110
+ if [ "$TOKEN" == "" ]
111
+ then
112
+ echo "ERROR: "
113
+ echo "$(echo "$RESPONSE" | xmllint --format - | grep faultstring)"
114
+ exit 1
115
+ fi
116
+ }
117
+ #------------------------------------------------------------------------------
118
+ function WriteTA()
119
+ #
120
+ # Write the token and sign to the output file
121
+ #
122
+ {
123
+ cat <<EOF > $TAFN
124
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
125
+ <loginTicketResponse version="1">
126
+ <credentials>
127
+ <token>$TOKEN</token>
128
+ <sign>$SIGN</sign>
129
+ </credentials>
130
+ </loginTicketResponse>
131
+ EOF
132
+ }
133
+
134
+ function WriteYAML()
135
+ {
136
+ cat <<EOF > /tmp/snoopy_afip_"$CUIT"_$(date +"%d_%m_%Y").yml
137
+ token: '$TOKEN'
138
+ sign: '$SIGN'
139
+ EOF
140
+ }
141
+ #------------------------------------------------------------------------------
142
+ #
143
+ # MAIN program
144
+ #
145
+ # If we were invoked with a service name in arg #1, use it
146
+ #[ $# -eq 1 ] && SERVICE=$1
147
+ # otherwise, ask for it
148
+ #[ $# -eq 0 ] && read -p "Service name: " SERVICE
149
+
150
+ # Parse commandline arguments
151
+ while getopts 'k:u:c:i:' OPTION
152
+ do
153
+ case $OPTION in
154
+ c) CRT=$OPTARG
155
+ ;;
156
+ k) KEY=$OPTARG
157
+ ;;
158
+ u) URL=$OPTARG
159
+ ;;
160
+ i) CUIT=$OPTARG
161
+ ;;
162
+ esac
163
+ done
164
+ shift $(($OPTIND - 1))
165
+ MakeTRA # Generate TRA
166
+ MakeCMS # Generate CMS (TRA + signature + certificate)
167
+ MakeSOAPrequest # Generate the SOAP request XML
168
+ CallWSAA # Invoke WSAA sending SOAP request
169
+ ParseTA # Parse the WSAA SOAP response, extract Token and Sign
170
+ # WriteTA # Write an abbreviated TA.xml with Token and Sign only
171
+ WriteYAML
172
+ echo "Access Ticket acquired, written to: $TAFN" # Inform success and exit
173
+ echo $REQUEST
174
+ echo $TRA
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snoopy_afip
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
11
+ platform: ruby
12
+ authors:
13
+ - g.edera, eserdio
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2016-09-12 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: savon
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - "="
28
+ - !ruby/object:Gem::Version
29
+ hash: 31
30
+ segments:
31
+ - 2
32
+ - 4
33
+ - 0
34
+ version: 2.4.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: nokogiri
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 1
48
+ - 6
49
+ version: "1.6"
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: wasabi
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 9
61
+ segments:
62
+ - 3
63
+ - 2
64
+ - 3
65
+ version: 3.2.3
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: akami
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 13
77
+ segments:
78
+ - 1
79
+ - 1
80
+ version: "1.1"
81
+ type: :runtime
82
+ version_requirements: *id004
83
+ - !ruby/object:Gem::Dependency
84
+ name: nori
85
+ prerelease: false
86
+ requirement: &id005 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 2
94
+ - 3
95
+ - 0
96
+ version: 2.3.0
97
+ type: :runtime
98
+ version_requirements: *id005
99
+ description: Adaptador para el Web Service de Facturacion Electronica de AFIP
100
+ email:
101
+ - gab.edera@gmail.com
102
+ executables: []
103
+
104
+ extensions: []
105
+
106
+ extra_rdoc_files:
107
+ - LICENSE.txt
108
+ - README.textile
109
+ files:
110
+ - .document
111
+ - CHANGELOG
112
+ - Gemfile
113
+ - Gemfile.lock
114
+ - LICENSE.txt
115
+ - README.textile
116
+ - Rakefile
117
+ - VERSION
118
+ - autotest/discover.rb
119
+ - snoopy_afip.gemspec
120
+ - lib/snoopy_afip.rb
121
+ - lib/snoopy_afip/auth_data.rb
122
+ - lib/snoopy_afip/authorizer.rb
123
+ - lib/snoopy_afip/bill.rb
124
+ - lib/snoopy_afip/constants.rb
125
+ - lib/snoopy_afip/core_ext/float.rb
126
+ - lib/snoopy_afip/core_ext/hash.rb
127
+ - lib/snoopy_afip/core_ext/string.rb
128
+ - lib/snoopy_afip/version.rb
129
+ - spec/snoopy_afip/auth_data_spec.rb
130
+ - spec/snoopy_afip/authorizer_spec.rb
131
+ - spec/snoopy_afip/bill_spec.rb
132
+ - spec/spec_helper.rb
133
+ - wsaa-client.sh
134
+ has_rdoc: true
135
+ homepage: https://github.com/enriserdio/snoopy_afip
136
+ licenses:
137
+ - MIT
138
+ post_install_message:
139
+ rdoc_options: []
140
+
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 3
149
+ segments:
150
+ - 0
151
+ version: "0"
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 3
158
+ segments:
159
+ - 0
160
+ version: "0"
161
+ requirements: []
162
+
163
+ rubyforge_project:
164
+ rubygems_version: 1.6.2
165
+ signing_key:
166
+ specification_version: 3
167
+ summary: Adaptador AFIP wsfe.
168
+ test_files:
169
+ - spec/snoopy_afip/auth_data_spec.rb
170
+ - spec/snoopy_afip/authorizer_spec.rb
171
+ - spec/snoopy_afip/bill_spec.rb
172
+ - spec/spec_helper.rb