bravo 0.4.0 → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
data/lib/bravo/bill.rb CHANGED
@@ -1,59 +1,80 @@
1
1
  module Bravo
2
+ # The main class in Bravo. Handles WSFE method interactions.
3
+ # Subsequent implementations will be added here (maybe).
4
+ #
2
5
  class Bill
3
- attr_reader :client, :base_imp, :total
6
+ # Returns the Savon::Client instance in charge of the interactions with WSFE API.
7
+ # (built on init)
8
+ #
9
+ attr_reader :client
10
+
4
11
  attr_accessor :net, :doc_num, :iva_cond, :documento, :concepto, :moneda,
5
12
  :due_date, :aliciva_id, :fch_serv_desde, :fch_serv_hasta,
6
- :body, :response
13
+ :body, :response, :invoice_type
7
14
 
8
15
  def initialize(attrs = {})
9
16
  Bravo::AuthData.fetch
10
- @client = Savon::Client.new(Bravo.service_url)
11
- @body = {"Auth" => Bravo.auth_hash}
12
- @net = attrs[:net] || 0
13
- self.documento = attrs[:documento] || Bravo.default_documento
14
- self.moneda = attrs[:moneda] || Bravo.default_moneda
15
- self.iva_cond = attrs[:iva_cond]
16
- self.concepto = attrs[:concepto] || Bravo.default_concepto
17
+ @client = Savon.client(wsdl: Bravo::AuthData.wsfe_url, log: false)
18
+ @body = { "Auth" => Bravo::AuthData.auth_hash }
19
+ self.iva_cond = attrs[:iva_cond]
20
+ @net = attrs[:net] || 0
21
+ self.documento = attrs[:documento] || Bravo.default_documento
22
+ self.moneda = attrs[:moneda] || Bravo.default_moneda
23
+ self.concepto = attrs[:concepto] || Bravo.default_concepto
24
+ self.invoice_type = attrs[:invoice_type] || :invoice
17
25
  end
18
26
 
27
+ # Searches the corresponding invoice type according to the combination of
28
+ # the seller's IVA condition and the buyer's IVA condition
29
+ # @return [String] the document type string
30
+ #
19
31
  def cbte_type
20
- Bravo::BILL_TYPE[Bravo.own_iva_cond][iva_cond] ||
21
- raise(NullOrInvalidAttribute.new, "Please choose a valid document type.")
22
- end
32
+ own_iva = Bravo::BILL_TYPE.has_key?(Bravo.own_iva_cond) ? Bravo::BILL_TYPE[Bravo.own_iva_cond] : raise(NullOrInvalidAttribute.new, "Own iva_cond is invalid.")
33
+ target_iva = own_iva.has_key?(iva_cond) ? own_iva[iva_cond] : raise(NullOrInvalidAttribute.new, "Target iva_cond is invalid.")
34
+ type = target_iva.has_key?(invoice_type) ? target_iva[invoice_type] : raise(NullOrInvalidAttribute.new, "Selected invoice_type is invalid.")
23
35
 
24
- def exchange_rate
25
- return 1 if moneda == :peso
26
- response = client.fe_param_get_cotizacion do |soap|
27
- soap.namespaces["xmlns"] = "http://ar.gov.afip.dif.FEV1/"
28
- soap.body = body.merge!({"MonId" => Bravo::MONEDAS[moneda][:codigo]})
29
- end
30
- response.to_hash[:fe_param_get_cotizacion_response][:fe_param_get_cotizacion_result][:result_get][:mon_cotiz].to_f
31
36
  end
32
37
 
38
+ # Calculates the total field for the invoice by adding
39
+ # net and iva_sum.
40
+ # @return [Float] the sum of both fields, or 0 if the net is 0.
41
+ #
33
42
  def total
34
43
  @total = net.zero? ? 0 : net + iva_sum
35
44
  end
36
45
 
46
+ # Calculates the corresponding iva sum.
47
+ # This is performed by multiplying the net by the tax value
48
+ # @return [Float] the iva sum
49
+ #
50
+ # TODO: fix this
51
+ #
37
52
  def iva_sum
38
53
  @iva_sum = net * Bravo::ALIC_IVA[aliciva_id][1]
39
54
  @iva_sum.round_up_with_precision(2)
40
55
  end
41
56
 
57
+ # Files the authorization request to AFIP
58
+ # @return [Boolean] wether the request succeeded or not
59
+ #
42
60
  def authorize
43
61
  setup_bill
44
- response = client.fecae_solicitar do |soap|
45
- soap.namespaces["xmlns"] = "http://ar.gov.afip.dif.FEV1/"
46
- soap.body = body
62
+ response = client.call(:fecae_solicitar) do |soap|
63
+ # soap.namespaces["xmlns"] = "http://ar.gov.afip.dif.FEV1/"
64
+ soap.message body
47
65
  end
48
66
 
49
67
  setup_response(response.to_hash)
50
68
  self.authorized?
51
69
  end
52
70
 
71
+ # Sets up the request body for the authorisation
72
+ # @return [Hash] returns the request body as a hash
73
+ #
53
74
  def setup_bill
54
75
  today = Time.new.strftime('%Y%m%d')
55
76
 
56
- fecaereq = {"FeCAEReq" => {
77
+ fecaereq = { "FeCAEReq" => {
57
78
  "FeCabReq" => Bravo::Bill.header(cbte_type),
58
79
  "FeDetReq" => {
59
80
  "FECAEDetRequest" => {
@@ -62,14 +83,14 @@ module Bravo
62
83
  "CbteFch" => today,
63
84
  "ImpTotConc" => 0.00,
64
85
  "MonId" => Bravo::MONEDAS[moneda][:codigo],
65
- "MonCotiz" => exchange_rate,
86
+ "MonCotiz" => 1,
66
87
  "ImpOpEx" => 0.00,
67
88
  "ImpTrib" => 0.00,
68
89
  "Iva" => {
69
90
  "AlicIva" => {
70
91
  "Id" => "5",
71
92
  "BaseImp" => net,
72
- "Importe" => iva_sum}}}}}}
93
+ "Importe" => iva_sum } } } } } }
73
94
 
74
95
  detail = fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
75
96
 
@@ -77,26 +98,20 @@ module Bravo
77
98
  detail["ImpNeto"] = net.to_f
78
99
  detail["ImpIVA"] = iva_sum
79
100
  detail["ImpTotal"] = total
80
- detail["CbteDesde"] = detail["CbteHasta"] = next_bill_number
101
+ detail["CbteDesde"] = detail["CbteHasta"] = Bravo::Reference.next_bill_number(cbte_type)
81
102
 
82
103
  unless concepto == 0
83
- detail.merge!({"FchServDesde" => fch_serv_desde || today,
84
- "FchServHasta" => fch_serv_hasta || today,
85
- "FchVtoPago" => due_date || today})
104
+ detail.merge!({ "FchServDesde" => fch_serv_desde || today,
105
+ "FchServHasta" => fch_serv_hasta || today,
106
+ "FchVtoPago" => due_date || today })
86
107
  end
87
108
 
88
109
  body.merge!(fecaereq)
89
110
  end
90
111
 
91
- def next_bill_number
92
- resp = client.fe_comp_ultimo_autorizado do |s|
93
- s.namespaces["xmlns"] = "http://ar.gov.afip.dif.FEV1/"
94
- s.body = {"Auth" => Bravo.auth_hash, "PtoVta" => Bravo.sale_point, "CbteTipo" => cbte_type}
95
- end
96
-
97
- resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1
98
- end
99
-
112
+ # Returns the result of the authorization operation
113
+ # @return [Boolean] the response result
114
+ #
100
115
  def authorized?
101
116
  !response.nil? && response.header_result == "A" && response.detail_result == "A"
102
117
  end
@@ -104,11 +119,18 @@ module Bravo
104
119
  private
105
120
 
106
121
  class << self
107
- def header(cbte_type)#todo sacado de la factura
108
- {"CantReg" => "1", "CbteTipo" => cbte_type, "PtoVta" => Bravo.sale_point}
122
+ # Sets the header hash for the request
123
+ # @return [Hash]
124
+ #
125
+ def header(cbte_type)
126
+ # todo sacado de la factura
127
+ { "CantReg" => "1", "CbteTipo" => cbte_type, "PtoVta" => Bravo.sale_point }
109
128
  end
110
129
  end
111
130
 
131
+ # Response parser. Only works for the authorize method
132
+ # @return [Struct] a struct with key-value pairs with the response values
133
+ #
112
134
  def setup_response(response)
113
135
  # TODO: turn this into an all-purpose Response class
114
136
 
@@ -124,18 +146,18 @@ module Bravo
124
146
 
125
147
  request_detail.merge!(iva)
126
148
 
127
- response_hash = {:header_result => response_header.delete(:resultado),
128
- :authorized_on => response_header.delete(:fch_proceso),
129
- :detail_result => response_detail.delete(:resultado),
130
- :cae_due_date => response_detail.delete(:cae_fch_vto),
131
- :cae => response_detail.delete(:cae),
132
- :iva_id => request_detail.delete(:id),
133
- :iva_importe => request_detail.delete(:importe),
134
- :moneda => request_detail.delete(:mon_id),
135
- :cotizacion => request_detail.delete(:mon_cotiz),
136
- :iva_base_imp => request_detail.delete(:base_imp),
137
- :doc_num => request_detail.delete(:doc_nro)
138
- }.merge!(request_header).merge!(request_detail)
149
+ response_hash = { :header_result => response_header.delete(:resultado),
150
+ :authorized_on => response_header.delete(:fch_proceso),
151
+ :detail_result => response_detail.delete(:resultado),
152
+ :cae_due_date => response_detail.delete(:cae_fch_vto),
153
+ :cae => response_detail.delete(:cae),
154
+ :iva_id => request_detail.delete(:id),
155
+ :iva_importe => request_detail.delete(:importe),
156
+ :moneda => request_detail.delete(:mon_id),
157
+ :cotizacion => request_detail.delete(:mon_cotiz),
158
+ :iva_base_imp => request_detail.delete(:base_imp),
159
+ :doc_num => request_detail.delete(:doc_nro)
160
+ }.merge!(request_header).merge!(request_detail)
139
161
 
140
162
  keys, values = response_hash.to_a.transpose
141
163
  self.response = (defined?(Struct::Response) ? Struct::Response : Struct.new("Response", *keys)).new(*values)
@@ -1,5 +1,9 @@
1
1
  # encoding: utf-8
2
+ # Here we define Hashes
3
+ #
2
4
  module Bravo
5
+ # This constant contains the invoice types mappings between codes and names
6
+ # used by WSFE.
3
7
  CBTE_TIPO = {
4
8
  "01"=>"Factura A",
5
9
  "02"=>"Nota de Débito A",
@@ -21,19 +25,75 @@ module Bravo
21
25
  "64"=>"Liquidacion B"
22
26
  }
23
27
 
24
- CONCEPTOS = {"Productos"=>"01", "Servicios"=>"02", "Productos y Servicios"=>"03"}
28
+ # Name to code mapping for Sale types.
29
+ #
30
+ CONCEPTOS = { "Productos"=>"01", "Servicios"=>"02", "Productos y Servicios"=>"03" }
25
31
 
26
- DOCUMENTOS = {"CUIT"=>"80", "CUIL"=>"86", "CDI"=>"87", "LE"=>"89", "LC"=>"90", "CI Extranjera"=>"91", "en tramite"=>"92", "Acta Nacimiento"=>"93", "CI Bs. As. RNP"=>"95", "DNI"=>"96", "Pasaporte"=>"94", "Doc. (Otro)"=>"99"}
32
+ # Name to code mapping for types of documents.
33
+ #
34
+ DOCUMENTOS = {
35
+ "CUIT"=>"80",
36
+ "CUIL"=>"86",
37
+ "CDI"=>"87",
38
+ "LE"=>"89",
39
+ "LC"=>"90",
40
+ "CI Extranjera"=>"91",
41
+ "en tramite"=>"92",
42
+ "Acta Nacimiento"=>"93",
43
+ "CI Bs. As. RNP"=>"95",
44
+ "DNI"=>"96",
45
+ "Pasaporte"=>"94",
46
+ "Doc. (Otro)"=>"99" }
27
47
 
