afip_bill 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3c0e0285a52b25705a1a077b4313b0fc52b81c05
4
+ data.tar.gz: 94b7a2088d435d37811e90af9739e4e3377c1c69
5
+ SHA512:
6
+ metadata.gz: d85b4cc8da53850529036974cb6671cb946681b0328aa98046ef2ebb5f416f7a8323290dfe66edb7a444f750829fba15ccf75c7349c51661ac0e9f28541dacb2
7
+ data.tar.gz: 1545203c8f83ca7e465e395ca0f886b88179db3b05d39954fe31fd787c87893487d230c07c2d177031cfb93c666627ac0c9ede41e3444423167919affcc44f9e
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.8
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in afip_bill.gemspec
4
+ gemspec
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ afip_bill (0.1.0)
5
+ barby (~> 0.6.2)
6
+ pdfkit (~> 0.8.2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ barby (0.6.4)
12
+ byebug (9.0.6)
13
+ coderay (1.1.1)
14
+ diff-lcs (1.2.5)
15
+ method_source (0.8.2)
16
+ pdfkit (0.8.2)
17
+ pry (0.10.4)
18
+ coderay (~> 1.1.0)
19
+ method_source (~> 0.8.1)
20
+ slop (~> 3.4)
21
+ pry-byebug (3.4.0)
22
+ byebug (~> 9.0)
23
+ pry (~> 0.10)
24
+ rake (10.5.0)
25
+ rspec (3.5.0)
26
+ rspec-core (~> 3.5.0)
27
+ rspec-expectations (~> 3.5.0)
28
+ rspec-mocks (~> 3.5.0)
29
+ rspec-core (3.5.4)
30
+ rspec-support (~> 3.5.0)
31
+ rspec-expectations (3.5.0)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.5.0)
34
+ rspec-mocks (3.5.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.5.0)
37
+ rspec-support (3.5.0)
38
+ slop (3.6.0)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ afip_bill!
45
+ bundler (~> 1.13)
46
+ pry-byebug (~> 3.4.0)
47
+ rake (~> 10.0)
48
+ rspec (~> 3.0)
49
+
50
+ BUNDLED WITH
51
+ 1.13.6
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Luciano
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.
@@ -0,0 +1,40 @@
1
+ # Afip::Bill
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/bill`. 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_bill'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install afip_bill
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. Then, run `rake spec` to run the tests. 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_bill. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'afip_bill/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "afip_bill"
8
+ s.version = AfipBill::VERSION
9
+ s.authors = ["Luciano Becerra", "Mauro Otonelli"]
10
+ s.email = ["luciano@ombulabs.com"]
11
+
12
+ s.summary = "AFIP PDF bill"
13
+ s.description = "Allows you to generate an AFIP bill in PDF format"
14
+ s.homepage = "http://github.com/ombulabs/afip_bill"
15
+ s.license = "MIT"
16
+
17
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|s|features)/})
19
+ end
20
+ s.bindir = "exe"
21
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_development_dependency "bundler", "~> 1.13"
25
+ s.add_development_dependency "rake", "~> 10.0"
26
+ s.add_development_dependency "rspec", "~> 3.0"
27
+ s.add_development_dependency "pry-byebug", "~> 3.4.0"
28
+
29
+ s.add_dependency "barby", "~> 0.6.2"
30
+ s.add_dependency "pdfkit", "~> 0.8.2"
31
+ end
@@ -0,0 +1,20 @@
1
+ require 'afip_bill/check_digit'
2
+ require 'afip_bill/generator'
3
+ require 'afip_bill/line_item'
4
+ require 'afip_bill/user'
5
+ require 'afip_bill/version'
6
+
7
+ module AfipBill
8
+ def self.configuration
9
+ @configuration ||= {
10
+ business_name: nil,
11
+ business_address: nil,
12
+ business_start_date: nil,
13
+ business_cuit: nil,
14
+ city: nil,
15
+ ingresos_brutos: nil,
16
+ iva: nil,
17
+ sale_point: nil,
18
+ }
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ # Calculates checksum for AFIP bills (dígito verificador)
2
+ #
3
+ # @param [String]
4
+ # @return [Integer]
5
+
6
+ module AfipBill
7
+ class CheckDigit
8
+ attr_reader :code_array
9
+
10
+ def initialize(code)
11
+ @code_array = code.to_s.chars
12
+ end
13
+
14
+ def calculate
15
+ step1 = even_index_numbers * 3
16
+ step2 = step1 + odd_index_numbers
17
+ step2_last = step2.to_s[-1].to_i
18
+
19
+ step2_last.zero? ? 0 : 10 - step2_last
20
+ end
21
+
22
+ private
23
+
24
+ def numbers_with_index(parity)
25
+ numbers = code_array.select.each_with_index { |_, i| i.send(parity) }
26
+ numbers.map(&:to_i).inject { |x, sum| x + sum }
27
+ end
28
+
29
+ def even_index_numbers
30
+ numbers_with_index(:even?)
31
+ end
32
+
33
+ def odd_index_numbers
34
+ numbers_with_index(:odd?)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,71 @@
1
+ require "json"
2
+ require "date"
3
+ require "afip_bill/check_digit"
4
+ require "barby/barcode/code_25_interleaved"
5
+ require "barby/outputter/html_outputter"
6
+ require "pdfkit"
7
+
8
+ module AfipBill
9
+ class Generator
10
+ attr_reader :afip_bill, :bill_type, :user, :line_items
11
+
12
+ HEADER_PATH = File.dirname(__FILE__) + '/views/shared/_factura_header.html.erb'.freeze
13
+ FOOTER_PATH = File.dirname(__FILE__) + '/views/shared/_factura_footer.html.erb'.freeze
14
+ BRAVO_CBTE_TIPO = { "01" => "Factura A", "06" => "Factura B" }.freeze
15
+ IVA = 21.freeze
16
+
17
+ def initialize(bill, user, line_items = [])
18
+ @afip_bill = JSON.parse(bill)
19
+ @user = user
20
+ @bill_type = type_a_or_b_bill
21
+ @line_items = line_items
22
+ @template_header = ERB.new(File.read(HEADER_PATH)).result(binding)
23
+ @template_footer = ERB.new(File.read(FOOTER_PATH)).result(binding)
24
+ end
25
+
26
+ def type_a_or_b_bill
27
+ BRAVO_CBTE_TIPO[afip_bill["cbte_tipo"]][-1].downcase
28
+ end
29
+
30
+ def barcode
31
+ @barcode ||= Barby::Code25Interleaved.new(code_numbers)
32
+ end
33
+
34
+ def generate_pdf_file
35
+ tempfile = Tempfile.new("afip_bill.pdf")
36
+
37
+ PDFKit.new(template).to_file(tempfile.path)
38
+ end
39
+
40
+ def generate_pdf_string
41
+ PDFKit.new(template).to_pdf
42
+ end
43
+
44
+ private
45
+
46
+ def bill_path
47
+ File.dirname(__FILE__) + "/views/bills/factura_#{bill_type}.html.erb"
48
+ end
49
+
50
+ def code_numbers
51
+ code = code_hash.values.join("")
52
+ last_digit = CheckDigit.new(code).calculate
53
+ result = "#{code}#{last_digit}"
54
+ result.size.odd? ? "0" + result : result
55
+ end
56
+
57
+ def code_hash
58
+ {
59
+ cuit: afip_bill["doc_num"].tr("-", "").strip,
60
+ cbte_tipo: afip_bill["cbte_tipo"],
61
+ pto_venta: AfipBill.configuration[:sale_point],
62
+ cae: afip_bill["cae"],
63
+ vto_cae: afip_bill["fch_vto_pago"]
64
+ }
65
+ end
66
+
67
+ def template
68
+ ERB.new(File.read(bill_path)).result(binding)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ module AfipBill
2
+ class LineItem
3
+ attr_reader :name, :quantity, :imp_unitario
4
+ IVA = 21.freeze
5
+
6
+ def initialize(name, quantity, imp_unitario)
7
+ @name = name
8
+ @quantity = quantity
9
+ @imp_unitario = imp_unitario
10
+ end
11
+
12
+ def imp_total_unitario
13
+ quantity * imp_unitario
14
+ end
15
+
16
+ def imp_iva
17
+ imp_total_unitario * IVA / 100
18
+ end
19
+
20
+ def imp_total_unitario_con_iva
21
+ imp_total_unitario + imp_iva
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module AfipBill
2
+ class User
3
+ attr_accessor :company_name, :owner_name, :address, :tax_category
4
+
5
+ def initialize(company_name, owner_name, address, tax_category)
6
+ @company_name = company_name
7
+ @owner_name = owner_name
8
+ @address = address
9
+ @tax_category = tax_category
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module AfipBill
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,64 @@
1
+ <%= @template_header %>
2
+
3
+ <div style="left:484.19px;top:259.63px" class="cls_011"><span class="cls_011">Alicuota</span></div>
4
+ <div style="left:19.00px;top:263.04px" class="cls_010"><span class="cls_010">Código</span></div>
5
+ <div style="left:59.00px;top:263.04px" class="cls_010"><span class="cls_010">Producto / Servicio</span></div>
6
+ <div style="left:239.39px;top:263.04px" class="cls_010"><span class="cls_010">Cantidad</span></div>
7
+ <div style="left:285.77px;top:263.66px" class="cls_011"><span class="cls_011">U. medida</span></div>
8
+ <div style="left:335.05px;top:263.66px" class="cls_011"><span class="cls_011">Precio Unit.</span></div>
9
+ <div style="left:388.47px;top:263.66px" class="cls_011"><span class="cls_011">% Bonif</span></div>
10
+ <div style="left:434.50px;top:263.66px" class="cls_011"><span class="cls_011">Subtotal</span></div>
11
+ <div style="left:524.27px;top:263.66px" class="cls_011"><span class="cls_011">Subtotal c/IVA</span></div>
12
+ <div style="left:492.17px;top:267.68px" class="cls_011"><span class="cls_011">IVA</span></div>
13
+
14
+ <% top = 283 %>
15
+ <% @line_items.each_with_index do |line_item, index| %>
16
+ <div style="left:18.00px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= (index + 1).to_s.rjust(3, "0") %></span></div>
17
+ <div style="left:57.00px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.name %></span></div>
18
+ <div style="left:265.43px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.quantity %> </span></div>
19
+ <div style="left:288.30px;top:<%= top %>px" class="cls_012"><span class="cls_012">unidades</span></div>
20
+ <div style="left:360.54px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.imp_unitario %></span></div>
21
+ <div style="left:398.43px;top:<%= top %>px" class="cls_008"><span class="cls_008">0,00</span></div>
22
+ <div style="left:454.54px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.imp_total_unitario %></span></div>
23
+ <div style="left:490.00px;top:<%= top %>px" class="cls_008"><span class="cls_008">21%</span></div>
24
+ <div style="left:554.54px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.imp_total_unitario_con_iva %></span></div>
25
+ <% top += 25 %>
26
+ <% end %>
27
+
28
+ <div style="left:17.00px;top:511.97px" class="cls_010"><span class="cls_010">Otros Tributos</span></div>
29
+ <div style="left:21.00px;top:524.54px" class="cls_008"><span class="cls_008">Descripción</span></div>
30
+ <div style="left:184.00px;top:524.54px" class="cls_008"><span class="cls_008">Detalle</span></div>
31
+ <div style="left:270.61px;top:525.16px" class="cls_012"><span class="cls_012">Alíc. %</span></div>
32
+ <div style="left:317.88px;top:524.54px" class="cls_008"><span class="cls_008">Importe</span></div>
33
+ <div style="left:18.00px;top:538.63px" class="cls_008"><span class="cls_008">Per./Ret. de Impuesto a las Ganancias</span></div>
34
+ <div style="left:331.43px;top:538.63px" class="cls_008"><span class="cls_008">0,00</span></div>
35
+ <div style="left:18.00px;top:556.63px" class="cls_008"><span class="cls_008">Per./Ret. de IVA</span></div>
36
+ <div style="left:331.43px;top:556.63px" class="cls_008"><span class="cls_008">0,00</span></div>
37
+ <div style="left:397.98px;top:551.41px" class="cls_002"><span class="cls_002">Importe Neto Gravado: $</span></div>
38
+ <div style="left:547.48px;top:551.41px" class="cls_002"><span class="cls_002"><%= afip_bill["imp_neto"] %></span></div>
39
+ <div style="left:18.00px;top:567.63px" class="cls_008"><span class="cls_008">Per./Ret. Ingresos Brutos</span></div>
40
+ <div style="left:331.43px;top:567.63px" class="cls_008"><span class="cls_008">0,00</span></div>
41
+ <div style="left:456.98px;top:564.41px" class="cls_002"><span class="cls_002">IVA 27%: $</span></div>
42
+ <div style="left:557.49px;top:564.41px" class="cls_002"><span class="cls_002">0,00</span></div>
43
+ <div style="left:18.00px;top:578.63px" class="cls_008"><span class="cls_008">Impuestos Internos</span></div>
44
+ <div style="left:331.43px;top:578.63px" class="cls_008"><span class="cls_008">0,00</span></div>
45
+ <div style="left:18.00px;top:589.63px" class="cls_008"><span class="cls_008">Impuestos Municipales</span></div>
46
+ <div style="left:331.43px;top:589.63px" class="cls_008"><span class="cls_008">0,00</span></div>
47
+ <div style="left:456.98px;top:577.41px" class="cls_002"><span class="cls_002">IVA 21%: $</span></div>
48
+ <div style="left:552.48px;top:577.41px" class="cls_002"><span class="cls_002"><%= afip_bill["imp_iva"] %></span></div>
49
+ <div style="left:186.97px;top:602.91px" class="cls_006"><span class="cls_006">Importe Otros Tributos: $</span></div>
50
+ <div style="left:329.49px;top:602.91px" class="cls_006"><span class="cls_006">0,00</span></div>
51
+ <div style="left:449.48px;top:590.41px" class="cls_002"><span class="cls_002">IVA 10.5%: $</span></div>
52
+ <div style="left:557.49px;top:590.41px" class="cls_002"><span class="cls_002">0,00</span></div>
53
+ <div style="left:461.99px;top:603.41px" class="cls_002"><span class="cls_002">IVA 5%: $</span></div>
54
+ <div style="left:557.49px;top:603.41px" class="cls_002"><span class="cls_002">0,00</span></div>
55
+ <div style="left:454.48px;top:616.41px" class="cls_002"><span class="cls_002">IVA 2.5%: $</span></div>
56
+ <div style="left:557.49px;top:616.41px" class="cls_002"><span class="cls_002">0,00</span></div>
57
+ <div style="left:461.99px;top:629.41px" class="cls_002"><span class="cls_002">IVA 0%: $</span></div>
58
+ <div style="left:557.49px;top:629.41px" class="cls_002"><span class="cls_002">0,00</span></div>
59
+ <div style="left:394.49px;top:642.41px" class="cls_002"><span class="cls_002">Importe Otros Tributos: $</span></div>
60
+ <div style="left:557.49px;top:642.41px" class="cls_002"><span class="cls_002">0,00</span></div>
61
+ <div style="left:427.99px;top:656.29px" class="cls_005"><span class="cls_005">Importe Total: $</span></div>
62
+ <div style="left:544.42px;top:656.29px" class="cls_005"><span class="cls_005"><%= afip_bill["imp_total"] %></span></div>
63
+
64
+ <%= @template_footer %>
@@ -0,0 +1,32 @@
1
+ <%= @template_header %>
2
+
3
+ <div style="left:19.00px;top:272.04px" class="cls_010"><span class="cls_010">Código</span></div>
4
+ <div style="left:60.00px;top:272.04px" class="cls_010"><span class="cls_010">Producto / Servicio</span></div>
5
+ <div style="left:211.39px;top:272.04px" class="cls_010"><span class="cls_010">Cantidad</span></div>
6
+ <div style="left:263.61px;top:272.04px" class="cls_010"><span class="cls_010">U. Medida</span></div>
7
+ <div style="left:324.05px;top:272.66px" class="cls_011"><span class="cls_011">Precio Unit.</span></div>
8
+ <div style="left:386.97px;top:272.66px" class="cls_011"><span class="cls_011">% Bonif</span></div>
9
+ <div style="left:434.42px;top:272.66px" class="cls_011"><span class="cls_011">Imp. Bonif.</span></div>
10
+ <div style="left:520.50px;top:272.66px" class="cls_011"><span class="cls_011">Subtotal</span></div>
11
+
12
+ <% top = 293 %>
13
+ <% @line_items.each_with_index do |line_item, index| %>
14
+ <div style="left:18.00px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= (index + 1).to_s.rjust(3, "0") %></span></div>
15
+ <div style="left:57.00px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.name %></span></div>
16
+ <div style="left:243.43px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.quantity %></span></div>
17
+ <div style="left:268.30px;top:<%= top %>px" class="cls_012"><span class="cls_012">unidades</span></div>
18
+ <div style="left:357.54px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.imp_unitario %></span></div>
19
+ <div style="left:392.22px;top:<%= top %>px" class="cls_008"><span class="cls_008">0,00</span></div>
20
+ <div style="left:471.43px;top:<%= top %>px" class="cls_008"><span class="cls_008">0,00</span></div>
21
+ <div style="left:554.54px;top:<%= top %>px" class="cls_008"><span class="cls_008"><%= line_item.imp_total_unitario %></span></div>
22
+ <% top += 25 %>
23
+ <% end %>
24
+
25
+ <div style="left:444.50px;top:616.59px" class="cls_002"><span class="cls_002">Subtotal: $</span></div>
26
+ <div style="left:545.48px;top:616.59px" class="cls_002"><span class="cls_002"><%= afip_bill["imp_total"] %></span></div>
27
+ <div style="left:382.49px;top:634.59px" class="cls_002"><span class="cls_002">Importe Otros Tributos: $</span></div>
28
+ <div style="left:555.49px;top:634.59px" class="cls_002"><span class="cls_002">0,00</span></div>
29
+ <div style="left:415.99px;top:652.54px" class="cls_005"><span class="cls_005">Importe Total: $</span></div>
30
+ <div style="left:542.42px;top:652.54px" class="cls_005"><span class="cls_005"><%= afip_bill["imp_total"] %></span></div>
31
+
32
+ <%= @template_footer %>
@@ -0,0 +1,16 @@
1
+ <div style="left:277.76px;top:683.29px" class="cls_005"><span class="cls_005">Pág. 1/1</span></div>
2
+ <div style="left:121.00px;top:687.59px" class="cls_013"><span class="cls_013">Comprobante Autorizado</span></div>
3
+ <div style="left:442.56px;top:688.54px" class="cls_005"><span class="cls_005">CAE N°:</span><span class="cls_007"> <%= afip_bill["cae"] %></span></div>
4
+ <div style="left:16.00px;top:705.73px" class="cls_014"><span class="cls_014">Esta Administración Federal no se responsabiliza por los datos ingresados en el detalle de la operación</span></div>
5
+ <div style="left:374.31px;top:702.54px" class="cls_005"><span class="cls_005">Fecha de Vto. de CAE:</span></div>
6
+ <div style="left:486.00px;top:702.54px" class="cls_007"><span class="cls_007"><%= Date.parse(afip_bill["fch_vto_pago"]).strftime("%d/%m/%Y") %></span></div>
7
+ <div style="left:100px;top:775px" class="cls_008"><span class="cls_008"><%= barcode.data %></span></div>
8
+
9
+ <div style="left:16px;top:725px;">
10
+ <%= barcode.to_html %>
11
+ </div>
12
+
13
+ </div>
14
+
15
+ </body>
16
+ </html>
@@ -0,0 +1,194 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
4
+ <style type="text/css">
5
+ html, body, div, span, applet, object, iframe,
6
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
7
+ a, abbr, acronym, address, big, cite, code,
8
+ del, dfn, em, img, ins, kbd, q, s, samp,
9
+ small, strike, strong, sub, sup, tt, var,
10
+ b, u, i, center,
11
+ dl, dt, dd, ol, ul, li,
12
+ fieldset, form, label, legend,
13
+ table, caption, tbody, tfoot, thead, tr, th, td,
14
+ article, aside, canvas, details, embed,
15
+ figure, figcaption, footer, header, hgroup,
16
+ menu, nav, output, ruby, section, summary,
17
+ time, mark, audio, video {
18
+ margin: 0;
19
+ padding: 0;
20
+ border: 0;
21
+ font-size: 100%;
22
+ font: inherit;
23
+ vertical-align: baseline;
24
+ }
25
+ /* HTML5 display-role reset for older browsers */
26
+ article, aside, details, figcaption, figure,
27
+ footer, header, hgroup, menu, nav, section {
28
+ display: block;
29
+ }
30
+ body {
31
+ line-height: 1;
32
+ }
33
+ ol, ul {
34
+ list-style: none;
35
+ }
36
+ blockquote, q {
37
+ quotes: none;
38
+ }
39
+ blockquote:before, blockquote:after,
40
+ q:before, q:after {
41
+ content: '';
42
+ content: none;
43
+ }
44
+ table {
45
+ border-collapse: collapse;
46
+ border-spacing: 0;
47
+ }
48
+ div {
49
+ position: absolute;
50
+ }
51
+
52
+ .main {
53
+ left: 50%;
54
+ margin-left: -297px;
55
+ top: 0px;
56
+ width: 595px;
57
+ height: 842px;
58
+ overflow: hidden;
59
+ border: 1px solid grey;
60
+ }
61
+
62
+ span.cls_003, div.cls_003, span.cls_009, div.cls_009, span.cls_004, div.cls_004, span.cls_010, div.cls_010,
63
+ span.cls_002, div.cls_002, span.cls_005, div.cls_005, span.cls_006, div.cls_006, span.cls_007, div.cls_007,
64
+ span.cls_008, div.cls_008, span.cls_011, div.cls_011, span.cls_012, div.cls_012, span.cls_013, div.cls_013,
65
+ span.cls_014, div.cls_014 {
66
+ font-family: Arial, serif;
67
+ text-decoration: none;
68
+ font-weight:bold;
69
+ font-style:normal;
70
+ color:rgb(0,0,0);
71
+ }
72
+ span.cls_013, div.cls_013,
73
+ span.cls_014, div.cls_014 {
74
+ font-style:italic;
75
+ }
76
+ span.cls_006, div.cls_006, span.cls_007, div.cls_007,
77
+ span.cls_008, div.cls_008, span.cls_012, div.cls_012 {
78
+ font-weight:normal;
79
+ }
80
+
81
+ span.cls_003,
82
+ div.cls_003 {
83
+ font-size: 14.1px;
84
+ }
85
+ span.cls_009,
86
+ div.cls_009 {
87
+ font-size: 24.1px;
88
+ }
89
+ span.cls_004,
90
+ div.cls_004 {
91
+ font-size: 18.1px;
92
+ }
93
+ span.cls_010,
94
+ div.cls_010 {
95
+ font-size: 8.1px;
96
+ }
97
+ span.cls_002,
98
+ div.cls_002 {
99
+ font-size: 9.1px;
100
+ }
101
+ span.cls_005,
102
+ div.cls_005 {
103
+ font-size: 10.1px;
104
+ }
105
+ span.cls_006,
106
+ div.cls_006 {
107
+ font-size: 9.1px;
108
+ }
109
+ span.cls_007,
110
+ div.cls_007 {
111
+ font-size: 10.1px;
112
+ }
113
+ span.cls_008,
114
+ div.cls_008 {
115
+ font-size: 8.1px;
116
+ }
117
+ span.cls_011,
118
+ div.cls_011 {
119
+ font-size: 7.0px;
120
+ }
121
+ span.cls_012,
122
+ div.cls_012 {
123
+ font-size: 7.0px;
124
+ }
125
+ span.cls_013,
126
+ div.cls_013 {
127
+ font-size: 9.1px;
128
+ }
129
+ span.cls_014,
130
+ div.cls_014 {
131
+ font-size: 6.0px;
132
+ }
133
+
134
+ table.barby-barcode {
135
+ border-spacing: 0;
136
+ height: 45px;
137
+ width: 369px;
138
+ }
139
+ tr.barby-row {}
140
+ td.barby-cell {
141
+ width: 1px;
142
+ height: 45px;
143
+ }
144
+ td.barby-cell.on {
145
+ background: #000;
146
+ }
147
+ .breaker {
148
+ page-break-before: always;
149
+ }
150
+ </style>
151
+ </head>
152
+
153
+ <body>
154
+
155
+ <div class="main">
156
+ <div style="left:0px;top:0px">
157
+ <img src='file://<%= Gem.loaded_specs['afip_bill'].full_gem_path + "/lib/afip_bill/assets/images/factura_#{bill_type}.jpg" %>' width="595px" height="842px">
158
+
159
+ </div>
160
+
161
+ <div style="left:263.78px;top:22.31px" class="cls_003"><span class="cls_003">ORIGINAL</span></div>
162
+ <div style="left:289.84px;top:43.90px" class="cls_009"><span class="cls_009"> <%= bill_type.upcase %> </span></div>
163
+ <div style="left:341.00px;top:53.33px" class="cls_004"><span class="cls_004">FACTURA</span></div>
164
+ <div style="left:70.00px;top:58.33px" class="cls_004"><span class="cls_004"> <%= AfipBill.configuration[:business_name] %></span></div>
165
+ <div style="left:282.94px;top:72.04px" class="cls_010"><span class="cls_010">COD. 01</span></div>
166
+ <div style="left:341.00px;top:84.24px" class="cls_002"><span class="cls_002">Punto de Venta:</span></div>
167
+ <div style="left:417.00px;top:84.04px" class="cls_005"><span class="cls_005"><%= AfipBill.configuration[:sale_point] %></span></div>
168
+ <div style="left:461.00px;top:84.24px" class="cls_002"><span class="cls_002">Comp. Nro:</span></div>
169
+ <div style="left:517.00px;top:84.04px" class="cls_005"><span class="cls_005"><%= afip_bill["cbte_hasta"].to_s.rjust(8, "0") %></span></div>
170
+ <div style="left:21.00px;top:101.59px" class="cls_002"><span class="cls_002">Razón Social:</span><span class="cls_006"> <%= AfipBill.configuration[:business_name] %></span></div>
171
+ <div style="left:341.00px;top:100.41px" class="cls_002"><span class="cls_002">Fecha de Emisión:</span></div>
172
+ <div style="left:428.00px;top:99.54px" class="cls_005"><span class="cls_005"><%= Date.parse(afip_bill["cbte_fch"]).strftime("%d/%m/%Y") %></span></div>
173
+ <div style="left:21.00px;top:125.59px" class="cls_002"><span class="cls_002">Domicilio Comercial:</span><span class="cls_006"> <%= AfipBill.configuration[:business_address] %> </span></div>
174
+ <div style="left:341.00px;top:123.41px" class="cls_002"><span class="cls_002">CUIT:</span></div>
175
+ <div style="left:373.00px;top:123.41px" class="cls_006"><span class="cls_006"><%= AfipBill.configuration[:business_cuit] %></span></div>
176
+ <div style="left:116.00px;top:135.94px" class="cls_006"><span class="cls_006"><%= AfipBill.configuration[:city] %></span></div>
177
+ <div style="left:341.00px;top:135.41px" class="cls_002"><span class="cls_002">Ingresos Brutos:</span></div>
178
+ <div style="left:419.00px;top:135.41px" class="cls_006"><span class="cls_006"><%= AfipBill.configuration[:ingresos_brutos] %></span></div>
179
+ <div style="left:341.00px;top:147.41px" class="cls_002"><span class="cls_002">Fecha de Inicio de Actividades:</span></div>
180
+ <div style="left:482.00px;top:147.41px" class="cls_006"><span class="cls_006"><%= AfipBill.configuration[:business_start_date] %></span></div>
181
+ <div style="left:21.00px;top:150.41px" class="cls_002"><span class="cls_002">Condición frente al IVA: <%= AfipBill.configuration[:iva] %></span></div>
182
+ <div style="left:21.00px;top:169.29px" class="cls_005"><span class="cls_005">Período Facturado Desde:</span></div>
183
+ <div style="left:159.00px;top:169.29px" class="cls_007"><span class="cls_007"><%= Date.parse(afip_bill["fch_serv_desde"]).strftime("%d/%m/%Y") %></span></div>
184
+ <div style="left:232.44px;top:169.29px" class="cls_005"><span class="cls_005">Hasta:</span><span class="cls_007"><%= Date.parse(afip_bill["fch_serv_hasta"]).strftime("%d/%m/%Y") %></span></div>
185
+ <div style="left:362.96px;top:169.29px" class="cls_005"><span class="cls_005">Fecha de Vto. para el pago:</span></div>
186
+ <div style="left:495.00px;top:169.29px" class="cls_007"><span class="cls_007"><%= Date.parse(afip_bill["fch_serv_hasta"]).strftime("%d/%m/%Y") %></span></div>
187
+ <div style="left:21.00px;top:189.59px" class="cls_002"><span class="cls_002">CUIT:</span></div>
188
+ <div style="left:52.00px;top:189.59px" class="cls_006"><span class="cls_006"><%= afip_bill["doc_num"] %></span></div>
189
+ <div style="left:205.97px;top:189.59px" class="cls_002"><span class="cls_002">Apellido y Nombre / Razón Social:</span></div>
190
+ <div style="left:354.00px;top:190.63px" class="cls_008"><span class="cls_008"><%= bill_type == "a" ? user.company_name : user.owner_name %></span></div>
191
+ <div style="left:21.00px;top:208.59px" class="cls_002"><span class="cls_002">Condición frente al IVA:</span></div>
192
+ <div style="left:131.00px;top:208.59px" class="cls_006"><span class="cls_006"><%= user.tax_category %></span></div>
193
+ <div style="left:262.48px;top:208.59px" class="cls_002"><span class="cls_002">Domicilio Comercial:</span><span class="cls_008"> <%= user.address %></span></div>
194
+ <div style="left:21.00px;top:229.91px" class="cls_002"><span class="cls_002">Condición de venta:</span><span class="cls_008"> Otra</span></div>
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require "afip_bill/check_digit"
3
+
4
+ describe AfipBill::CheckDigit do
5
+ subject { described_class }
6
+
7
+ describe "#generate" do
8
+ context "when the sum of code numbers is even" do
9
+ it "returns the expected checksum" do
10
+ code = "01234567890"
11
+
12
+ checksum = subject.new(code).calculate
13
+ expect(checksum).to eq 5
14
+ end
15
+
16
+ it "returns the expected checksum" do
17
+ code = "111111111112233334444444444444455555555"
18
+
19
+ checksum = subject.new(code).calculate
20
+
21
+ expect(checksum).to eq 3
22
+ end
23
+ end
24
+
25
+ context "when the sum of code numbers is odd" do
26
+ it "returns the expected checksum" do
27
+ code = "01234567890"
28
+
29
+ checksum = subject.new(code).calculate
30
+
31
+ expect(checksum).to eq 5
32
+ end
33
+
34
+ it "returns the expected checksum" do
35
+ code = "111111111112233334444444444444455555555"
36
+
37
+ checksum = subject.new(code).calculate
38
+
39
+ expect(checksum).to eq 3
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+ require "afip_bill/generator"
3
+ require "afip_bill/line_item"
4
+ require "afip_bill/user"
5
+
6
+ describe AfipBill::Generator do
7
+ subject { described_class }
8
+
9
+ let(:user) do
10
+ AfipBill::User.new("OmbuShop", "John Snow", "Winterfell 123",
11
+ "Consumidor Final")
12
+ end
13
+ let(:bill_path) { File.expand_path("../../support/#{type}.json", __FILE__) }
14
+ let(:bill) { File.read(bill_path) }
15
+ let(:item_1) { AfipBill::LineItem.new("Item 1", 1, 100) }
16
+ let(:item_2) { AfipBill::LineItem.new("Item 2", 1, 100) }
17
+
18
+ let(:pdf_file) { subject.new(bill, user, [item_1, item_2]).generate_pdf_file }
19
+ let(:pdf_string) { subject.new(bill, user, [item_1, item_2]).generate_pdf_string }
20
+
21
+ before do
22
+ AfipBill.configuration[:business_name] = "OmbuShop SRL"
23
+ AfipBill.configuration[:business_address] = "Av Juan B. Justo 1500"
24
+ AfipBill.configuration[:business_start_date] = "01/01/1900"
25
+ AfipBill.configuration[:business_cuit] = "30112233445"
26
+ AfipBill.configuration[:city] = "Ciudad de Buenos Aires"
27
+ AfipBill.configuration[:ingresos_brutos] = "901-111111-4"
28
+ AfipBill.configuration[:iva] = "IVA Responsable Inscripto"
29
+ AfipBill.configuration[:sale_point] = "005"
30
+ end
31
+
32
+ describe "generate_pdf" do
33
+ context "Bill type A" do
34
+ let(:type) { "type_a" }
35
+
36
+ it "has the 'magic number' that identify a PDF file" do
37
+ expect(pdf_file.readpartial(4)).to eq "%PDF"
38
+ expect(pdf_string[0..3]).to eq "%PDF"
39
+ end
40
+ end
41
+
42
+ context "Bill type B" do
43
+ let(:type) { "type_b" }
44
+
45
+ it "has the 'magic number' that identify a PDF file" do
46
+ expect(pdf_file.readpartial(4)).to eq "%PDF"
47
+ expect(pdf_string[0..3]).to eq "%PDF"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "pry"
3
+ require "afip_bill"
@@ -0,0 +1,30 @@
1
+ {
2
+ "header_result":"A",
3
+ "authorized_on":"20161012105510",
4
+ "detail_result":"A",
5
+ "cae_due_date":"20161022",
6
+ "cae":"66414616381416",
7
+ "iva_id":"03",
8
+ "iva_importe":0.0,
9
+ "moneda":"PES",
10
+ "cotizacion":1,
11
+ "iva_base_imp":300.0,
12
+ "doc_num":"27041233515",
13
+ "cant_reg":"1",
14
+ "cbte_tipo":"01",
15
+ "pto_vta":"0017",
16
+ "concepto":"02",
17
+ "doc_tipo":"80",
18
+ "cbte_fch":"20161012",
19
+ "imp_tot_conc":0.0,
20
+ "imp_op_ex":0.0,
21
+ "imp_trib":0.0,
22
+ "imp_neto":300.0,
23
+ "imp_iva":0.0,
24
+ "imp_total":300.0,
25
+ "cbte_hasta":1,
26
+ "cbte_desde":1,
27
+ "fch_serv_desde":"20161001",
28
+ "fch_serv_hasta":"20161031",
29
+ "fch_vto_pago":"20161012"
30
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "header_result":"A",
3
+ "authorized_on":"20161012105510",
4
+ "detail_result":"A",
5
+ "cae_due_date":"20161022",
6
+ "cae":"66414616381416",
7
+ "iva_id":"03",
8
+ "iva_importe":0.0,
9
+ "moneda":"PES",
10
+ "cotizacion":1,
11
+ "iva_base_imp":300.0,
12
+ "doc_num":"27041233515",
13
+ "cant_reg":"1",
14
+ "cbte_tipo":"06",
15
+ "pto_vta":"0017",
16
+ "concepto":"02",
17
+ "doc_tipo":"80",
18
+ "cbte_fch":"20161012",
19
+ "imp_tot_conc":0.0,
20
+ "imp_op_ex":0.0,
21
+ "imp_trib":0.0,
22
+ "imp_neto":300.0,
23
+ "imp_iva":0.0,
24
+ "imp_total":300.0,
25
+ "cbte_hasta":1,
26
+ "cbte_desde":1,
27
+ "fch_serv_desde":"20161001",
28
+ "fch_serv_hasta":"20161031",
29
+ "fch_vto_pago":"20161012"
30
+ }
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: afip_bill
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Luciano Becerra
8
+ - Mauro Otonelli
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-01-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.13'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.13'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry-byebug
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 3.4.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 3.4.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: barby
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 0.6.2
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 0.6.2
84
+ - !ruby/object:Gem::Dependency
85
+ name: pdfkit
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 0.8.2
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 0.8.2
98
+ description: Allows you to generate an AFIP bill in PDF format
99
+ email:
100
+ - luciano@ombulabs.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - Gemfile.lock
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - afip_bill.gemspec
114
+ - lib/afip_bill.rb
115
+ - lib/afip_bill/assets/images/factura_a.jpg
116
+ - lib/afip_bill/assets/images/factura_b.jpg
117
+ - lib/afip_bill/check_digit.rb
118
+ - lib/afip_bill/generator.rb
119
+ - lib/afip_bill/line_item.rb
120
+ - lib/afip_bill/user.rb
121
+ - lib/afip_bill/version.rb
122
+ - lib/afip_bill/views/bills/factura_a.html.erb
123
+ - lib/afip_bill/views/bills/factura_b.html.erb
124
+ - lib/afip_bill/views/shared/_factura_footer.html.erb
125
+ - lib/afip_bill/views/shared/_factura_header.html.erb
126
+ - spec/afip_bill/check_digit_spec.rb
127
+ - spec/afip_bill/generator_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/support/type_a.json
130
+ - spec/support/type_b.json
131
+ homepage: http://github.com/ombulabs/afip_bill
132
+ licenses:
133
+ - MIT
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 2.4.8
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: AFIP PDF bill
155
+ test_files: []
156
+ has_rdoc: