afip_wsfe 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +91 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/afip_wsfe.gemspec +74 -0
- data/lib/afip_wsfe.rb +39 -0
- data/lib/afip_wsfe/auth_data.rb +38 -0
- data/lib/afip_wsfe/authorizer.rb +10 -0
- data/lib/afip_wsfe/bill.rb +196 -0
- data/lib/afip_wsfe/constants.rb +99 -0
- data/lib/afip_wsfe/version.rb +4 -0
- data/lib/afip_wsfe/wsaa.rb +94 -0
- data/test/helper.rb +34 -0
- data/test/test_afip_wsfe.rb +7 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6da65fd172e15ffdff04d438c4975aad59af616a
|
4
|
+
data.tar.gz: c73f90b3248f64370487480526a91d9637945223
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8b205fcb408a0ab70f3252932b6900b94ca3e7961768ab7c482bfbc10c2db40b556ad72b903e7cb92abdd4cd0e27014ea5e9aa5be21025ba43adbfe1b7a19c7c
|
7
|
+
data.tar.gz: '08b0bdc4b5e133ba4a7c3acd0fb0c2521a1dd11e986b8737777871fdd6dc60dd6323e7e53ee3ac874a35ee0c4b373da639b1b1a86be7b23ce8f880789908d0e2'
|
data/.document
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
afip_wsfe
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1.0"
|
12
|
+
gem "juwelier"
|
13
|
+
gem "simplecov", ">= 0"
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (5.2.0)
|
5
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
|
+
i18n (>= 0.7, < 2)
|
7
|
+
minitest (~> 5.1)
|
8
|
+
tzinfo (~> 1.1)
|
9
|
+
addressable (2.5.2)
|
10
|
+
public_suffix (>= 2.0.2, < 4.0)
|
11
|
+
builder (3.2.3)
|
12
|
+
concurrent-ruby (1.0.5)
|
13
|
+
descendants_tracker (0.0.4)
|
14
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
15
|
+
docile (1.3.1)
|
16
|
+
faraday (0.12.2)
|
17
|
+
multipart-post (>= 1.2, < 3)
|
18
|
+
git (1.4.0)
|
19
|
+
github_api (0.18.2)
|
20
|
+
addressable (~> 2.4)
|
21
|
+
descendants_tracker (~> 0.0.4)
|
22
|
+
faraday (~> 0.8)
|
23
|
+
hashie (~> 3.5, >= 3.5.2)
|
24
|
+
oauth2 (~> 1.0)
|
25
|
+
hashie (3.5.7)
|
26
|
+
highline (2.0.0)
|
27
|
+
i18n (1.0.1)
|
28
|
+
concurrent-ruby (~> 1.0)
|
29
|
+
json (1.8.6)
|
30
|
+
juwelier (2.4.9)
|
31
|
+
builder
|
32
|
+
bundler
|
33
|
+
git
|
34
|
+
github_api
|
35
|
+
highline
|
36
|
+
kamelcase (~> 0)
|
37
|
+
nokogiri
|
38
|
+
psych
|
39
|
+
rake
|
40
|
+
rdoc
|
41
|
+
semver2
|
42
|
+
jwt (1.5.6)
|
43
|
+
kamelcase (0.0.2)
|
44
|
+
semver2 (~> 3)
|
45
|
+
mini_portile2 (2.3.0)
|
46
|
+
minitest (5.11.3)
|
47
|
+
multi_json (1.13.1)
|
48
|
+
multi_xml (0.6.0)
|
49
|
+
multipart-post (2.0.0)
|
50
|
+
nokogiri (1.8.3)
|
51
|
+
mini_portile2 (~> 2.3.0)
|
52
|
+
oauth2 (1.4.0)
|
53
|
+
faraday (>= 0.8, < 0.13)
|
54
|
+
jwt (~> 1.0)
|
55
|
+
multi_json (~> 1.3)
|
56
|
+
multi_xml (~> 0.5)
|
57
|
+
rack (>= 1.2, < 3)
|
58
|
+
psych (3.0.2)
|
59
|
+
public_suffix (3.0.2)
|
60
|
+
rack (2.0.5)
|
61
|
+
rake (12.3.1)
|
62
|
+
rdoc (3.12.2)
|
63
|
+
json (~> 1.4)
|
64
|
+
semver2 (3.4.2)
|
65
|
+
shoulda (3.5.0)
|
66
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
67
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
68
|
+
shoulda-context (1.2.2)
|
69
|
+
shoulda-matchers (2.8.0)
|
70
|
+
activesupport (>= 3.0.0)
|
71
|
+
simplecov (0.16.1)
|
72
|
+
docile (~> 1.1)
|
73
|
+
json (>= 1.8, < 3)
|
74
|
+
simplecov-html (~> 0.10.0)
|
75
|
+
simplecov-html (0.10.2)
|
76
|
+
thread_safe (0.3.6)
|
77
|
+
tzinfo (1.2.5)
|
78
|
+
thread_safe (~> 0.1)
|
79
|
+
|
80
|
+
PLATFORMS
|
81
|
+
ruby
|
82
|
+
|
83
|
+
DEPENDENCIES
|
84
|
+
bundler (~> 1.0)
|
85
|
+
juwelier
|
86
|
+
rdoc (~> 3.12)
|
87
|
+
shoulda
|
88
|
+
simplecov
|
89
|
+
|
90
|
+
BUNDLED WITH
|
91
|
+
1.16.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2018 Paco Moreno
|
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.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= afip_wsfe
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to afip_wsfe
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2018 Paco Moreno. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'juwelier'
|
15
|
+
Juwelier::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
17
|
+
gem.name = "afip_wsfe"
|
18
|
+
gem.homepage = "http://github.com/pakerimus/afip_wsfe"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Wrapper para usar web service de factura electrónica de AFIP}
|
21
|
+
gem.description = %Q{Wrapper para usar web service de factura electrónica de AFIP}
|
22
|
+
gem.email = "pakerimus@gmail.com"
|
23
|
+
gem.authors = ["Paco Moreno"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
gem.add_dependency "savon", '~> 2.11.0'
|
26
|
+
end
|
27
|
+
Juwelier::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |test|
|
31
|
+
test.libs << 'lib' << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Code coverage detail"
|
37
|
+
task :simplecov do
|
38
|
+
ENV['COVERAGE'] = "true"
|
39
|
+
Rake::Task['test'].execute
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rdoc/task'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "afip_wsfe #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/afip_wsfe.gemspec
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Generated by juwelier
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Juwelier::Tasks in rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: afip_wsfe 0.1.1 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "afip_wsfe".freeze
|
9
|
+
s.version = "0.1.1"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib".freeze]
|
13
|
+
s.authors = ["Paco Moreno".freeze]
|
14
|
+
s.date = "2018-07-02"
|
15
|
+
s.description = "Wrapper para usar web service de factura electr\u{f3}nica de AFIP".freeze
|
16
|
+
s.email = "pakerimus@gmail.com".freeze
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".ruby-gemset",
|
24
|
+
".ruby-version",
|
25
|
+
"Gemfile",
|
26
|
+
"Gemfile.lock",
|
27
|
+
"LICENSE.txt",
|
28
|
+
"README.rdoc",
|
29
|
+
"Rakefile",
|
30
|
+
"VERSION",
|
31
|
+
"afip_wsfe.gemspec",
|
32
|
+
"lib/afip_wsfe.rb",
|
33
|
+
"lib/afip_wsfe/auth_data.rb",
|
34
|
+
"lib/afip_wsfe/authorizer.rb",
|
35
|
+
"lib/afip_wsfe/bill.rb",
|
36
|
+
"lib/afip_wsfe/constants.rb",
|
37
|
+
"lib/afip_wsfe/version.rb",
|
38
|
+
"lib/afip_wsfe/wsaa.rb",
|
39
|
+
"test/helper.rb",
|
40
|
+
"test/test_afip_wsfe.rb"
|
41
|
+
]
|
42
|
+
s.homepage = "http://github.com/pakerimus/afip_wsfe".freeze
|
43
|
+
s.licenses = ["MIT".freeze]
|
44
|
+
s.rubygems_version = "2.6.14".freeze
|
45
|
+
s.summary = "Wrapper para usar web service de factura electr\u{f3}nica de AFIP".freeze
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
s.specification_version = 4
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_development_dependency(%q<shoulda>.freeze, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
|
53
|
+
s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
54
|
+
s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
|
56
|
+
s.add_runtime_dependency(%q<savon>.freeze, ["~> 2.11.0"])
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<shoulda>.freeze, [">= 0"])
|
59
|
+
s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
|
60
|
+
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
61
|
+
s.add_dependency(%q<juwelier>.freeze, [">= 0"])
|
62
|
+
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
63
|
+
s.add_dependency(%q<savon>.freeze, ["~> 2.11.0"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<shoulda>.freeze, [">= 0"])
|
67
|
+
s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
|
68
|
+
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
69
|
+
s.add_dependency(%q<juwelier>.freeze, [">= 0"])
|
70
|
+
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
71
|
+
s.add_dependency(%q<savon>.freeze, ["~> 2.11.0"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
data/lib/afip_wsfe.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'afip_wsfe/version'
|
4
|
+
require 'afip_wsfe/constants'
|
5
|
+
require 'savon'
|
6
|
+
|
7
|
+
require 'net/http'
|
8
|
+
require 'net/https'
|
9
|
+
|
10
|
+
module AfipWsfe
|
11
|
+
|
12
|
+
# Exception Class for missing or invalid attributes
|
13
|
+
class NullOrInvalidAttribute < StandardError; end
|
14
|
+
|
15
|
+
autoload :Constants, 'afip_wsfe/constants'
|
16
|
+
autoload :Authorizer, 'afip_wsfe/authorizer'
|
17
|
+
autoload :AuthData, 'afip_wsfe/auth_data'
|
18
|
+
autoload :Bill, 'afip_wsfe/bill'
|
19
|
+
autoload :Wsaa, 'afip_wsfe/wsaa'
|
20
|
+
|
21
|
+
extend self
|
22
|
+
|
23
|
+
attr_accessor :environment, :verbose, :log_level,
|
24
|
+
:pkey, :cert, :openssl_bin,
|
25
|
+
:cuit, :own_iva_cond, :sale_point,
|
26
|
+
:default_documento, :default_concepto, :default_moneda
|
27
|
+
|
28
|
+
def auth_hash
|
29
|
+
{"Token" => AfipWsfe::TOKEN, "Sign" => AfipWsfe::SIGN, "Cuit" => AfipWsfe.cuit}
|
30
|
+
end
|
31
|
+
|
32
|
+
def log?
|
33
|
+
AfipWsfe.verbose || ENV["WSFE_VERBOSE"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_token
|
37
|
+
AuthData.remove
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module AfipWsfe
|
2
|
+
|
3
|
+
class AuthData
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
attr_accessor :todays_data_file_name
|
8
|
+
|
9
|
+
def fetch
|
10
|
+
todays_data_file_exists = if File.exists?(todays_data_file_name)
|
11
|
+
true
|
12
|
+
else
|
13
|
+
wsaa = AfipWsfe::Wsaa.new
|
14
|
+
wsaa.login
|
15
|
+
end
|
16
|
+
|
17
|
+
YAML.load_file(todays_data_file_name).each do |k, v|
|
18
|
+
AfipWsfe.const_set(k.to_s.upcase, v) unless AfipWsfe.const_defined?(k.to_s.upcase)
|
19
|
+
end if todays_data_file_exists
|
20
|
+
end
|
21
|
+
|
22
|
+
def auth_hash
|
23
|
+
fetch unless AfipWsfe.constants.include?(:TOKEN) && AfipWsfe.constants.include?(:SIGN)
|
24
|
+
{ 'Token' => AfipWsfe::TOKEN, 'Sign' => AfipWsfe::SIGN, 'Cuit' => AfipWsfe.cuit }
|
25
|
+
end
|
26
|
+
|
27
|
+
def todays_data_file_name
|
28
|
+
@todays_data_file ||= "/tmp/bravo_#{ AfipWsfe.cuit }_#{ Time.zone.today.strftime('%Y_%m_%d') }.yml"
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove
|
32
|
+
AfipWsfe.remove_const(:TOKEN)
|
33
|
+
AfipWsfe.remove_const(:SIGN)
|
34
|
+
File.delete(@todays_data_file)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module AfipWsfe
|
2
|
+
class Bill
|
3
|
+
attr_reader :client, :base_imp, :total
|
4
|
+
attr_accessor :net, :doc_num, :iva_cond, :documento, :concepto, :moneda,
|
5
|
+
:due_date, :fch_serv_desde, :fch_serv_hasta, :fch_emision,
|
6
|
+
:body, :response, :ivas, :nro_comprobante
|
7
|
+
|
8
|
+
def initialize(attrs = {})
|
9
|
+
AfipWsfe.environment ||= :test
|
10
|
+
AfipWsfe::AuthData.fetch
|
11
|
+
|
12
|
+
@client = Savon.client(
|
13
|
+
wsdl: AfipWsfe::URLS[AfipWsfe.environment][:wsfe],
|
14
|
+
namespaces: {
|
15
|
+
"xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
|
16
|
+
"xmlns:ar" => "http://ar.gov.afip.dif.FEV1/"
|
17
|
+
},
|
18
|
+
log: AfipWsfe.log?,
|
19
|
+
log_level: AfipWsfe.log_level || :debug,
|
20
|
+
ssl_cert_key_file: AfipWsfe.pkey,
|
21
|
+
ssl_cert_file: AfipWsfe.cert,
|
22
|
+
ssl_verify_mode: :none,
|
23
|
+
read_timeout: 90,
|
24
|
+
open_timeout: 90,
|
25
|
+
headers: {
|
26
|
+
"Accept-Encoding" => "gzip, deflate",
|
27
|
+
"Connection" => "Keep-Alive"
|
28
|
+
}
|
29
|
+
)
|
30
|
+
|
31
|
+
@body = {"Auth" => AfipWsfe.auth_hash}
|
32
|
+
@net = attrs[:net] || 0
|
33
|
+
self.documento = attrs[:documento] || AfipWsfe.default_documento
|
34
|
+
self.moneda = attrs[:moneda] || AfipWsfe.default_moneda
|
35
|
+
self.iva_cond = attrs[:iva_cond] || :responsable_monotributo
|
36
|
+
self.concepto = attrs[:concepto] || AfipWsfe.default_concepto
|
37
|
+
self.ivas = attrs[:ivas] || Array.new # [ 1, 100.00, 10.50 ], [ 2, 100.00, 21.00 ]
|
38
|
+
end
|
39
|
+
|
40
|
+
def cbte_type
|
41
|
+
AfipWsfe::BILL_TYPE[AfipWsfe.own_iva_cond][iva_cond] || raise(NullOrInvalidAttribute.new, "Please choose a valid document type.")
|
42
|
+
end
|
43
|
+
|
44
|
+
def concept
|
45
|
+
AfipWsfe::CONCEPTOS[concepto] || raise(NullOrInvalidAttribute.new, "Please choose a valid concept.")
|
46
|
+
end
|
47
|
+
|
48
|
+
def document
|
49
|
+
AfipWsfe::DOCUMENTOS[documento] || raise(NullOrInvalidAttribute.new, "Please choose a valid document.")
|
50
|
+
end
|
51
|
+
|
52
|
+
def currency
|
53
|
+
raise(NullOrInvalidAttribute.new, "Please choose a valid currency.") unless AfipWsfe::MONEDAS[moneda]
|
54
|
+
AfipWsfe::MONEDAS[moneda][:codigo]
|
55
|
+
end
|
56
|
+
|
57
|
+
def exchange_rate
|
58
|
+
return 1 if moneda == :peso
|
59
|
+
savon_response = client.call :fe_param_get_cotizacion do
|
60
|
+
body.merge!({"MonId" => AfipWsfe::MONEDAS[moneda][:codigo]})
|
61
|
+
end
|
62
|
+
savon_response.to_hash[:fe_param_get_cotizacion_response][:fe_param_get_cotizacion_result][:result_get][:mon_cotiz].to_f
|
63
|
+
end
|
64
|
+
|
65
|
+
def total
|
66
|
+
@total = net.zero? ? 0 : net + iva_sum
|
67
|
+
end
|
68
|
+
|
69
|
+
def iva_sum
|
70
|
+
@iva_sum = 0.0
|
71
|
+
self.ivas.each{ |i|
|
72
|
+
@iva_sum += i[1] * AfipWsfe::ALIC_IVA[ i[0] ][1]
|
73
|
+
}
|
74
|
+
@iva_sum.round(2)
|
75
|
+
end
|
76
|
+
|
77
|
+
def authorize
|
78
|
+
body = setup_bill
|
79
|
+
savon_response = client.call(:fecae_solicitar, message: body)
|
80
|
+
setup_response(savon_response.to_hash)
|
81
|
+
self.authorized?
|
82
|
+
end
|
83
|
+
|
84
|
+
def setup_bill
|
85
|
+
fecha_emision = (fch_emision || Time.zone.today).strftime('%Y%m%d')
|
86
|
+
|
87
|
+
nro_comprobante ||= next_bill_number
|
88
|
+
|
89
|
+
array_ivas = Array.new
|
90
|
+
self.ivas.each{ |i|
|
91
|
+
array_ivas << {
|
92
|
+
"Id" => AfipWsfe::ALIC_IVA[ i[0] ][0],
|
93
|
+
"BaseImp" => i[1] ,
|
94
|
+
"Importe" => i[2] }
|
95
|
+
}
|
96
|
+
|
97
|
+
fecaereq = {
|
98
|
+
"FeCAEReq" => {
|
99
|
+
"FeCabReq" => AfipWsfe::Bill.header(cbte_type),
|
100
|
+
"FeDetReq" => {
|
101
|
+
"FECAEDetRequest" => {
|
102
|
+
"CbteDesde" => nro_comprobante,
|
103
|
+
"CbteHasta" => nro_comprobante,
|
104
|
+
"Concepto" => concept,
|
105
|
+
"DocTipo" => document,
|
106
|
+
"DocNro" => doc_num,
|
107
|
+
"CbteFch" => fecha_emision,
|
108
|
+
"ImpTotConc" => 0.00,
|
109
|
+
"MonId" => currency,
|
110
|
+
"MonCotiz" => exchange_rate,
|
111
|
+
"ImpOpEx" => 0.00,
|
112
|
+
"ImpTrib" => 0.00,
|
113
|
+
"ImpNeto" => net.to_f
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
detail = fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
|
120
|
+
|
121
|
+
if AfipWsfe.own_iva_cond == :responsable_monotributo
|
122
|
+
detail["ImpTotal"] = net.to_f
|
123
|
+
else
|
124
|
+
detail["ImpIVA"] = iva_sum
|
125
|
+
detail["ImpTotal"] = total.to_f.round(2)
|
126
|
+
detail["Iva"] = { "AlicIva" => array_ivas }
|
127
|
+
end
|
128
|
+
|
129
|
+
unless concepto == "Productos" # En "Productos" ("01"), si se mandan estos parámetros la afip rechaza.
|
130
|
+
detail.merge!({"FchServDesde" => fch_serv_desde || today,
|
131
|
+
"FchServHasta" => fch_serv_hasta || today,
|
132
|
+
"FchVtoPago" => due_date || today})
|
133
|
+
end
|
134
|
+
|
135
|
+
body.merge!(fecaereq)
|
136
|
+
end
|
137
|
+
|
138
|
+
def next_bill_number
|
139
|
+
var = {"Auth" => AfipWsfe.auth_hash,"PtoVta" => AfipWsfe.sale_point, "CbteTipo" => cbte_type}
|
140
|
+
resp = client.call :fe_comp_ultimo_autorizado do
|
141
|
+
message(var)
|
142
|
+
end
|
143
|
+
|
144
|
+
resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1
|
145
|
+
end
|
146
|
+
|
147
|
+
def authorized?
|
148
|
+
!response.nil? && response[:header_result] == "A" && response[:detail_result] == "A"
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
class << self
|
154
|
+
def header(cbte_type)#todo sacado de la factura
|
155
|
+
{"CantReg" => "1", "CbteTipo" => cbte_type, "PtoVta" => AfipWsfe.sale_point}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def setup_response(the_response)
|
160
|
+
result = the_response[:fecae_solicitar_response][:fecae_solicitar_result]
|
161
|
+
|
162
|
+
if not result[:fe_det_resp] or not result[:fe_cab_resp] then
|
163
|
+
self.response = {
|
164
|
+
errores: result[:errors],
|
165
|
+
header_result: {resultado: "X"},
|
166
|
+
observaciones: nil
|
167
|
+
}
|
168
|
+
return
|
169
|
+
end
|
170
|
+
|
171
|
+
response_header = result[:fe_cab_resp]
|
172
|
+
response_detail = result[:fe_det_resp][:fecae_det_response]
|
173
|
+
|
174
|
+
request_header = body["FeCAEReq"]["FeCabReq"].underscore_keys.symbolize_keys
|
175
|
+
request_detail = body["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"].underscore_keys.symbolize_keys
|
176
|
+
|
177
|
+
response_detail.merge!( result[:errors] ) if result[:errors]
|
178
|
+
|
179
|
+
self.response = {
|
180
|
+
header_result: response_header.delete(:resultado),
|
181
|
+
authorized_on: response_header.delete(:fch_proceso),
|
182
|
+
detail_result: response_detail.delete(:resultado),
|
183
|
+
cae_due_date: response_detail.delete(:cae_fch_vto),
|
184
|
+
cae: response_detail.delete(:cae),
|
185
|
+
iva_id: request_detail.delete(:id),
|
186
|
+
iva_importe: request_detail.delete(:importe),
|
187
|
+
moneda: request_detail.delete(:mon_id),
|
188
|
+
cotizacion: request_detail.delete(:mon_cotiz),
|
189
|
+
iva_base_imp: request_detail.delete(:base_imp),
|
190
|
+
doc_num: request_detail.delete(:doc_nro),
|
191
|
+
observaciones: response_detail.delete(:observaciones),
|
192
|
+
errores: response_detail.delete(:err)
|
193
|
+
}.merge!(request_header).merge!(request_detail)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module AfipWsfe
|
2
|
+
# This constant contains the invoice types mappings between codes and names used by WSFE.
|
3
|
+
CBTE_TIPO = {
|
4
|
+
'01'=>'Factura A',
|
5
|
+
'02'=>'Nota de Débito A',
|
6
|
+
'03'=>'Nota de Crédito A',
|
7
|
+
'04'=>'Recibos A',
|
8
|
+
'05'=>'Notas de Venta al contado A',
|
9
|
+
'06'=>'Factura B',
|
10
|
+
'07'=>'Nota de Debito B',
|
11
|
+
'08'=>'Nota de Credito B',
|
12
|
+
'09'=>'Recibos B',
|
13
|
+
'10'=>'Notas de Venta al contado B',
|
14
|
+
'11'=>'Factura C',
|
15
|
+
'12'=>'Nota de Debito C',
|
16
|
+
'13'=>'Nota de Credito C',
|
17
|
+
'34'=>'Cbtes. A del Anexo I, Apartado A,inc.f),R.G.Nro. 1415',
|
18
|
+
'35'=>'Cbtes. B del Anexo I,Apartado A,inc. f),R.G. Nro. 1415',
|
19
|
+
'39'=>'Otros comprobantes A que cumplan con R.G.Nro. 1415',
|
20
|
+
'40'=>'Otros comprobantes B que cumplan con R.G.Nro. 1415',
|
21
|
+
'60'=>'Cta de Vta y Liquido prod. A',
|
22
|
+
'61'=>'Cta de Vta y Liquido prod. B',
|
23
|
+
'63'=>'Liquidacion A',
|
24
|
+
'64'=>'Liquidacion B'
|
25
|
+
}
|
26
|
+
|
27
|
+
# Name to code mapping for Sale types.
|
28
|
+
CONCEPTOS = {
|
29
|
+
'Productos' => 1,
|
30
|
+
'Servicios' => 2,
|
31
|
+
'Productos y Servicios' => 3
|
32
|
+
}
|
33
|
+
|
34
|
+
# Name to code mapping for types of documents.
|
35
|
+
DOCUMENTOS = {
|
36
|
+
'CUIT'=>'80',
|
37
|
+
'CUIL'=>'86',
|
38
|
+
'CDI'=>'87',
|
39
|
+
'LE'=>'89',
|
40
|
+
'LC'=>'90',
|
41
|
+
'CI Extranjera'=>'91',
|
42
|
+
'en tramite'=>'92',
|
43
|
+
'Acta Nacimiento'=>'93',
|
44
|
+
'CI Bs. As. RNP'=>'95',
|
45
|
+
'DNI'=>'96',
|
46
|
+
'Pasaporte'=>'94',
|
47
|
+
'Doc. (Otro)'=>'99'
|
48
|
+
}
|
49
|
+
|
50
|
+
# Currency code and names hash identified by a symbol
|
51
|
+
MONEDAS = {
|
52
|
+
:peso => { codigo: 'PES', nombre: 'Pesos Argentinos' },
|
53
|
+
:dolar => { codigo: 'DOL', nombre: 'Dolar Estadounidense' },
|
54
|
+
:real => { codigo: '012', nombre: 'Real' },
|
55
|
+
:euro => { codigo: '060', nombre: 'Euro' },
|
56
|
+
:oro => { codigo: '049', nombre: 'Gramos de Oro Fino' }
|
57
|
+
}
|
58
|
+
|
59
|
+
# Tax percentage and codes according to each iva combination
|
60
|
+
ALIC_IVA = [
|
61
|
+
['03', 0],
|
62
|
+
['04', 0.105],
|
63
|
+
['05', 0.21],
|
64
|
+
['06', 0.27]
|
65
|
+
]
|
66
|
+
|
67
|
+
BILL_TYPE = {
|
68
|
+
:responsable_inscripto => {
|
69
|
+
:responsable_inscripto => '01',
|
70
|
+
:consumidor_final => '06',
|
71
|
+
:exento => '06',
|
72
|
+
:responsable_monotributo => '06',
|
73
|
+
:nota_credito_a => '03',
|
74
|
+
:nota_credito_b => '08',
|
75
|
+
:nota_debito_a => '02',
|
76
|
+
:nota_debito_b => '07'
|
77
|
+
},
|
78
|
+
:responsable_monotributo => {
|
79
|
+
:responsable_inscripto => '11',
|
80
|
+
:consumidor_final => '11',
|
81
|
+
:exento => '11',
|
82
|
+
:responsable_monotributo => '11',
|
83
|
+
:nota_credito_c => '13',
|
84
|
+
:nota_debito_c => '12'
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
# This hash keeps the set of urls for wsaa and wsfe for production and testing envs
|
89
|
+
URLS = {
|
90
|
+
test: {
|
91
|
+
wsaa: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl',
|
92
|
+
wsfe: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL'
|
93
|
+
},
|
94
|
+
production: {
|
95
|
+
wsaa: 'https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl',
|
96
|
+
wsfe: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx'
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module AfipWsfe
|
2
|
+
# Authorization class. Handles interactions wiht the WSAA, to provide
|
3
|
+
# valid key and signature that will last for a day.
|
4
|
+
#
|
5
|
+
class Wsaa
|
6
|
+
|
7
|
+
def initialize(url=nil)
|
8
|
+
AfipWsfe.environment ||= :test
|
9
|
+
@client = Savon.client(
|
10
|
+
wsdl: AfipWsfe::URLS[AfipWsfe.environment][:wsaa],
|
11
|
+
namespaces: {
|
12
|
+
"xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
|
13
|
+
"xmlns:ar" => "http://ar.gov.afip.dif.FEV1/"
|
14
|
+
},
|
15
|
+
log: AfipWsfe.log?,
|
16
|
+
log_level: AfipWsfe.log_level || :debug,
|
17
|
+
ssl_verify_mode: :none,
|
18
|
+
read_timeout: 90,
|
19
|
+
open_timeout: 90,
|
20
|
+
headers: {
|
21
|
+
"Accept-Encoding" => "gzip, deflate",
|
22
|
+
"Connection" => "Keep-Alive"
|
23
|
+
},
|
24
|
+
convert_request_keys_to: :none
|
25
|
+
)
|
26
|
+
|
27
|
+
@cms = nil
|
28
|
+
@performed = false
|
29
|
+
@response = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def login
|
33
|
+
raise "Archivo de llave privada no encontrado en #{ AfipWsfe.pkey }" unless File.exists?(AfipWsfe.pkey)
|
34
|
+
raise "Archivo certificado no encontrado en #{ AfipWsfe.cert }" unless File.exists?(AfipWsfe.cert)
|
35
|
+
build_tra
|
36
|
+
call_web_service
|
37
|
+
parse_response
|
38
|
+
status
|
39
|
+
end
|
40
|
+
|
41
|
+
def status
|
42
|
+
return false unless @performed
|
43
|
+
@response.success?
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def build_tra
|
49
|
+
now = Time.zone.now
|
50
|
+
from = now.beginning_of_day.strftime('%FT%T%:z')
|
51
|
+
to = now.end_of_day.strftime('%FT%T%:z')
|
52
|
+
id = now.strftime('%s')
|
53
|
+
|
54
|
+
tra = {
|
55
|
+
"header" => {
|
56
|
+
"uniqueId" => id,
|
57
|
+
"generationTime" => from,
|
58
|
+
"expirationTime" => to
|
59
|
+
},
|
60
|
+
"service" => "wsfe"
|
61
|
+
}.to_xml(root: "loginTicketRequest")
|
62
|
+
|
63
|
+
@cms = `echo '#{ tra }' |
|
64
|
+
#{ AfipWsfe.openssl_bin } cms -sign -in /dev/stdin -signer #{ AfipWsfe.cert } -inkey #{ AfipWsfe.pkey } -nodetach -outform der |
|
65
|
+
#{ AfipWsfe.openssl_bin } base64 -e`
|
66
|
+
end
|
67
|
+
|
68
|
+
def call_web_service
|
69
|
+
body = { "ns1:in0" => @cms }
|
70
|
+
@response = client.call :login_cms, message: body
|
71
|
+
@performed = true
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_response
|
75
|
+
if status
|
76
|
+
response_hash = Hash.from_xml @response.body[:login_cms_response][:login_cms_return]
|
77
|
+
token = response_hash["loginTicketResponse"]["credentials"]["token"]
|
78
|
+
sign = response_hash["loginTicketResponse"]["credentials"]["sign"]
|
79
|
+
write_yaml(token, sign)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def write_yaml(token, sign)
|
84
|
+
filename = "/tmp/bravo_#{ AfipWsfe.cuit }_#{ Time.zone.today.strftime('%Y_%m_%d') }.yml"
|
85
|
+
content = {
|
86
|
+
token: token,
|
87
|
+
sign: sign
|
88
|
+
}
|
89
|
+
File.open(filename, 'w') { |f|
|
90
|
+
f.write content.to_yaml
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
module SimpleCov::Configuration
|
4
|
+
def clean_filters
|
5
|
+
@filters = []
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
SimpleCov.configure do
|
10
|
+
clean_filters
|
11
|
+
load_adapter 'test_frameworks'
|
12
|
+
end
|
13
|
+
|
14
|
+
ENV["COVERAGE"] && SimpleCov.start do
|
15
|
+
add_filter "/.rvm/"
|
16
|
+
end
|
17
|
+
require 'rubygems'
|
18
|
+
require 'bundler'
|
19
|
+
begin
|
20
|
+
Bundler.setup(:default, :development)
|
21
|
+
rescue Bundler::BundlerError => e
|
22
|
+
$stderr.puts e.message
|
23
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
24
|
+
exit e.status_code
|
25
|
+
end
|
26
|
+
require 'test/unit'
|
27
|
+
require 'shoulda'
|
28
|
+
|
29
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
30
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
31
|
+
require 'afip_wsfe'
|
32
|
+
|
33
|
+
class Test::Unit::TestCase
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: afip_wsfe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paco Moreno
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: shoulda
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rdoc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: juwelier
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: savon
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.11.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.11.0
|
97
|
+
description: Wrapper para usar web service de factura electrónica de AFIP
|
98
|
+
email: pakerimus@gmail.com
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files:
|
102
|
+
- LICENSE.txt
|
103
|
+
- README.rdoc
|
104
|
+
files:
|
105
|
+
- ".document"
|
106
|
+
- ".ruby-gemset"
|
107
|
+
- ".ruby-version"
|
108
|
+
- Gemfile
|
109
|
+
- Gemfile.lock
|
110
|
+
- LICENSE.txt
|
111
|
+
- README.rdoc
|
112
|
+
- Rakefile
|
113
|
+
- VERSION
|
114
|
+
- afip_wsfe.gemspec
|
115
|
+
- lib/afip_wsfe.rb
|
116
|
+
- lib/afip_wsfe/auth_data.rb
|
117
|
+
- lib/afip_wsfe/authorizer.rb
|
118
|
+
- lib/afip_wsfe/bill.rb
|
119
|
+
- lib/afip_wsfe/constants.rb
|
120
|
+
- lib/afip_wsfe/version.rb
|
121
|
+
- lib/afip_wsfe/wsaa.rb
|
122
|
+
- test/helper.rb
|
123
|
+
- test/test_afip_wsfe.rb
|
124
|
+
homepage: http://github.com/pakerimus/afip_wsfe
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.6.14
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Wrapper para usar web service de factura electrónica de AFIP
|
148
|
+
test_files: []
|