Afip 1.4.6 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- metadata +4 -27
- data/.gitignore +0 -8
- data/Afip-1.2.1.gem +0 -0
- data/Afip-1.4.gem +0 -0
- data/Afip.gemspec +0 -41
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -49
- data/LICENSE.txt +0 -21
- data/README.md +0 -39
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/lib/Afip.rb +0 -59
- data/lib/Afip/auth_data.rb +0 -71
- data/lib/Afip/authorizer.rb +0 -10
- data/lib/Afip/bill.rb +0 -274
- data/lib/Afip/constants.rb +0 -115
- data/lib/Afip/core_ext/float.rb +0 -8
- data/lib/Afip/core_ext/hash.rb +0 -23
- data/lib/Afip/core_ext/string.rb +0 -12
- data/lib/Afip/ctg.rb +0 -234
- data/lib/Afip/padron.rb +0 -184
- data/lib/Afip/version.rb +0 -3
- data/lib/Afip/wsaa.rb +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45fffc2182c70856fbf8f8c12facd1bd5f8bf0653bbd319f24a87cab99da787e
|
4
|
+
data.tar.gz: f38fe48699d8ab2c5c790fbb723d0e04c7f44fa1050203605f8171875ccbc82c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b94a4851e7ce54f99cc2ee98af7a59401cf67236b6b0c27b2317d63c602ce2d38f76362125fdb2015196288f756c8b882640c50b44a529dfded54496b2b5e0b0
|
7
|
+
data.tar.gz: e48f2867900d2bf664ee1ac067bb8393738b5f67af95e48c05ca26fbdd3ec8dff3da1f31b7905a70d20519e428a7eb25914f7abd2398359e0cb9abc40b4550db
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Afip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Facundo A. Díaz Martínez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: savon
|
@@ -86,30 +86,7 @@ email:
|
|
86
86
|
executables: []
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
|
-
files:
|
90
|
-
- ".gitignore"
|
91
|
-
- Afip-1.2.1.gem
|
92
|
-
- Afip-1.4.gem
|
93
|
-
- Afip.gemspec
|
94
|
-
- Gemfile
|
95
|
-
- Gemfile.lock
|
96
|
-
- LICENSE.txt
|
97
|
-
- README.md
|
98
|
-
- Rakefile
|
99
|
-
- bin/console
|
100
|
-
- bin/setup
|
101
|
-
- lib/Afip.rb
|
102
|
-
- lib/Afip/auth_data.rb
|
103
|
-
- lib/Afip/authorizer.rb
|
104
|
-
- lib/Afip/bill.rb
|
105
|
-
- lib/Afip/constants.rb
|
106
|
-
- lib/Afip/core_ext/float.rb
|
107
|
-
- lib/Afip/core_ext/hash.rb
|
108
|
-
- lib/Afip/core_ext/string.rb
|
109
|
-
- lib/Afip/ctg.rb
|
110
|
-
- lib/Afip/padron.rb
|
111
|
-
- lib/Afip/version.rb
|
112
|
-
- lib/Afip/wsaa.rb
|
89
|
+
files: []
|
113
90
|
homepage: http://litecode.com.ar/
|
114
91
|
licenses:
|
115
92
|
- MIT
|
@@ -130,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
107
|
- !ruby/object:Gem::Version
|
131
108
|
version: '0'
|
132
109
|
requirements: []
|
133
|
-
rubygems_version: 3.
|
110
|
+
rubygems_version: 3.1.2
|
134
111
|
signing_key:
|
135
112
|
specification_version: 4
|
136
113
|
summary: Comunicacion con AFIP
|
data/.gitignore
DELETED
data/Afip-1.2.1.gem
DELETED
Binary file
|
data/Afip-1.4.gem
DELETED
Binary file
|
data/Afip.gemspec
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "Afip/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "Afip"
|
8
|
-
spec.version = Afip::VERSION
|
9
|
-
spec.authors = ["Facundo A. Díaz Martínez"]
|
10
|
-
spec.email = ["facundo_diaz_martinez@hotmail.com"]
|
11
|
-
|
12
|
-
spec.summary = %q{Comunicacion con AFIP}
|
13
|
-
spec.description = %q{Gema para la comunicacion con los Web Services de AFIP.}
|
14
|
-
spec.homepage = "http://litecode.com.ar/"
|
15
|
-
spec.license = "MIT"
|
16
|
-
|
17
|
-
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
-
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
-
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
21
|
-
else
|
22
|
-
raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
-
"public gem pushes."
|
24
|
-
end
|
25
|
-
|
26
|
-
# Specify which files should be added to the gem when it is released.
|
27
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
28
|
-
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
29
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
30
|
-
end
|
31
|
-
|
32
|
-
spec.bindir = "exe"
|
33
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
|
-
spec.require_paths = ["lib"]
|
35
|
-
|
36
|
-
spec.add_dependency "savon"
|
37
|
-
spec.add_dependency "httpi"
|
38
|
-
spec.add_dependency "nokogiri", ">= 1.10.4"
|
39
|
-
spec.add_development_dependency "bundler"
|
40
|
-
spec.add_development_dependency "rake"
|
41
|
-
end
|
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
Afip (1.4.5)
|
5
|
-
httpi
|
6
|
-
nokogiri (>= 1.10.4)
|
7
|
-
savon
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: https://rubygems.org/
|
11
|
-
specs:
|
12
|
-
akami (1.3.1)
|
13
|
-
gyoku (>= 0.4.0)
|
14
|
-
nokogiri
|
15
|
-
builder (3.2.4)
|
16
|
-
gyoku (1.3.1)
|
17
|
-
builder (>= 2.1.2)
|
18
|
-
httpi (2.4.4)
|
19
|
-
rack
|
20
|
-
socksify
|
21
|
-
mini_portile2 (2.4.0)
|
22
|
-
nokogiri (1.10.9)
|
23
|
-
mini_portile2 (~> 2.4.0)
|
24
|
-
nori (2.6.0)
|
25
|
-
rack (2.2.2)
|
26
|
-
rake (10.5.0)
|
27
|
-
savon (2.12.0)
|
28
|
-
akami (~> 1.2)
|
29
|
-
builder (>= 2.1.2)
|
30
|
-
gyoku (~> 1.2)
|
31
|
-
httpi (~> 2.3)
|
32
|
-
nokogiri (>= 1.8.1)
|
33
|
-
nori (~> 2.4)
|
34
|
-
wasabi (~> 3.4)
|
35
|
-
socksify (1.7.1)
|
36
|
-
wasabi (3.5.0)
|
37
|
-
httpi (~> 2.0)
|
38
|
-
nokogiri (>= 1.4.2)
|
39
|
-
|
40
|
-
PLATFORMS
|
41
|
-
ruby
|
42
|
-
|
43
|
-
DEPENDENCIES
|
44
|
-
Afip!
|
45
|
-
bundler
|
46
|
-
rake
|
47
|
-
|
48
|
-
BUNDLED WITH
|
49
|
-
1.17.3
|
data/LICENSE.txt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (c) 2018 Facundo A. Díaz Martínez
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|
data/README.md
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# Afip
|
2
|
-
|
3
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/Afip`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
gem 'Afip'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install Afip
|
22
|
-
|
23
|
-
## Usage
|
24
|
-
|
25
|
-
TODO: Write usage instructions here
|
26
|
-
|
27
|
-
## Development
|
28
|
-
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
-
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
-
|
33
|
-
## Contributing
|
34
|
-
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/Afip.
|
36
|
-
|
37
|
-
## License
|
38
|
-
|
39
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
DELETED
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "Afip"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start(__FILE__)
|
data/bin/setup
DELETED
data/lib/Afip.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require "Afip/version"
|
2
|
-
require "bundler/setup"
|
3
|
-
require "Afip/constants"
|
4
|
-
require "savon"
|
5
|
-
require "Afip/core_ext/float"
|
6
|
-
require "Afip/core_ext/hash"
|
7
|
-
require "Afip/core_ext/string"
|
8
|
-
|
9
|
-
require 'net/http'
|
10
|
-
require 'net/https'
|
11
|
-
|
12
|
-
#require 'net/http'
|
13
|
-
require 'net/https'
|
14
|
-
module Afip
|
15
|
-
|
16
|
-
class NullOrInvalidAttribute < StandardError; end
|
17
|
-
|
18
|
-
def self.root
|
19
|
-
File.expand_path '../..', __FILE__
|
20
|
-
end
|
21
|
-
|
22
|
-
autoload :Authorizer, "Afip/authorizer"
|
23
|
-
autoload :AuthData, "Afip/auth_data"
|
24
|
-
autoload :Padron, "Afip/padron"
|
25
|
-
autoload :Wsaa, "Afip/wsaa"
|
26
|
-
autoload :Bill, "Afip/bill"
|
27
|
-
autoload :CTG, "Afip/ctg"
|
28
|
-
|
29
|
-
|
30
|
-
class << self
|
31
|
-
mattr_accessor :cuit, :pkey, :cert, :environment, :openssl_bin,
|
32
|
-
:default_concepto, :default_documento, :default_moneda, :own_iva_cond, :service_url, :auth_url
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.setup(&block)
|
36
|
-
yield self
|
37
|
-
end
|
38
|
-
|
39
|
-
extend self
|
40
|
-
|
41
|
-
def auth_hash(service = "wsfe")
|
42
|
-
case service
|
43
|
-
when "wsfe"
|
44
|
-
{ 'Token' => Afip::TOKEN, 'Sign' => Afip::SIGN, 'Cuit' => Afip.cuit }
|
45
|
-
when "ws_sr_padron_a4"
|
46
|
-
{ 'token' => Afip::TOKEN, 'sign' => Afip::SIGN, 'cuitRepresentado' => Afip.cuit }
|
47
|
-
when "wsctg"
|
48
|
-
{ 'token' => Afip::TOKEN, 'sign' => Afip::SIGN, 'cuitRepresentado' => Afip.cuit }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def log?
|
53
|
-
Afip.verbose || ENV["VERBOSE"]
|
54
|
-
end
|
55
|
-
|
56
|
-
def deleteToken
|
57
|
-
AuthData.deleteToken
|
58
|
-
end
|
59
|
-
end
|
data/lib/Afip/auth_data.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
module Afip
|
2
|
-
|
3
|
-
# This class handles authorization data
|
4
|
-
#
|
5
|
-
class AuthData
|
6
|
-
|
7
|
-
class << self
|
8
|
-
|
9
|
-
attr_accessor :environment, :todays_data_file_name
|
10
|
-
|
11
|
-
# Fetches WSAA Authorization Data to build the datafile for the day.
|
12
|
-
# It requires the private key file and the certificate to exist and
|
13
|
-
# to be configured as Afip.pkey and Afip.cert
|
14
|
-
#
|
15
|
-
def fetch(service = "wsfe")
|
16
|
-
unless File.exists?(Afip.pkey)
|
17
|
-
raise "Archivo de llave privada no encontrado en #{ Afip.pkey }"
|
18
|
-
end
|
19
|
-
|
20
|
-
unless File.exists?(Afip.cert)
|
21
|
-
raise "Archivo certificado no encontrado en #{ Afip.cert }"
|
22
|
-
end
|
23
|
-
|
24
|
-
unless File.exists?(todays_data_file_name)
|
25
|
-
Afip::Wsaa.login(service)
|
26
|
-
end
|
27
|
-
|
28
|
-
YAML.load_file(todays_data_file_name).each do |k, v|
|
29
|
-
Afip.const_set(k.to_s.upcase, v) #unless Afip.const_defined?(k.to_s.upcase)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns the authorization hash, containing the Token, Signature and Cuit
|
34
|
-
# @return [Hash]
|
35
|
-
#
|
36
|
-
def auth_hash(service = "wsfe")
|
37
|
-
fetch unless Afip.constants.include?(:TOKEN) && Afip.constants.include?(:SIGN)
|
38
|
-
case service
|
39
|
-
when "wsfe"
|
40
|
-
{ 'Token' => Afip::TOKEN, 'Sign' => Afip::SIGN, 'Cuit' => Afip.cuit }
|
41
|
-
when "ws_sr_padron_a4"
|
42
|
-
{ 'token' => Afip::TOKEN, 'sign' => Afip::SIGN, 'cuitRepresentado' => Afip.cuit }
|
43
|
-
when "wsctg"
|
44
|
-
{ 'token' => Afip::TOKEN, 'sign' => Afip::SIGN, 'cuitRepresentado' => Afip.cuit }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the right wsaa url for the specific environment
|
49
|
-
# @return [String]
|
50
|
-
#
|
51
|
-
def wsaa_url
|
52
|
-
Afip::URLS[Afip.environment][:wsaa]
|
53
|
-
end
|
54
|
-
|
55
|
-
# Returns the right wsfe url for the specific environment
|
56
|
-
# @return [String]
|
57
|
-
#
|
58
|
-
def wsfe_url
|
59
|
-
raise 'Environment not sent to either :test or :production' unless Afip::URLS.keys.include? environment
|
60
|
-
Afip::URLS[Afip.environment][:wsfe]
|
61
|
-
end
|
62
|
-
|
63
|
-
# Creates the data file name for a cuit number and the current day
|
64
|
-
# @return [String]
|
65
|
-
#
|
66
|
-
def todays_data_file_name
|
67
|
-
"/tmp/#{environment.to_s}_Afip_#{ Afip.cuit }_#{ Time.new.strftime('%Y_%m_%d') }.yml"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
data/lib/Afip/authorizer.rb
DELETED
data/lib/Afip/bill.rb
DELETED
@@ -1,274 +0,0 @@
|
|
1
|
-
module Afip
|
2
|
-
class Bill
|
3
|
-
attr_reader :cbte_type, :body, :response, :fecha_emision, :total, :client
|
4
|
-
attr_accessor :net, :doc_num, :iva_cond, :documento, :concepto, :moneda, :cbte_type,
|
5
|
-
:due_date, :fch_serv_desde, :fch_serv_hasta, :fch_emision,
|
6
|
-
:ivas, :sale_point, :cant_reg, :no_gravado, :gravado, :exento, :otros_imp, :tributos, :opcionales
|
7
|
-
|
8
|
-
def initialize(attrs={})
|
9
|
-
@client = Bill.set_client
|
10
|
-
@sale_point = attrs[:sale_point]
|
11
|
-
@body = { "Auth" => Afip.auth_hash }
|
12
|
-
@net = attrs[:net] || 0.0
|
13
|
-
@documento = attrs[:documento] || Afip.default_documento
|
14
|
-
@moneda = attrs[:moneda] || Afip.default_moneda
|
15
|
-
@iva_cond = attrs[:iva_cond]
|
16
|
-
@concepto = attrs[:concepto] || Afip.default_concepto
|
17
|
-
@ivas = attrs[:ivas] || Array.new # [ 1, 100.00, 10.50 ], [ 2, 100.00, 21.00 ]
|
18
|
-
@fecha_emision = attrs[:fch_emision] || Time.new
|
19
|
-
@fch_serv_hasta = attrs[:fch_serv_hasta]
|
20
|
-
@fch_serv_desde = attrs[:fch_serv_desde]
|
21
|
-
@due_date = attrs[:due_date]
|
22
|
-
@cbte_type = attrs[:cbte_type]
|
23
|
-
@cant_reg = attrs[:cant_reg] || 1
|
24
|
-
@no_gravado = attrs[:no_gravado] || 0.0
|
25
|
-
@gravado = attrs[:gravado] || 0.0
|
26
|
-
@exento = attrs[:exento] || 0.0
|
27
|
-
@otros_imp = attrs[:otros_imp] || 0.0
|
28
|
-
@total = net.to_f + iva_sum.to_f + exento.to_f + no_gravado.to_f + otros_imp.to_f
|
29
|
-
@tributos = attrs[:tributos] || []
|
30
|
-
@opcionales = attrs[:opcionales] || []
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.get_ptos_vta
|
34
|
-
client = set_client
|
35
|
-
body = { "Auth" => Afip.auth_hash }
|
36
|
-
response = client.call(:fe_param_get_ptos_venta, message: body)
|
37
|
-
if response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:errors].nil?
|
38
|
-
if response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta].is_a?(Hash)
|
39
|
-
response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta][:nro]
|
40
|
-
else
|
41
|
-
response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta].map{|r| r[:nro]}
|
42
|
-
end
|
43
|
-
else
|
44
|
-
[]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.get_tipos_cbte
|
49
|
-
client = set_client
|
50
|
-
body = { "Auth" => Afip.auth_hash }
|
51
|
-
response = client.call(:fe_param_get_tipos_cbte, message: body)
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.get_tributos
|
55
|
-
client = set_client
|
56
|
-
body = { "Auth" => Afip.auth_hash }
|
57
|
-
response = client.call(:fe_param_get_tipos_tributos, message: body)
|
58
|
-
if response.body[:fe_param_get_tipos_tributos_response][:fe_param_get_tipos_tributos_result][:errors].nil?
|
59
|
-
response.body[:fe_param_get_tipos_tributos_response][:fe_param_get_tipos_tributos_result][:result_get][:tributo_tipo]
|
60
|
-
else
|
61
|
-
[]
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.set_client
|
66
|
-
Afip::AuthData.fetch
|
67
|
-
@client = Savon.client(
|
68
|
-
wsdl: Afip.service_url,
|
69
|
-
namespaces: { "xmlns" => "http://ar.gov.afip.dif.FEV1/" },
|
70
|
-
log_level: :debug,
|
71
|
-
ssl_cert_key_file: Afip.pkey,
|
72
|
-
ssl_cert_file: Afip.cert,
|
73
|
-
ssl_verify_mode: :none,
|
74
|
-
read_timeout: 90,
|
75
|
-
open_timeout: 90,
|
76
|
-
headers: { "Accept-Encoding" => "gzip, deflate", "Connection" => "Keep-Alive" }
|
77
|
-
)
|
78
|
-
end
|
79
|
-
|
80
|
-
def authorize
|
81
|
-
body = setup_bill
|
82
|
-
pp response = client.call(:fecae_solicitar, message: body)
|
83
|
-
setup_response(response.to_hash)
|
84
|
-
authorized?
|
85
|
-
end
|
86
|
-
|
87
|
-
def setup_bill
|
88
|
-
array_ivas = {}
|
89
|
-
array_ivas["AlicIva"] = ivas.map{ |i| { "Id" => i[0], "BaseImp" => i[1].round(2), "Importe" => i[2].round(2)} unless ["01", "02"].include?(i[0])}.compact
|
90
|
-
|
91
|
-
array_tributos = {}
|
92
|
-
array_tributos["Tributo"] = tributos.map{ |t|
|
93
|
-
if t[1].blank?
|
94
|
-
{
|
95
|
-
"Id" => t[0],
|
96
|
-
"BaseImp" => t[2].to_f.round(2),
|
97
|
-
"Alic" => t[3].to_f.round(2),
|
98
|
-
"Importe" => t[4].to_f.round(2)
|
99
|
-
}
|
100
|
-
else
|
101
|
-
{
|
102
|
-
"Id" => t[0],
|
103
|
-
"Desc" => t[1],
|
104
|
-
"BaseImp" => t[2].to_f.round(2),
|
105
|
-
"Alic" => t[3].to_f.round(2),
|
106
|
-
"Importe" => t[4].to_f.round(2)
|
107
|
-
}
|
108
|
-
end
|
109
|
-
}
|
110
|
-
|
111
|
-
array_opcionales = {}
|
112
|
-
array_opcionales["Opcional"] = opcionales.map{ |t|
|
113
|
-
{
|
114
|
-
"Id" => t[:id],
|
115
|
-
"Valor" => t[:valor]
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
|
-
|
120
|
-
fecaereq = {
|
121
|
-
"FeCAEReq" => {
|
122
|
-
"FeCabReq" => Afip::Bill.header(cant_reg, cbte_type, sale_point),
|
123
|
-
"FeDetReq" => {
|
124
|
-
"FECAEDetRequest" => {
|
125
|
-
"Concepto" => Afip::CONCEPTOS[concepto],
|
126
|
-
"DocTipo" => Afip::DOCUMENTOS[documento],
|
127
|
-
"DocNro" => doc_num,
|
128
|
-
"CbteFch" => fecha_emision.strftime('%Y%m%d'),
|
129
|
-
"ImpTotConc" => no_gravado,
|
130
|
-
"ImpNeto" => net.to_f,
|
131
|
-
"MonId" => Afip::MONEDAS[moneda][:codigo],
|
132
|
-
"MonCotiz" => exchange_rate,
|
133
|
-
"ImpOpEx" => exento,
|
134
|
-
"ImpTotal" => (Afip.own_iva_cond == :responsable_monotributo ? net : total).to_f.round(2),
|
135
|
-
"CbteDesde" => next_bill_number,
|
136
|
-
"CbteHasta" => next_bill_number
|
137
|
-
}
|
138
|
-
}
|
139
|
-
}
|
140
|
-
}
|
141
|
-
|
142
|
-
detail = fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
|
143
|
-
|
144
|
-
if (Afip.own_iva_cond == :responsable_inscripto)
|
145
|
-
detail["ImpIVA"] = iva_sum
|
146
|
-
detail["Iva"] = array_ivas unless iva_sum == 0
|
147
|
-
end
|
148
|
-
|
149
|
-
if !tributos.empty?
|
150
|
-
detail["ImpTrib"] = otros_imp
|
151
|
-
detail["Tributos"] = array_tributos
|
152
|
-
end
|
153
|
-
|
154
|
-
if !opcionales.empty?
|
155
|
-
detail["Opcionales"] = array_opcionales
|
156
|
-
end
|
157
|
-
|
158
|
-
unless concepto == "Productos" # En "Productos" ("01"), si se mandan estos parámetros la afip rechaza.
|
159
|
-
detail.merge!({"FchServDesde" => fch_serv_desde.strftime("%Y%m%d"),
|
160
|
-
"FchServHasta" => fch_serv_hasta.strftime("%Y%m%d"),
|
161
|
-
"FchVtoPago" => due_date.strftime("%Y%m%d")})
|
162
|
-
end
|
163
|
-
|
164
|
-
if ["201", "202", "203", "206", "207", "208", "211", "212", "213"].include?(cbte_type)
|
165
|
-
detail.merge!({"FchVtoPago" => due_date.strftime("%Y%m%d")})
|
166
|
-
end
|
167
|
-
|
168
|
-
body.merge!(fecaereq)
|
169
|
-
pp body
|
170
|
-
end
|
171
|
-
|
172
|
-
def self.header(cant_reg, cbte_type, sale_point)
|
173
|
-
{"CantReg" => cant_reg.to_s, "CbteTipo" => cbte_type, "PtoVta" => sale_point}
|
174
|
-
end
|
175
|
-
|
176
|
-
def exchange_rate
|
177
|
-
return 1 if moneda == :peso
|
178
|
-
response = client.call :fe_param_get_cotizacion do
|
179
|
-
message = body.merge!({"MonId" => Afip::MONEDAS[moneda][:codigo]})
|
180
|
-
end
|
181
|
-
response.to_hash[:fe_param_get_cotizacion_response][:fe_param_get_cotizacion_result][:result_get][:mon_cotiz].to_f
|
182
|
-
end
|
183
|
-
|
184
|
-
def iva_sum
|
185
|
-
iva_sum = 0.0
|
186
|
-
self.ivas.each{ |i|
|
187
|
-
iva_sum += i[2]
|
188
|
-
}
|
189
|
-
return iva_sum.round(2)
|
190
|
-
end
|
191
|
-
|
192
|
-
def next_bill_number
|
193
|
-
var = {"Auth" => Afip.auth_hash, "PtoVta" => sale_point, "CbteTipo" => cbte_type}
|
194
|
-
resp = client.call :fe_comp_ultimo_autorizado do
|
195
|
-
message(var)
|
196
|
-
end
|
197
|
-
|
198
|
-
resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1
|
199
|
-
end
|
200
|
-
|
201
|
-
def setup_response(response)
|
202
|
-
# TODO: turn this into an all-purpose Response class
|
203
|
-
pp response
|
204
|
-
result = response[:fecae_solicitar_response][:fecae_solicitar_result]
|
205
|
-
|
206
|
-
if not result[:fe_det_resp] or not result[:fe_cab_resp] then
|
207
|
-
# Si no obtuvo respuesta ni cabecera ni detalle, evito hacer '[]' sobre algo indefinido.
|
208
|
-
# Ejemplo: Error con el token-sign de WSAA
|
209
|
-
keys, values = {
|
210
|
-
:errores => result[:errors],
|
211
|
-
:header_result => {:resultado => "X" },
|
212
|
-
:observaciones => nil
|
213
|
-
}.to_a.transpose
|
214
|
-
@response = (defined?(Struct::ResponseMal) ? Struct::ResponseMal : Struct.new("ResponseMal", *keys)).new(*values)
|
215
|
-
return
|
216
|
-
end
|
217
|
-
|
218
|
-
response_header = result[:fe_cab_resp]
|
219
|
-
response_detail = result[:fe_det_resp][:fecae_det_response]
|
220
|
-
|
221
|
-
request_header = body["FeCAEReq"]["FeCabReq"].underscore_keys.symbolize_keys
|
222
|
-
request_detail = body["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"].underscore_keys.symbolize_keys
|
223
|
-
|
224
|
-
# Esto no funciona desde que se soportan múltiples alícuotas de iva simultáneas
|
225
|
-
# FIX ? TO-DO
|
226
|
-
# iva = request_detail.delete(:iva)["AlicIva"].underscore_keys.symbolize_keys
|
227
|
-
# request_detail.merge!(iva)
|
228
|
-
|
229
|
-
if result[:errors] then
|
230
|
-
response_detail.merge!( result[:errors] )
|
231
|
-
end
|
232
|
-
|
233
|
-
response_hash = {
|
234
|
-
:header_result => response_header.delete(:resultado),
|
235
|
-
:authorized_on => response_header.delete(:fch_proceso),
|
236
|
-
:detail_result => response_detail.delete(:resultado),
|
237
|
-
:cae_due_date => response_detail.delete(:cae_fch_vto),
|
238
|
-
:cae => response_detail.delete(:cae),
|
239
|
-
:iva_id => request_detail.delete(:id),
|
240
|
-
:iva_importe => request_detail.delete(:importe),
|
241
|
-
:moneda => request_detail.delete(:mon_id),
|
242
|
-
:cotizacion => request_detail.delete(:mon_cotiz),
|
243
|
-
:iva_base_imp => request_detail.delete(:base_imp),
|
244
|
-
:doc_num => request_detail.delete(:doc_nro),
|
245
|
-
:observaciones => response_detail.delete(:observaciones),
|
246
|
-
:ivas => response_detail.delete(:iva),
|
247
|
-
:tributos => response_detail.delete(:tributos),
|
248
|
-
:errores => response_detail.delete(:err)
|
249
|
-
}.merge!(request_header).merge!(request_detail)
|
250
|
-
|
251
|
-
keys, values = response_hash.to_a.transpose
|
252
|
-
pp @response = Struct.new("Response", *keys).new(*values)
|
253
|
-
end
|
254
|
-
|
255
|
-
def authorized?
|
256
|
-
!response.nil? && response.header_result == "A" && response.detail_result == "A"
|
257
|
-
end
|
258
|
-
|
259
|
-
def self.comp_consultar(cbte_tipo, cbte_nro, pto_venta)
|
260
|
-
client = set_client
|
261
|
-
|
262
|
-
body = {
|
263
|
-
"Auth" => Afip.auth_hash,
|
264
|
-
"FeCompConsReq" => {
|
265
|
-
"CbteTipo" => cbte_tipo,
|
266
|
-
"CbteNro" => cbte_nro,
|
267
|
-
"PtoVta" => pto_venta
|
268
|
-
}
|
269
|
-
}
|
270
|
-
response = client.call(:fe_comp_consultar, message: body)
|
271
|
-
return response
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|