48
+ # Currency code and names hash identified by a symbol
49
+ #
28
50
  MONEDAS = {
29
- :peso => {:codigo => "PES", :nombre =>"Pesos Argentinos"},
30
- :dolar => {:codigo => "DOL", :nombre =>"Dolar Estadounidense"},
31
- :real => {:codigo => "012", :nombre =>"Real"},
32
- :euro => {:codigo => "060", :nombre =>"Euro"},
33
- :oro => {:codigo => "049", :nombre =>"Gramos de Oro Fino"}
34
- }
51
+ :peso => { :codigo => "PES", :nombre =>"Pesos Argentinos" },
52
+ :dolar => { :codigo => "DOL", :nombre =>"Dolar Estadounidense" },
53
+ :real => { :codigo => "012", :nombre =>"Real" },
54
+ :euro => { :codigo => "060", :nombre =>"Euro" },
55
+ :oro => { :codigo => "049", :nombre =>"Gramos de Oro Fino" } }
56
+
35
57
 
58
+ # Tax percentage and codes according to each iva combination
59
+ #
36
60
  ALIC_IVA = [["03", 0], ["04", 0.105], ["05", 0.21], ["06", 0.27]]
37
61
 
38
- BILL_TYPE = {:responsable_inscripto => {:responsable_inscripto => "01", :consumidor_final => "06", :exento => "06", :responsable_monotributo => "06"}}
39
- end
62
+
63
+
64
+ # This hash keeps the codes for A document types by operation
65
+ #
66
+ BILL_TYPE_A = {
67
+ :invoice => "01",
68
+ :debit => "02",
69
+ :credit => "03" }
70
+
71
+ # This hash keeps the codes for A document types by operation
72
+ #
73
+ BILL_TYPE_B = {
74
+ :invoice => "06",
75
+ :debit => "07",
76
+ :credit => "08" }
77
+
78
+ # This hash keeps the different buyer and invoice type mapping corresponding to
79
+ # the seller's iva condition and invoice kind.
80
+ # Usage:
81
+ # `BILL_TYPE[seller_iva_cond][buyer_iva_cond][invoice_type]` #=> invoice type as string
82
+ # `BILL_TYPE[:responsable_inscripto][:responsable_inscripto][:invoice]` #=> "01"
83
+ #
84
+ BILL_TYPE = {
85
+ :responsable_inscripto => {
86
+ :responsable_inscripto => BILL_TYPE_A,
87
+ :consumidor_final => BILL_TYPE_B,
88
+ :exento => BILL_TYPE_B,
89
+ :responsable_monotributo => BILL_TYPE_B } }
90
+
91
+ # This hash keeps the set of urls for wsaa and wsfe for production and testing envs
92
+ #
93
+ URLS = {
94
+ :test => { :wsaa => "https://wsaahomo.afip.gov.ar/ws/services/LoginCms",
95
+ :wsfe => "https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL" },
96
+
97
+ :production => { :wsaa => "https://wsaa.afip.gov.ar/ws/services/LoginCms",
98
+ :wsfe => "https://servicios1.afip.gov.ar/wsfev1/service.asmx" } }
99
+ end
@@ -1,7 +1,13 @@
1
+ # Float monkeypatching.
2
+ #
1
3
  class Float
4
+ # Should be removed.
5
+ #
2
6
  def round_with_precision(precision = nil)
3
7
  precision.nil? ? round : (self * (10 ** precision)).round / (10 ** precision).to_f
4
8
  end
9
+ # Should be removed.
10
+ #
5
11
  def round_up_with_precision(precision = nil)
6
12
  precision.nil? ? round : ((self * (10 ** precision)).round + 1) / (10 ** precision).to_f
7
13
  end
@@ -1,4 +1,8 @@
1
+ # Methods stolen from ActiveSupport, to avoid requiring the gem as a dependency
1
2
  class Hash
3
+ # Alters the hash, converting it's keys to symbols
4
+ # @return [Hash]
5
+ #
2
6
  def symbolize_keys!
3
7
  keys.each do |key|
4
8
  self[(key.to_sym rescue key) || key] = delete(key)
@@ -6,10 +10,16 @@ class Hash
6
10
  self
7
11
  end unless method_defined?(:symbolize_keys!)
8
12
 
13
+ # Returns a copy of the hash, with it's keys converted to symbols
14
+ # @return [Hash]
15
+ #
9
16
  def symbolize_keys
10
17
  dup.symbolize_keys!
