snoopy_afip 2.0.0

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