Afip 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- metadata +4 -28
- data/.DS_Store +0 -0
- data/.gitignore +0 -8
- data/Afip.gemspec +0 -39
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -48
- 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/.DS_Store +0 -0
- data/lib/Afip.rb +0 -59
- data/lib/Afip/.DS_Store +0 -0
- data/lib/Afip/auth_data.rb +0 -71
- data/lib/Afip/authorizer.rb +0 -10
- data/lib/Afip/bill.rb +0 -193
- data/lib/Afip/constants.rb +0 -80
- 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 -183
- 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: 423fa3ea9b764029af3e4c7910def5a4f5037125ddc481dd55fa3daa41ee66ae
|
4
|
+
data.tar.gz: 04d2a8faaed0d63660f65f0c04ea19b8294cc1c1a21c0e2b9d8a2599b68798d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4533a6716f515077477e543a39079a6a84768553b9e65d52f4c49d5c88d6075abe84f9caebf790e3c7ec6c25c4281301f697d8009fa4d76c4901e0a8c3ddd62
|
7
|
+
data.tar.gz: 560c2dea89ed78fc6d98a9e55dd1c60ed44e3c5c4e3df27c32b52c5bccc9261049f15a1eee28952867624c5ce6e599aabf972a4463f84e0190a858961daeea0e
|
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: 0.8.
|
4
|
+
version: 0.8.2
|
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: 2018-12-
|
11
|
+
date: 2018-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: savon
|
@@ -72,31 +72,7 @@ email:
|
|
72
72
|
executables: []
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
75
|
-
files:
|
76
|
-
- ".DS_Store"
|
77
|
-
- ".gitignore"
|
78
|
-
- Afip.gemspec
|
79
|
-
- Gemfile
|
80
|
-
- Gemfile.lock
|
81
|
-
- LICENSE.txt
|
82
|
-
- README.md
|
83
|
-
- Rakefile
|
84
|
-
- bin/console
|
85
|
-
- bin/setup
|
86
|
-
- lib/.DS_Store
|
87
|
-
- lib/Afip.rb
|
88
|
-
- lib/Afip/.DS_Store
|
89
|
-
- lib/Afip/auth_data.rb
|
90
|
-
- lib/Afip/authorizer.rb
|
91
|
-
- lib/Afip/bill.rb
|
92
|
-
- lib/Afip/constants.rb
|
93
|
-
- lib/Afip/core_ext/float.rb
|
94
|
-
- lib/Afip/core_ext/hash.rb
|
95
|
-
- lib/Afip/core_ext/string.rb
|
96
|
-
- lib/Afip/ctg.rb
|
97
|
-
- lib/Afip/padron.rb
|
98
|
-
- lib/Afip/version.rb
|
99
|
-
- lib/Afip/wsaa.rb
|
75
|
+
files: []
|
100
76
|
homepage: https://www.desideral.com
|
101
77
|
licenses:
|
102
78
|
- MIT
|
@@ -118,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
94
|
version: '0'
|
119
95
|
requirements: []
|
120
96
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.7.
|
97
|
+
rubygems_version: 2.7.8
|
122
98
|
signing_key:
|
123
99
|
specification_version: 4
|
124
100
|
summary: Comunicacion con AFIP
|
data/.DS_Store
DELETED
Binary file
|
data/.gitignore
DELETED
data/Afip.gemspec
DELETED
@@ -1,39 +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 = "https://www.desideral.com"
|
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
|
-
spec.bindir = "exe"
|
32
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
|
-
spec.require_paths = ["lib"]
|
34
|
-
|
35
|
-
spec.add_dependency "savon"
|
36
|
-
spec.add_dependency "httpi"
|
37
|
-
spec.add_development_dependency "bundler", "~> 1.16"
|
38
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
39
|
-
end
|
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
Afip (0.8.0)
|
5
|
-
httpi
|
6
|
-
savon
|
7
|
-
|
8
|
-
GEM
|
9
|
-
remote: https://rubygems.org/
|
10
|
-
specs:
|
11
|
-
akami (1.3.1)
|
12
|
-
gyoku (>= 0.4.0)
|
13
|
-
nokogiri
|
14
|
-
builder (3.2.3)
|
15
|
-
gyoku (1.3.1)
|
16
|
-
builder (>= 2.1.2)
|
17
|
-
httpi (2.4.4)
|
18
|
-
rack
|
19
|
-
socksify
|
20
|
-
mini_portile2 (2.3.0)
|
21
|
-
nokogiri (1.8.5)
|
22
|
-
mini_portile2 (~> 2.3.0)
|
23
|
-
nori (2.6.0)
|
24
|
-
rack (2.0.6)
|
25
|
-
rake (10.5.0)
|
26
|
-
savon (2.12.0)
|
27
|
-
akami (~> 1.2)
|
28
|
-
builder (>= 2.1.2)
|
29
|
-
gyoku (~> 1.2)
|
30
|
-
httpi (~> 2.3)
|
31
|
-
nokogiri (>= 1.8.1)
|
32
|
-
nori (~> 2.4)
|
33
|
-
wasabi (~> 3.4)
|
34
|
-
socksify (1.7.1)
|
35
|
-
wasabi (3.5.0)
|
36
|
-
httpi (~> 2.0)
|
37
|
-
nokogiri (>= 1.4.2)
|
38
|
-
|
39
|
-
PLATFORMS
|
40
|
-
ruby
|
41
|
-
|
42
|
-
DEPENDENCIES
|
43
|
-
Afip!
|
44
|
-
bundler (~> 1.16)
|
45
|
-
rake (~> 10.0)
|
46
|
-
|
47
|
-
BUNDLED WITH
|
48
|
-
1.17.1
|
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/.DS_Store
DELETED
Binary file
|
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/.DS_Store
DELETED
Binary file
|
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
|
-
@todays_data_file = "/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,193 +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,
|
5
|
-
:due_date, :fch_serv_desde, :fch_serv_hasta, :fch_emision,
|
6
|
-
:ivas, :sale_point
|
7
|
-
|
8
|
-
def initialize(attrs={})
|
9
|
-
Bill.set_client
|
10
|
-
@sale_point = attrs[:sale_point]
|
11
|
-
@body = { "Auth" => Afip.auth_hash }
|
12
|
-
@net = attrs[:net] || 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
|
-
@cbte_type = Afip::BILL_TYPE[Afip.own_iva_cond][iva_cond]
|
20
|
-
@total = net.zero? ? 0 : net + iva_sum
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.get_ptos_vta
|
24
|
-
client = set_client
|
25
|
-
body = { "Auth" => Afip.auth_hash }
|
26
|
-
response = client.call(:fe_param_get_ptos_venta, message: body)
|
27
|
-
if response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:errors].nil?
|
28
|
-
if response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta].is_a?(Hash)
|
29
|
-
response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta][:nro]
|
30
|
-
else
|
31
|
-
response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta].map{|r| r[:nro]}
|
32
|
-
end
|
33
|
-
else
|
34
|
-
[]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.set_client
|
39
|
-
Afip::AuthData.fetch
|
40
|
-
@client = Savon.client(
|
41
|
-
wsdl: Afip.service_url,
|
42
|
-
namespaces: { "xmlns" => "http://ar.gov.afip.dif.FEV1/" },
|
43
|
-
log_level: :debug,
|
44
|
-
ssl_cert_key_file: Afip.pkey,
|
45
|
-
ssl_cert_file: Afip.cert,
|
46
|
-
ssl_verify_mode: :none,
|
47
|
-
read_timeout: 90,
|
48
|
-
open_timeout: 90,
|
49
|
-
headers: { "Accept-Encoding" => "gzip, deflate", "Connection" => "Keep-Alive" }
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
def authorize
|
54
|
-
body = setup_bill
|
55
|
-
response = client.call(:fecae_solicitar, message: body)
|
56
|
-
setup_response(response.to_hash)
|
57
|
-
authorized?
|
58
|
-
end
|
59
|
-
|
60
|
-
def setup_bill
|
61
|
-
array_ivas = Array.new
|
62
|
-
ivas.each{ |i|
|
63
|
-
array_ivas << {
|
64
|
-
"Id" => i[0],
|
65
|
-
"BaseImp" => i[1].round(2),
|
66
|
-
"Importe" => i[2].round(2) }
|
67
|
-
}
|
68
|
-
|
69
|
-
fecaereq = {
|
70
|
-
"FeCAEReq" => {
|
71
|
-
"FeCabReq" => Afip::Bill.header(cbte_type, sale_point),
|
72
|
-
"FeDetReq" => {
|
73
|
-
"FECAEDetRequest" => {
|
74
|
-
"Concepto" => Afip::CONCEPTOS[concepto],
|
75
|
-
"DocTipo" => Afip::DOCUMENTOS[documento],
|
76
|
-
"DocNro" => doc_num,
|
77
|
-
"CbteFch" => fecha_emision.strftime('%Y%m%d'),
|
78
|
-
"ImpTotConc" => 0.00,
|
79
|
-
"ImpNeto" => net.to_f,
|
80
|
-
"MonId" => Afip::MONEDAS[moneda][:codigo],
|
81
|
-
"MonCotiz" => exchange_rate,
|
82
|
-
"ImpOpEx" => 0.00,
|
83
|
-
"ImpTrib" => 0.00,
|
84
|
-
"ImpTotal" => (Afip.own_iva_cond == :responsable_monotributo ? net : total).to_f.round(2),
|
85
|
-
"CbteDesde" => next_bill_number,
|
86
|
-
"CbteHasta" => next_bill_number
|
87
|
-
}
|
88
|
-
}
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
detail = fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"]
|
93
|
-
|
94
|
-
if (Afip.own_iva_cond == :responsable_inscripto)
|
95
|
-
detail["ImpIVA"] = iva_sum
|
96
|
-
detail["Iva"] = { "AlicIva" => array_ivas }
|
97
|
-
end
|
98
|
-
|
99
|
-
unless concepto == "Productos" # En "Productos" ("01"), si se mandan estos parámetros la afip rechaza.
|
100
|
-
detail.merge!({"FchServDesde" => fch_serv_desde,
|
101
|
-
"FchServHasta" => fch_serv_hasta,
|
102
|
-
"FchVtoPago" => due_date})
|
103
|
-
end
|
104
|
-
|
105
|
-
body.merge!(fecaereq)
|
106
|
-
end
|
107
|
-
|
108
|
-
def self.header(cbte_type, sale_point)
|
109
|
-
{"CantReg" => "1", "CbteTipo" => cbte_type, "PtoVta" => sale_point}
|
110
|
-
end
|
111
|
-
|
112
|
-
def exchange_rate
|
113
|
-
return 1 if moneda == :peso
|
114
|
-
response = client.call :fe_param_get_cotizacion do
|
115
|
-
message = body.merge!({"MonId" => Afip::MONEDAS[moneda][:codigo]})
|
116
|
-
end
|
117
|
-
response.to_hash[:fe_param_get_cotizacion_response][:fe_param_get_cotizacion_result][:result_get][:mon_cotiz].to_f
|
118
|
-
end
|
119
|
-
|
120
|
-
def iva_sum
|
121
|
-
iva_sum = 0.0
|
122
|
-
self.ivas.each{ |i|
|
123
|
-
iva_sum += i[2]
|
124
|
-
}
|
125
|
-
return iva_sum.round(2)
|
126
|
-
end
|
127
|
-
|
128
|
-
def next_bill_number
|
129
|
-
var = {"Auth" => Afip.auth_hash, "PtoVta" => sale_point, "CbteTipo" => cbte_type}
|
130
|
-
resp = client.call :fe_comp_ultimo_autorizado do
|
131
|
-
message(var)
|
132
|
-
end
|
133
|
-
|
134
|
-
resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1
|
135
|
-
end
|
136
|
-
|
137
|
-
def setup_response(response)
|
138
|
-
# TODO: turn this into an all-purpose Response class
|
139
|
-
|
140
|
-
result = response[:fecae_solicitar_response][:fecae_solicitar_result]
|
141
|
-
|
142
|
-
if not result[:fe_det_resp] or not result[:fe_cab_resp] then
|
143
|
-
# Si no obtuvo respuesta ni cabecera ni detalle, evito hacer '[]' sobre algo indefinido.
|
144
|
-
# Ejemplo: Error con el token-sign de WSAA
|
145
|
-
keys, values = {
|
146
|
-
:errores => result[:errors],
|
147
|
-
:header_result => {:resultado => "X" },
|
148
|
-
:observaciones => nil
|
149
|
-
}.to_a.transpose
|
150
|
-
@response = (defined?(Struct::ResponseMal) ? Struct::ResponseMal : Struct.new("ResponseMal", *keys)).new(*values)
|
151
|
-
return
|
152
|
-
end
|
153
|
-
|
154
|
-
response_header = result[:fe_cab_resp]
|
155
|
-
response_detail = result[:fe_det_resp][:fecae_det_response]
|
156
|
-
|
157
|
-
request_header = body["FeCAEReq"]["FeCabReq"].underscore_keys.symbolize_keys
|
158
|
-
request_detail = body["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"].underscore_keys.symbolize_keys
|
159
|
-
|
160
|
-
# Esto no funciona desde que se soportan múltiples alícuotas de iva simultáneas
|
161
|
-
# FIX ? TO-DO
|
162
|
-
# iva = request_detail.delete(:iva)["AlicIva"].underscore_keys.symbolize_keys
|
163
|
-
# request_detail.merge!(iva)
|
164
|
-
|
165
|
-
if result[:errors] then
|
166
|
-
response_detail.merge!( result[:errors] )
|
167
|
-
end
|
168
|
-
|
169
|
-
response_hash = {
|
170
|
-
:header_result => response_header.delete(:resultado),
|
171
|
-
:authorized_on => response_header.delete(:fch_proceso),
|
172
|
-
:detail_result => response_detail.delete(:resultado),
|
173
|
-
:cae_due_date => response_detail.delete(:cae_fch_vto),
|
174
|
-
:cae => response_detail.delete(:cae),
|
175
|
-
:iva_id => request_detail.delete(:id),
|
176
|
-
:iva_importe => request_detail.delete(:importe),
|
177
|
-
:moneda => request_detail.delete(:mon_id),
|
178
|
-
:cotizacion => request_detail.delete(:mon_cotiz),
|
179
|
-
:iva_base_imp => request_detail.delete(:base_imp),
|
180
|
-
:doc_num => request_detail.delete(:doc_nro),
|
181
|
-
:observaciones => response_detail.delete(:observaciones),
|
182
|
-
:errores => response_detail.delete(:err)
|
183
|
-
}.merge!(request_header).merge!(request_detail)
|
184
|
-
|
185
|
-
keys, values = response_hash.to_a.transpose
|
186
|
-
@response = (defined?(Struct::Response) ? Struct::Response : Struct.new("Response", *keys)).new(*values)
|
187
|
-
end
|
188
|
-
|
189
|
-
def authorized?
|
190
|
-
!response.nil? && response.header_result == "A" && response.detail_result == "A"
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
data/lib/Afip/constants.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
module Afip
|
2
|
-
CBTE_TIPO = {
|
3
|
-
"01"=>"Factura A",
|
4
|
-
"02"=>"Nota de Débito A",
|
5
|
-
"03"=>"Nota de Crédito A",
|
6
|
-
"06"=>"Factura B",
|
7
|
-
"07"=>"Nota de Debito B",
|
8
|
-
"08"=>"Nota de Credito B",
|
9
|
-
"11"=>"Factura C",
|
10
|
-
"12"=>"Nota de Debito C",
|
11
|
-
"13"=>"Nota de Credito C"
|
12
|
-
}
|
13
|
-
|
14
|
-
CONCEPTOS = {"Productos"=>"01", "Servicios"=>"02", "Productos y Servicios"=>"03"}
|
15
|
-
|
16
|
-
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"}
|
17
|
-
|
18
|
-
MONEDAS = {
|
19
|
-
:peso => {:codigo => "PES", :nombre =>"Pesos Argentinos"},
|
20
|
-
:dolar => {:codigo => "DOL", :nombre =>"Dolar Estadounidense"},
|
21
|
-
:real => {:codigo => "012", :nombre =>"Real"},
|
22
|
-
:euro => {:codigo => "060", :nombre =>"Euro"},
|
23
|
-
:oro => {:codigo => "049", :nombre =>"Gramos de Oro Fino"}
|
24
|
-
}
|
25
|
-
|
26
|
-
ALIC_IVA = [["03", 0], ["04", 0.105], ["05", 0.21], ["06", 0.27]]
|
27
|
-
|
28
|
-
IVA_COND = ["Responsable Inscripto", "Responsable Monotributo"]
|
29
|
-
|
30
|
-
BILL_TYPE = {
|
31
|
-
:responsable_inscripto => {
|
32
|
-
:responsable_inscripto => "01",
|
33
|
-
:consumidor_final => "06",
|
34
|
-
:exento => "06",
|
35
|
-
:responsable_monotributo => "06",
|
36
|
-
:nota_credito_a => "03",
|
37
|
-
:nota_credito_b => "08",
|
38
|
-
:nota_debito_a => "02",
|
39
|
-
:nota_debito_b => "07"
|
40
|
-
},
|
41
|
-
:responsable_monotributo => {
|
42
|
-
:responsable_inscripto => "11",
|
43
|
-
:consumidor_final => "11",
|
44
|
-
:exento => "11",
|
45
|
-
:responsable_monotributo => "11",
|
46
|
-
:nota_credito_c => "13",
|
47
|
-
:nota_debito_c => "12"
|
48
|
-
}
|
49
|
-
}
|
50
|
-
|
51
|
-
AVAILABLE_TYPES = {
|
52
|
-
:responsable_inscripto => {
|
53
|
-
:responsable_inscripto => ["01", "02", "03"],
|
54
|
-
:consumidor_final => ["06", "07", "08"],
|
55
|
-
:exento => ["06", "07", "08"],
|
56
|
-
:responsable_monotributo => ["06", "07", "08"],
|
57
|
-
},
|
58
|
-
:responsable_monotributo => {
|
59
|
-
:responsable_inscripto => ["11", "12", "13"],
|
60
|
-
:consumidor_final => ["11", "12", "13"],
|
61
|
-
:exento => ["11", "12", "13"],
|
62
|
-
:responsable_monotributo => ["11", "12", "13"]
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
URLS =
|
67
|
-
{
|
68
|
-
:test => {
|
69
|
-
:wsaa => 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',
|
70
|
-
:padron => "https://awshomo.afip.gov.ar/sr-padron/webservices/personaServiceA5?WSDL",
|
71
|
-
:wsfe => 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL'
|
72
|
-
},
|
73
|
-
:production => {
|
74
|
-
:wsaa => 'https://wsaa.afip.gov.ar/ws/services/LoginCms',
|
75
|
-
:padron => "https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA5?WSDL",
|
76
|
-
:wsfe => 'https://servicios1.afip.gov.ar/wsfev1/service.asmx'
|
77
|
-
}
|
78
|
-
}
|
79
|
-
|
80
|
-
end
|
data/lib/Afip/core_ext/float.rb
DELETED
@@ -1,8 +0,0 @@
|
|
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
|
data/lib/Afip/core_ext/hash.rb
DELETED
@@ -1,23 +0,0 @@
|
|
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
|
data/lib/Afip/core_ext/string.rb
DELETED
@@ -1,12 +0,0 @@
|
|
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
|
data/lib/Afip/ctg.rb
DELETED
@@ -1,234 +0,0 @@
|
|
1
|
-
module Afip
|
2
|
-
class CTG
|
3
|
-
attr_reader :client, :base_imp, :total
|
4
|
-
attr_accessor :ctg_num, :cp_num, :cod_especie, :cuit_canjeador,:rccc, :cuit_destino, :cuit_destinatario, :localidad_origen, :localidad_destino,
|
5
|
-
:cosecha, :peso, :cuit_transportista, :horas, :patente, :km, :cuit_corredor, :remitente_com, :body
|
6
|
-
|
7
|
-
def initialize(attrs = {})
|
8
|
-
Afip::AuthData.fetch("wsctg")
|
9
|
-
@client = Savon.client(
|
10
|
-
ssl_cert_key_file: Afip.pkey,
|
11
|
-
ssl_cert_file: Afip.cert,
|
12
|
-
env_namespace: :soapenv,
|
13
|
-
namespace_identifier: :ctg,
|
14
|
-
log: true,
|
15
|
-
logger: Rails.logger,
|
16
|
-
log_level: :debug,
|
17
|
-
pretty_print_xml: true,
|
18
|
-
encoding: 'UTF-8',
|
19
|
-
ssl_version: :TLSv1,
|
20
|
-
wsdl: Afip.service_url
|
21
|
-
)
|
22
|
-
|
23
|
-
@ctg_num = attrs[:ctg_num]
|
24
|
-
@cp_num = attrs[:cp_num]
|
25
|
-
@cod_especie = attrs[:cod_especie]
|
26
|
-
@cuit_canjeador = attrs[:cuit_canjeador]
|
27
|
-
@rccc = attrs[:rccc]
|
28
|
-
@cuit_destino = attrs[:cuit_destino]
|
29
|
-
@cuit_destinatario = attrs[:cuit_destinatario]
|
30
|
-
@localidad_origen = attrs[:localidad_origen]
|
31
|
-
@localidad_destino = attrs[:localidad_destino]
|
32
|
-
@cosecha = attrs[:cosecha]
|
33
|
-
@peso = attrs[:peso]
|
34
|
-
@cuit_transportista = attrs[:cuit_transportista]
|
35
|
-
@horas = attrs[:horas]
|
36
|
-
@patente = attrs[:patente]
|
37
|
-
@km = attrs[:km]
|
38
|
-
@cuit_corredor = attrs[:cuit_corredor]
|
39
|
-
@remitente_com = attrs[:remitente_com]
|
40
|
-
@cuit_chofer = attrs[:cuit_chofer]
|
41
|
-
@cant_kilos_carta_porte = attrs[:cant_kilos_carta_porte]
|
42
|
-
@establecimiento = attrs[:establecimiento]
|
43
|
-
@cuit_solicitante = attrs[:cuit_solicitante]
|
44
|
-
@fecha_desde = attrs[:fecha_desde]
|
45
|
-
@fecha_hasta = attrs[:fecha_hasta]
|
46
|
-
|
47
|
-
@body = {"request" =>{"auth" => Afip.auth_hash("wsctg")}}
|
48
|
-
end
|
49
|
-
|
50
|
-
def solicitar_ctg_inicial
|
51
|
-
pp body = setup_ctg
|
52
|
-
|
53
|
-
pp response = client.call(:solicitar_ctg_inicial,message: body)
|
54
|
-
|
55
|
-
setup_response(response.to_hash)
|
56
|
-
|
57
|
-
self.authorized?
|
58
|
-
end
|
59
|
-
|
60
|
-
def setup_ctg
|
61
|
-
|
62
|
-
datos = {
|
63
|
-
"datosSolicitarCTGInicial" =>{
|
64
|
-
"cartaPorte" => @cp_num, #long
|
65
|
-
"codigoEspecie" => @cod_especie, #int
|
66
|
-
#"cuitCanjeador" => @cuit_canjeador.to_i, #long
|
67
|
-
#"remitenteComercialComoCanjeador" => @rccc,
|
68
|
-
"cuitDestino" => @cuit_destino, #long
|
69
|
-
"cuitDestinatario" => @cuit_destinatario, #long
|
70
|
-
"codigoLocalidadOrigen" => @localidad_origen, #int
|
71
|
-
"codigoLocalidadDestino" => @localidad_destino, #int
|
72
|
-
"codigoCosecha" => @cosecha, #string
|
73
|
-
"pesoNeto" => @peso.to_i, #long
|
74
|
-
#"cuitTransportista" => @cuit_transportista.to_i, #long
|
75
|
-
#"cantHoras" => @horas, #int
|
76
|
-
#"patente" => @patente, #string
|
77
|
-
"kmARecorrer" => @km, #unsignedint
|
78
|
-
#"cuitCorredor" => @cuit_corredor.to_i, #long
|
79
|
-
#"remitenteComercialcomoProductor" => @remitente_com
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
@body["request"].merge!(datos)
|
84
|
-
return @body
|
85
|
-
end
|
86
|
-
|
87
|
-
def authorized?
|
88
|
-
!response.nil?
|
89
|
-
end
|
90
|
-
|
91
|
-
def anular_ctg
|
92
|
-
request = {
|
93
|
-
"datosAnularCTG" =>{
|
94
|
-
"cartaPorte" => "@ctg_num",
|
95
|
-
"ctg" => "#{self.code}"
|
96
|
-
}
|
97
|
-
}
|
98
|
-
body["request"].merge(request)
|
99
|
-
|
100
|
-
response = client.call(:anular_ctg, message: body)
|
101
|
-
end
|
102
|
-
|
103
|
-
def cambiar_destino_detinatario_ctg_rechazado
|
104
|
-
request = {
|
105
|
-
"datosCambiarDestinoDestinatarioCTGRechazado" =>{
|
106
|
-
"cartaPorte" => "@ctg_num",
|
107
|
-
"ctg" => "#{code}",
|
108
|
-
"codigoLocalidadDestino" => @destino,
|
109
|
-
"codigoLocalidadDestinatario" => @destinatario,
|
110
|
-
"kmARecorrer" => @km
|
111
|
-
}
|
112
|
-
}
|
113
|
-
body["request"].merge(request)
|
114
|
-
|
115
|
-
response = client.call(:cambiar_destino_detinatario_ctg_rechazado, message: body)
|
116
|
-
end
|
117
|
-
|
118
|
-
def confirmar_arribo
|
119
|
-
request = {
|
120
|
-
"datosConfirmarArribo" =>{
|
121
|
-
"cartaPorte" => "@ctg_num",
|
122
|
-
"ctg" => "#{self.code}",
|
123
|
-
"cuitTransportista" => @cuit_transportista,
|
124
|
-
"cuitChofer" => @cuit_chofer,
|
125
|
-
"cantKilosCartaPorte" => @cant_kilos_carta_porte
|
126
|
-
}
|
127
|
-
}
|
128
|
-
body["request"].merge(request)
|
129
|
-
|
130
|
-
response = client.call(:confirmar_arribo, message: body)
|
131
|
-
end
|
132
|
-
|
133
|
-
def confirmar_definitivo
|
134
|
-
request = {
|
135
|
-
"datosConfirmarDefinitivo" =>{
|
136
|
-
"cartaPorte" => "@ctg_num",
|
137
|
-
"ctg" => "#{self.code}",
|
138
|
-
"establecimiento" => @establecimiento,
|
139
|
-
"codigoCosecha" => @cosecha,
|
140
|
-
"pesoNeto" => @peso
|
141
|
-
}
|
142
|
-
}
|
143
|
-
body["request"].merge(request)
|
144
|
-
|
145
|
-
response = client.call(:confirmar_definitivo, message: body)
|
146
|
-
end
|
147
|
-
|
148
|
-
def consultar_cosechas
|
149
|
-
response = client.call(:consultar_cosechas, message: body)
|
150
|
-
return response["arrayCosechas"]
|
151
|
-
end
|
152
|
-
|
153
|
-
def consultar_constancia_ctg_pdf
|
154
|
-
request = {
|
155
|
-
"ctg" => "#{code}"
|
156
|
-
}
|
157
|
-
|
158
|
-
body["request"].merge(request)
|
159
|
-
|
160
|
-
response = client.call(:consultar_constancia_ctg_pdf, message: body)
|
161
|
-
end
|
162
|
-
|
163
|
-
def consultar_ctg
|
164
|
-
request = {
|
165
|
-
"consultarCTGDatos" =>{
|
166
|
-
"cartaPorte" => "@ctg_num",
|
167
|
-
"ctg" => "#{self.code}",
|
168
|
-
"patente" => @patente,
|
169
|
-
"cuitSolicitante" => @cuit_solicitante,
|
170
|
-
"cuitDestino" => @destino,
|
171
|
-
"fechaEmisionDesde" => @fecha_desde,
|
172
|
-
"fechaEmisionHsta" => @fecha_hasta,
|
173
|
-
"cuitCorredor" => @cuit_corredor
|
174
|
-
}
|
175
|
-
}
|
176
|
-
body["request"].merge(request)
|
177
|
-
|
178
|
-
response = client.call(:consultar_ctg, message: body)
|
179
|
-
end
|
180
|
-
|
181
|
-
def consultar_ctg_rechazados
|
182
|
-
response = client.call(:consultar_ctg_rechazados, message: body)
|
183
|
-
return response.to_hash["response"]["arrayConsultarCTGRechazados"]
|
184
|
-
end
|
185
|
-
|
186
|
-
def consultar_detalle_ctg
|
187
|
-
request = {
|
188
|
-
"ctg" => "#{code}"
|
189
|
-
}
|
190
|
-
|
191
|
-
body["request"].merge(request)
|
192
|
-
|
193
|
-
response = client.call(:consultar_constancia_ctg_pdf, message: body)
|
194
|
-
return response.to_hash["response"]["consultarDetalleCTGDatos"]
|
195
|
-
end
|
196
|
-
|
197
|
-
def consultar_especies
|
198
|
-
response = client.call(:consultar_especies, message: body)
|
199
|
-
response.body[:consultar_especies_response][:response][:array_especies][:especie].map{|c| [c[:codigo],c[:descripcion]]}
|
200
|
-
end
|
201
|
-
|
202
|
-
def consultar_establecimientos
|
203
|
-
response = client.call(:consultar_establecimientos, message: body)
|
204
|
-
response.body[:consultar_especies_response][:response][:array_establecimientos][:establecimiento].map{|c| [c]}
|
205
|
-
end
|
206
|
-
|
207
|
-
def consultar_provincias
|
208
|
-
response = client.call(:consultar_provincias, message: body)
|
209
|
-
response.body[:consultar_provincias_response][:consultar_provincias_response][:array_provincias][:provincia].map{|c| [c[:codigo],c[:descripcion]]}
|
210
|
-
end
|
211
|
-
|
212
|
-
def consultar_cosechas
|
213
|
-
response = client.call(:consultar_cosechas, message: body)
|
214
|
-
response.body[:consultar_cosechas_response][:response][:array_cosechas][:cosecha].map{|c| [c[:codigo],c[:descripcion]]}
|
215
|
-
end
|
216
|
-
|
217
|
-
def consultar_localidades(city)
|
218
|
-
body["request"]["codigoProvincia"] = city
|
219
|
-
response = client.call(:consultar_localidades_por_provincia, message: body)
|
220
|
-
if response.body[:consultar_localidades_por_provincia_response][:response][:array_localidades][:localidad].class.name != "Array"
|
221
|
-
[response.body[:consultar_localidades_por_provincia_response][:response][:array_localidades][:localidad]].map{|c| [c[:codigo],c[:descripcion]]}
|
222
|
-
else
|
223
|
-
response.body[:consultar_localidades_por_provincia_response][:response][:array_localidades][:localidad].map{|c| [c[:codigo],c[:descripcion]]}
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
private
|
228
|
-
|
229
|
-
def setup_response(response)
|
230
|
-
# TODO: turn this into an all-purpose Response class
|
231
|
-
pp response
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
data/lib/Afip/padron.rb
DELETED
@@ -1,183 +0,0 @@
|
|
1
|
-
module Afip
|
2
|
-
class Padron
|
3
|
-
attr_reader :client, :body, :fault_code
|
4
|
-
attr_accessor :dni, :tipo, :data
|
5
|
-
def initialize(attrs = {})
|
6
|
-
Afip::AuthData.environment = Afip.environment || :production
|
7
|
-
url = Afip::AuthData.environment == :production ? "aws" : "awshomo"
|
8
|
-
Afip.service_url = "https://#{url}.afip.gov.ar/sr-padron/webservices/personaServiceA4?WSDL"
|
9
|
-
Afip.cuit ||= "20368642682"
|
10
|
-
Afip.cert ||= "#{Afip.root}/lib/Afip/certs/desideral_prod.crt"
|
11
|
-
Afip.pkey ||= "#{Afip.root}/lib/Afip/certs/desideral.key"
|
12
|
-
Afip::AuthData.fetch("ws_sr_padron_a4")
|
13
|
-
|
14
|
-
@client = Savon.client(
|
15
|
-
ssl_cert_key_file: Afip.pkey,
|
16
|
-
ssl_cert_file: Afip.cert,
|
17
|
-
env_namespace: :soapenv,
|
18
|
-
namespace_identifier: :a4,
|
19
|
-
encoding: 'UTF-8',
|
20
|
-
wsdl: Afip.service_url
|
21
|
-
)
|
22
|
-
|
23
|
-
@dni = attrs[:dni].rjust(8, "0")
|
24
|
-
@tipo = attrs[:tipo]
|
25
|
-
@cuit = get_cuit
|
26
|
-
end
|
27
|
-
|
28
|
-
def get_persona
|
29
|
-
body = setup_body
|
30
|
-
|
31
|
-
response = client.call(:get_persona,message: body)
|
32
|
-
rescue Savon::SOAPFault => error
|
33
|
-
if !error.blank?
|
34
|
-
@fault_code = error.to_hash[:fault][:faultstring]
|
35
|
-
else
|
36
|
-
@fault_code = nil
|
37
|
-
end
|
38
|
-
return response
|
39
|
-
end
|
40
|
-
|
41
|
-
def get_data
|
42
|
-
@data = get_persona
|
43
|
-
if fault_code.nil?
|
44
|
-
set_data
|
45
|
-
else
|
46
|
-
return nil
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
def set_data
|
52
|
-
pp data.body
|
53
|
-
if not data.body[:get_persona_response][:persona_return][:persona][:actividad].nil?
|
54
|
-
{
|
55
|
-
:last_name => data.body[:get_persona_response][:persona_return][:persona][:apellido],
|
56
|
-
:first_name => data.body[:get_persona_response][:persona_return][:persona][:nombre],
|
57
|
-
:cuit => data.body[:get_persona_response][:persona_return][:persona][:id_persona],
|
58
|
-
:cp => data.body[:get_persona_response][:persona_return][:persona][:domicilio].last[:cod_postal],
|
59
|
-
:address => data.body[:get_persona_response][:persona_return][:persona][:domicilio].last[:direccion],
|
60
|
-
:city_id => data.body[:get_persona_response][:persona_return][:persona][:domicilio].last[:id_provincia],
|
61
|
-
:city => PROVINCIAS[data.body[:get_persona_response][:persona_return][:persona][:domicilio].last[:id_provincia]],
|
62
|
-
:locality => data.body[:get_persona_response][:persona_return][:persona][:domicilio].last[:localidad],
|
63
|
-
:birthday => data.body[:get_persona_response][:persona_return][:persona][:fecha_nacimiento].to_date
|
64
|
-
}
|
65
|
-
else
|
66
|
-
{
|
67
|
-
:last_name => Padron.divide_name(data.body[:get_persona_response][:persona_return][:persona][:apellido])[0],
|
68
|
-
:first_name => Padron.divide_name(data.body[:get_persona_response][:persona_return][:persona][:apellido])[1],
|
69
|
-
:cuit => data.body[:get_persona_response][:persona_return][:persona][:id_persona],
|
70
|
-
:cp => data.body[:get_persona_response][:persona_return][:persona].try(:[], :domicilio).try(:[], :cod_postal),
|
71
|
-
:address => data.body[:get_persona_response][:persona_return][:persona].try(:[], :domicilio).try(:[], :direccion),
|
72
|
-
:city_id => data.body[:get_persona_response][:persona_return][:persona].try(:[], :domicilio).try(:[], :id_provincia),
|
73
|
-
:city => PROVINCIAS[data.body[:get_persona_response][:persona_return][:persona].try(:[], :domicilio).try(:[], :id_provincia)],
|
74
|
-
:locality => data.body[:get_persona_response][:persona_return][:persona].try(:[], :domicilio).try(:[], :localidad),
|
75
|
-
:birthday => data.body[:get_persona_response][:persona_return][:persona][:fecha_nacimiento].to_date
|
76
|
-
}
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.divide_name(full_name)
|
81
|
-
full_name = full_name.strip.split(/\s+/)
|
82
|
-
last_name = ''
|
83
|
-
last = (full_name.count / 2) - 1
|
84
|
-
(0..last).each do |i|
|
85
|
-
if i != last
|
86
|
-
last_name += full_name[i] + ' '
|
87
|
-
else
|
88
|
-
last_name += full_name[i]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
full_name = full_name - (last_name.strip.split(/\s+/))
|
92
|
-
first_name = full_name.join(", ").gsub(",","").split.map(&:capitalize).join(' ')
|
93
|
-
last_name = last_name.split.map(&:capitalize).join(' ')
|
94
|
-
return [last_name, first_name]
|
95
|
-
end
|
96
|
-
|
97
|
-
def get_cuit
|
98
|
-
if dni.length == 11
|
99
|
-
@cuit = @dni
|
100
|
-
else
|
101
|
-
@cuit = calculate_cuit
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def calculate_cuit
|
106
|
-
multiplicador = "2345672345"
|
107
|
-
|
108
|
-
case tipo
|
109
|
-
when "F"
|
110
|
-
xy = 27
|
111
|
-
xy_dni = "27#{dni}"
|
112
|
-
when "M"
|
113
|
-
xy = 20
|
114
|
-
xy_dni = "20#{dni}"
|
115
|
-
end
|
116
|
-
verificador = 0
|
117
|
-
(0..9).each do |i|
|
118
|
-
verificador += (xy_dni.reverse[i].to_i * multiplicador[i].to_i)
|
119
|
-
end
|
120
|
-
verificador
|
121
|
-
z = verificador - (verificador / 11 * 11)
|
122
|
-
|
123
|
-
case z
|
124
|
-
when 0
|
125
|
-
z = 0
|
126
|
-
when 1
|
127
|
-
if tipo == "M"
|
128
|
-
z = 9
|
129
|
-
xy = 23
|
130
|
-
elsif tipo == "F"
|
131
|
-
z = 4
|
132
|
-
xy = 23
|
133
|
-
else
|
134
|
-
z = 11 - z
|
135
|
-
end
|
136
|
-
else
|
137
|
-
z = 11 - z
|
138
|
-
end
|
139
|
-
|
140
|
-
return "#{xy}#{dni}#{z}"
|
141
|
-
end
|
142
|
-
|
143
|
-
def setup_body
|
144
|
-
body = {
|
145
|
-
'token' => Afip::TOKEN,
|
146
|
-
'sign' => Afip::SIGN,
|
147
|
-
'cuitRepresentada' => Afip.cuit,
|
148
|
-
'idPersona' => @cuit.to_s
|
149
|
-
}
|
150
|
-
end
|
151
|
-
|
152
|
-
PROVINCIAS = {
|
153
|
-
"0" => 'CIUDAD AUTONOMA BUENOS AIRES',
|
154
|
-
"1" => 'BUENOS AIRES',
|
155
|
-
"2" => 'CATAMARCA',
|
156
|
-
"3" => 'CORDOBA',
|
157
|
-
"4" => 'CORRIENTES',
|
158
|
-
"5" => 'ENTRE RIOS',
|
159
|
-
"6" => 'JUJUY',
|
160
|
-
"7" => 'MENDOZA',
|
161
|
-
"8" => 'LA RIOJA',
|
162
|
-
"9" => 'SALTA',
|
163
|
-
"10" => 'SAN JUAN',
|
164
|
-
"11" => 'SAN LUIS',
|
165
|
-
"12" => 'SANTA FE',
|
166
|
-
"13" => 'SANTIAGO DEL ESTERO',
|
167
|
-
"14" => 'TUCUMAN',
|
168
|
-
"16" => 'CHACO',
|
169
|
-
"17" => 'CHUBUT',
|
170
|
-
"18" => 'FORMOSA',
|
171
|
-
"19" => 'MISIONES',
|
172
|
-
"20" => 'NEUQUEN',
|
173
|
-
"21" => 'LA PAMPA',
|
174
|
-
"22" => 'RIO NEGRO',
|
175
|
-
"23" => 'SANTA CRUZ',
|
176
|
-
"24" => 'TIERRA DEL FUEGO'
|
177
|
-
}
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
data/lib/Afip/version.rb
DELETED
data/lib/Afip/wsaa.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
module Afip
|
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
|
-
# Main method for authentication and authorization.
|
7
|
-
# When successful, produces the yaml file with auth data.
|
8
|
-
#
|
9
|
-
def self.login(service = "wsfe")
|
10
|
-
tra = build_tra(service)
|
11
|
-
cms = build_cms(tra)
|
12
|
-
req = build_request(cms)
|
13
|
-
auth = call_wsaa(req)
|
14
|
-
|
15
|
-
write_yaml(auth)
|
16
|
-
end
|
17
|
-
|
18
|
-
protected
|
19
|
-
# Builds the xml for the 'Ticket de Requerimiento de Acceso'
|
20
|
-
# @return [String] containing the request body
|
21
|
-
#
|
22
|
-
def self.build_tra service
|
23
|
-
@now = (Time.now) - 120
|
24
|
-
@from = @now.strftime('%FT%T%:z')
|
25
|
-
@to = (@now + ((12*60*60))).strftime('%FT%T%:z')
|
26
|
-
@id = @now.strftime('%s')
|
27
|
-
tra = <<-EOF
|
28
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
29
|
-
<loginTicketRequest version="1.0">
|
30
|
-
<header>
|
31
|
-
<uniqueId>#{ @id }</uniqueId>
|
32
|
-
<generationTime>#{ @from }</generationTime>
|
33
|
-
<expirationTime>#{ @to }</expirationTime>
|
34
|
-
</header>
|
35
|
-
<service>#{service}</service>
|
36
|
-
</loginTicketRequest>
|
37
|
-
EOF
|
38
|
-
return tra
|
39
|
-
end
|
40
|
-
|
41
|
-
# Builds the CMS
|
42
|
-
# @return [String] cms
|
43
|
-
#
|
44
|
-
def self.build_cms(tra)
|
45
|
-
cms = `echo '#{ tra }' |
|
46
|
-
#{ Afip.openssl_bin } cms -sign -in /dev/stdin -signer #{ Afip.cert } -inkey #{ Afip.pkey } -nodetach \
|
47
|
-
-outform der |
|
48
|
-
#{ Afip.openssl_bin } base64 -e`
|
49
|
-
return cms
|
50
|
-
end
|
51
|
-
|
52
|
-
# Builds the CMS request to log in to the server
|
53
|
-
# @return [String] the cms body
|
54
|
-
#
|
55
|
-
def self.build_request(cms)
|
56
|
-
request = <<-XML
|
57
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
58
|
-
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://wsaa.view.sua.dvadac.desein.afip.gov">
|
59
|
-
<SOAP-ENV:Body>
|
60
|
-
<ns1:loginCms>
|
61
|
-
<ns1:in0>
|
62
|
-
#{ cms }
|
63
|
-
</ns1:in0>
|
64
|
-
</ns1:loginCms>
|
65
|
-
</SOAP-ENV:Body>
|
66
|
-
</SOAP-ENV:Envelope>
|
67
|
-
XML
|
68
|
-
return request
|
69
|
-
end
|
70
|
-
|
71
|
-
# Calls the WSAA with the request built by build_request
|
72
|
-
# @return [Array] with the token and signature
|
73
|
-
#
|
74
|
-
def self.call_wsaa(req)
|
75
|
-
response = `echo '#{ req }' | curl -k -s -H 'Content-Type: application/soap+xml; action=""' -d @- #{ Afip::AuthData.wsaa_url }`
|
76
|
-
pp response
|
77
|
-
response = CGI::unescapeHTML(response)
|
78
|
-
token = response.scan(/\<token\>(.+)\<\/token\>/).first.first
|
79
|
-
sign = response.scan(/\<sign\>(.+)\<\/sign\>/).first.first
|
80
|
-
return [token, sign]
|
81
|
-
end
|
82
|
-
|
83
|
-
# Writes the token and signature to a YAML file in the /tmp directory
|
84
|
-
#
|
85
|
-
def self.write_yaml(certs)
|
86
|
-
yml = <<-YML
|
87
|
-
token: #{certs[0]}
|
88
|
-
sign: #{certs[1]}
|
89
|
-
YML
|
90
|
-
`echo '#{ yml }' > /tmp/#{Afip::AuthData.environment.to_s}_Afip_#{ Afip.cuit }_#{ Time.new.strftime('%Y_%m_%d') }.yml`
|
91
|
-
end
|
92
|
-
|
93
|
-
end
|
94
|
-
end
|