11
18
  end unless method_defined?(:symbolize_keys)
12
19
 
20
+ # Alters the hash, converting its keys to underscore strings
21
+ # @return [Hash]
22
+ #
13
23
  def underscore_keys!
14
24
  keys.each do |key|
15
25
  self[(key.underscore rescue key) || key] = delete(key)
@@ -17,6 +27,8 @@ class Hash
17
27
  self
18
28
  end unless method_defined?(:underscore_keys!)
19
29
 
30
+ # Returns a copy of the hash, with it's keys converted to underscore strings
31
+ # @return [Hash]
20
32
  def underscore_keys
21
33
  dup.underscore_keys!
22
34
  end unless method_defined?(:underscore_keys)
@@ -1,5 +1,8 @@
1
- # Stolen from activesupport/lib/active_support/inflector/methods.rb, line 48
1
+ # Added to avoid requiring ActiveSupport as dependency
2
+ #
2
3
  class String
4
+ # Stolen from activesupport/lib/active_support/inflector/methods.rb, line 48
5
+ #
3
6
  def underscore
4
7
  word = self.to_s.dup
5
8
  word.gsub!(/::/, '/')
@@ -0,0 +1,37 @@
1
+ module Bravo
2
+ # Class in charge of issuing read requests on the api
3
+ #
4
+ class Reference
5
+ # Fetches the number for the next bill to be issued
6
+ # @return [Integer] the number for the next bill
7
+ #
8
+ def self.next_bill_number(cbte_type)
9
+ set_client
10
+ resp = @client.call(:fe_comp_ultimo_autorizado) do |soap|
11
+ # soap.namespaces["xmlns"] = "http://ar.gov.afip.dif.FEV1/"
12
+ soap.message "Auth" => Bravo::AuthData.auth_hash, "PtoVta" => Bravo.sale_point, "CbteTipo" => cbte_type
13
+ end
14
+
15
+ resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1
16
+ end
17
+
18
+ # Fetches the possible document codes and names
19
+ # @return [Hash]
20
+ #
21
+ def self.get_custom(operation)
22
+ set_client
23
+ resp = @client.call(operation) do |soap|
24
+ soap.message "Auth" => Bravo::AuthData.auth_hash
25
+ end
26
+ resp.to_hash
27
+ end
28
+
29
+ # Sets up the cliet to perform consults to the api
30
+ #
31
+ #
32
+ def self.set_client
33
+ Bravo::AuthData.fetch
34
+ @client = Savon.client(wsdl: Bravo::AuthData.wsfe_url, log: false)
35
+ end
36
+ end
37
+ end
data/lib/bravo/version.rb CHANGED
@@ -1,3 +1,5 @@
1
1
  module Bravo
2
- VERSION = "0.4.0"
2
+ # Gem version
3
+ #
4
+ VERSION = "1.0.0.alpha"
3
5
  end
data/lib/bravo/wsaa.rb ADDED
@@ -0,0 +1,96 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Bravo
3
+ # Authorization class. Handles interactions wiht the WSAA, to provide
4
+ # valid key and signature that will last for a day.
5
+ #
6
+ class Wsaa
7
+ # Main method for authentication and authorization.
8
+ # When successful, produces the yaml file with auth data.
9
+ #
10
+ def self.login
11
+ tra = build_tra
12
+ cms = build_cms(tra)
13
+ req = build_request(cms)
14
+ auth = call_wsaa(req)
15
+
16
+ write_yaml(auth)
17
+ end
18
+
19
+ protected
20
+ # Builds the xml for the "Ticket de Requerimiento de Acceso"
21
+ # @return [String] containing the request body
22
+ #
23
+ def self.build_tra
24
+ now = Time.now
25
+ from = now.strftime("%FT%T%:z")
26
+ to = ((now - 120) + (24*60*60)).strftime("%FT%T%:z") # make sure ti will last for 24hs - 2 mins
27
+ id = now.strftime("%s")
28
+ tra = <<-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
+ return tra
40
+ end
41
+
42
+ # Builds the CMS
43
+ # @return [String] cms
44
+ #
45
+ def self.build_cms(tra)
46
+ cms = `echo '#{ tra }' |
47
+ #{ Bravo.openssl_bin } cms -sign -in /dev/stdin -signer #{ Bravo.cert } -inkey #{ Bravo.pkey } -nodetach \
48
+ -outform der |
49
+ #{ Bravo.openssl_bin } base64 -e`
50
+ return cms
51
+ end
52
+
53
+ # Builds the CMS request to log in to the server
54
+ # @return [String] the cms body
55
+ #
56
+ def self.build_request(cms)
57
+ request = <<-XML
58
+ <?xml version="1.0" encoding="UTF-8"?>
59
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://wsaa.view.sua.dvadac.desein.afip.gov">
60
+ <SOAP-ENV:Body>
61
+ <ns1:loginCms>
62
+ <ns1:in0>
63
+ #{ cms }
64
+ </ns1:in0>
65
+ </ns1:loginCms>
66
+ </SOAP-ENV:Body>
67
+ </SOAP-ENV:Envelope>
68
+ XML
69
+ return request
70
+ end
71
+
72
+ # Calls the WSAA with the request built by build_request
73
+ # @return [Array] with the token and signature
74
+ #
75
+ def self.call_wsaa(req)
76
+ response = `echo '#{ req }' |
77
+ curl -k -s -H 'Content-Type: application/soap+xml; action=""' -d @- #{ Bravo::AuthData.wsaa_url }`
78
+
79
+ response = CGI::unescapeHTML(response)
80
+ token = response.scan(/\<token\>(.+)\<\/token\>/).first.first
81
+ sign = response.scan(/\<sign\>(.+)\<\/sign\>/).first.first
82
+ return [token, sign]
83
+ end
84
+
85
+ # Writes the token and signature to a YAML file in the /tmp directory
86
+ #
87
+ def self.write_yaml(certs)
88
+ yml = <<-YML
89
+ token: #{certs[0]}
90
+ sign: #{certs[1]}
91
+ YML
92
+ `echo '#{ yml }' > /tmp/bravo_#{ Bravo.cuit }_#{ Time.new.strftime('%d_%m_%Y') }.yml`
93
+ end
94
+
95
+ end
96
+ end
data/lib/bravo.rb CHANGED
@@ -5,23 +5,28 @@ require "savon"
5
5
  require "bravo/core_ext/float"
6
6
  require "bravo/core_ext/hash"
7
7
  require "bravo/core_ext/string"
8
+
8
9
  module Bravo
9
10
 
11
+ # Exception Class for missing or invalid attributes
12
+ #
10
13
  class NullOrInvalidAttribute < StandardError; end
11
14
 
15
+ # Exception Clas for missing or invalid certifficate
16
+ #
17
+ class MissingCertificate < StandardError; end
18
+
12
19
  autoload :Authorizer, "bravo/authorizer"
13
20
  autoload :AuthData, "bravo/auth_data"
14
21
  autoload :Bill, "bravo/bill"
15
22
  autoload :Constants, "bravo/constants"
16
-
23
+ autoload :Wsaa, "bravo/wsaa"
24
+ autoload :Reference, "bravo/reference"
17
25
 
18
26
  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
27
 
22
- def auth_hash
23
- {"Token" => Bravo::TOKEN, "Sign" => Bravo::SIGN, "Cuit" => Bravo.cuit}
24
- end
28
+ attr_accessor :cuit, :sale_point, :default_documento, :pkey, :cert,
29
+ :default_concepto, :default_moneda, :own_iva_cond,
30
+ :verbose, :openssl_bin
25
31
 
26
- Savon::Request.log = false unless (Bravo.verbose == "true") || (ENV["VERBOSE"] == true)
27
- end
32
+ end
@@ -1,12 +1,13 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "AuthData" do
4
- it "should create constants for todays data" do
5
- Bravo::AuthData.fetch
6
- if RUBY_VERSION >= "1.9"
4
+ describe ".fetch" do
5
+ it "creates constants for todays data" do
6
+ Bravo.constants.should_not include(:TOKEN, :SIGN)
7
+
8
+ Bravo::AuthData.fetch
9
+
7
10
  Bravo.constants.should include(:TOKEN, :SIGN)
8
- else
9
- Bravo.constants.should include("TOKEN", "SIGN")
10
11
  end
11
12
  end
12
- end
13
+ end