ruby_danfe 0.9.0 → 1.11.5

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.
Files changed (43) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/README.md +122 -0
  5. data/Rakefile +28 -0
  6. data/lib/ruby_danfe.rb +1 -325
  7. data/lib/ruby_danfe/cst.rb +28 -0
  8. data/lib/ruby_danfe/dacte_generator.rb +511 -0
  9. data/lib/ruby_danfe/danfe_generator.rb +287 -0
  10. data/lib/ruby_danfe/danfe_nfce_generator.rb +237 -0
  11. data/lib/ruby_danfe/descricao.rb +44 -0
  12. data/lib/ruby_danfe/document.rb +80 -0
  13. data/lib/ruby_danfe/helper.rb +68 -0
  14. data/lib/ruby_danfe/options.rb +24 -0
  15. data/lib/ruby_danfe/railtie.rb +20 -0
  16. data/lib/ruby_danfe/ruby_danfe.rb +49 -0
  17. data/lib/ruby_danfe/version.rb +3 -0
  18. data/lib/ruby_danfe/xml.rb +58 -0
  19. data/ruby_danfe.gemspec +26 -11
  20. data/spec/features/ruby_danfe_spec.rb +59 -0
  21. data/spec/fixtures/4_decimals_nfe_simples_nacional.xml +1 -0
  22. data/spec/fixtures/cte.xml +1 -0
  23. data/spec/fixtures/nfe_date_format_infoadprod_infoadfisco_issues.xml +42 -0
  24. data/spec/fixtures/nfe_simples_nacional.xml +1 -0
  25. data/spec/fixtures/nfe_with_fci.xml +1 -0
  26. data/spec/fixtures/nfe_with_ns.xml +4 -0
  27. data/spec/fixtures/nfe_without_ns.xml +2 -0
  28. data/spec/lib/cst_spec.rb +51 -0
  29. data/spec/lib/descricao_spec.rb +129 -0
  30. data/spec/lib/helper_spec.rb +88 -0
  31. data/spec/lib/options_spec.rb +13 -0
  32. data/spec/lib/ruby_danfe_spec.rb +43 -0
  33. data/spec/spec_helper.rb +21 -0
  34. data/spec/support/be_same_file_as.rb +9 -0
  35. data/test/danfe.xml +42 -0
  36. data/test/generate.rb +8 -0
  37. data/test/generate_nfce.rb +8 -0
  38. data/test/nfce.xml +147 -0
  39. data/test/nfe_simples_nacional.xml +1 -0
  40. data/test/nfe_with_fci.xml +1 -0
  41. data/test/nfe_with_ns.xml +4 -0
  42. data/test/nfe_without_ns.xml +2 -0
  43. metadata +167 -75
@@ -0,0 +1,44 @@
1
+ #encoding: utf-8
2
+
3
+ module RubyDanfe
4
+ class Descricao
5
+ LINEBREAK = "\n"
6
+
7
+ def self.generate(det)
8
+ descricao = "#{det.css('prod/xProd').text}"
9
+
10
+ if need_infAdProd(det)
11
+ descricao += LINEBREAK
12
+ descricao += det.css('infAdProd').text
13
+ end
14
+
15
+ if need_fci(det)
16
+ descricao += LINEBREAK
17
+ descricao += "FCI: #{det.css('prod/nFCI').text}"
18
+ end
19
+
20
+ if need_st(det)
21
+ descricao += LINEBREAK
22
+ descricao += "ST: MVA: #{det.css('ICMS/*/pMVAST').text}% "
23
+ descricao += "* Alíq: #{det.css('ICMS/*/pICMSST').text}% "
24
+ descricao += "* BC: #{det.css('ICMS/*/vBCST').text} "
25
+ descricao += "* Vlr: #{det.css('ICMS/*/vICMSST').text}"
26
+ end
27
+
28
+ descricao
29
+ end
30
+
31
+ private
32
+ def self.need_infAdProd(det)
33
+ !det.css('infAdProd').text.empty?
34
+ end
35
+
36
+ def self.need_fci(det)
37
+ !det.css('prod/nFCI').text.empty?
38
+ end
39
+
40
+ def self.need_st(det)
41
+ det.css('ICMS/*/vBCST').text.to_i > 0
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,80 @@
1
+ module RubyDanfe
2
+ class Document
3
+ def initialize
4
+ @document = Prawn::Document.new(
5
+ :page_size => 'A4',
6
+ :page_layout => :portrait,
7
+ :left_margin => 0,
8
+ :right_margin => 0,
9
+ :top_margin => 0,
10
+ :botton_margin => 0
11
+ )
12
+
13
+ @document.font "Times-Roman"
14
+ end
15
+
16
+ def method_missing(method_name, *args, &block)
17
+ @document.send(method_name, *args, &block)
18
+ end
19
+
20
+ def respond_to_missing?(method_name, include_private = false)
21
+ @document.respond_to?(method_name, include_private) || super
22
+ end
23
+
24
+ def ititle(h, w, x, y, title)
25
+ self.text_box title, :size => 10, :at => [x.cm, Helper.invert(y.cm) - 2], :width => w.cm, :height => h.cm, :style => :bold
26
+ end
27
+
28
+ def ibarcode(h, w, x, y, info)
29
+ Barby::Code128C.new(info).annotate_pdf(self, :x => x.cm, :y => Helper.invert(y.cm), :width => w.cm, :height => h.cm) if info != ''
30
+ end
31
+
32
+ def iqrcode(h, w, x, y, info)
33
+ Barby::QrCode.new(info, :level => :q).annotate_pdf(self, :x => x.cm, :y => Helper.invert(y.cm), :width => w.cm, :height => h.cm) if info != ''
34
+ end
35
+
36
+ def irectangle(h, w, x, y)
37
+ self.stroke_rectangle [x.cm, Helper.invert(y.cm)], w.cm, h.cm
38
+ end
39
+
40
+ def ibox(h, w, x, y, title = '', info = '', options = {})
41
+ box [x.cm, Helper.invert(y.cm)], w.cm, h.cm, title, info, options
42
+ end
43
+
44
+ def idate(h, w, x, y, title = '', info = '', options = {})
45
+ tt = info.gsub(/T.*/, '').split('-')
46
+ ibox h, w, x, y, title, "#{tt[2]}/#{tt[1]}/#{tt[0]}", options
47
+ end
48
+
49
+ def box(at, w, h, title = '', info = '', options = {})
50
+ options = {
51
+ :align => :left,
52
+ :size => 10,
53
+ :style => nil,
54
+ :valign => :top,
55
+ :border => 1
56
+ }.merge(options)
57
+ self.stroke_rectangle at, w, h if options[:border] == 1
58
+ self.text_box title, :size => 6, :style => :italic, :at => [at[0] + 2, at[1] - 2], :width => w - 4, :height => 8 if title != ''
59
+ self.text_box info, :size => options[:size], :at => [at[0] + 2, at[1] - (title != '' ? 14 : 4) ], :width => w - 4, :height => h - (title != '' ? 14 : 4), :align => options[:align], :style => options[:style], :valign => options[:valign]
60
+ end
61
+
62
+ def inumeric(h, w, x, y, title = '', info = '', options = {})
63
+ numeric [x.cm, Helper.invert(y.cm)], w.cm, h.cm, title, info, options
64
+ end
65
+
66
+ def numeric(at, w, h, title = '', info = '', options = {})
67
+ options = {:decimals => 2}.merge(options)
68
+ info = Helper.numerify(info, options[:decimals]) if info != ''
69
+ box at, w, h, title, info, options.merge({:align => :right})
70
+ end
71
+
72
+ def itable(h, w, x, y, data, options = {}, &block)
73
+ self.bounding_box [x.cm, Helper.invert(y.cm)], :width => w.cm, :height => h.cm do
74
+ self.table data, options do |table|
75
+ yield(table)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,68 @@
1
+ module RubyDanfe
2
+ class Helper
3
+ def self.numerify(number, decimals = 2)
4
+ number = number.tr("\n","").delete(" ")
5
+ return "" if !number || number == ""
6
+ int, frac = ("%.#{decimals}f" % number).split(".")
7
+ int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1\.")
8
+ int + "," + frac
9
+ rescue
10
+ number
11
+ end
12
+
13
+ def self.invert(y)
14
+ 28.7.cm - y
15
+ end
16
+
17
+ def self.format_quantity(qty)
18
+ return Helper.numerify(qty, RubyDanfe.options.quantity_decimals) if RubyDanfe.options.numerify_prod_qcom
19
+ qty.gsub!(",", ".")
20
+ qty[qty.rindex('.')] = ',' if qty.rindex('.')
21
+ qty
22
+ end
23
+
24
+ def self.format_datetime(string)
25
+ formated_datetime = ""
26
+
27
+ if not string.empty?
28
+ date = DateTime.strptime(string, "%Y-%m-%dT%H:%M:%S")
29
+ formated_datetime = date.strftime("%d/%m/%Y %H:%M:%S")
30
+ end
31
+
32
+ formated_datetime
33
+ end
34
+
35
+ def self.format_date(string)
36
+ formated_date = ""
37
+
38
+ if not string.empty?
39
+ date = Date.strptime(string, "%Y-%m-%d")
40
+ formated_date = date.strftime("%d/%m/%Y")
41
+ end
42
+
43
+ formated_date
44
+ end
45
+
46
+ def self.format_time(string)
47
+ formated_time = ""
48
+
49
+ if not string.empty?
50
+ time = Time.strptime(string, "%H:%M:%S")
51
+ formated_time = time.strftime("%H:%M:%S")
52
+ end
53
+
54
+ formated_time
55
+ end
56
+
57
+ def self.extract_time_from_date(string)
58
+ formated_time = ""
59
+
60
+ if not string.empty?
61
+ date = DateTime.strptime(string, "%Y-%m-%dT%H:%M:%S")
62
+ formated_time = date.strftime("%H:%M:%S")
63
+ end
64
+
65
+ formated_time
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ module RubyDanfe
2
+ class Options < OpenStruct
3
+
4
+ DEFAULTOPTIONS = {
5
+ quantity_decimals: 2,
6
+ numerify_prod_qcom: false
7
+ }
8
+
9
+ def initialize(new_options={})
10
+ options = DEFAULTOPTIONS.merge(config_yaml_load)
11
+ super options.merge(new_options)
12
+ end
13
+
14
+ private
15
+ def file
16
+ File.exists?("config/ruby_danfe.yml") ? File.open("config/ruby_danfe.yml").read : ""
17
+ end
18
+
19
+ def config_yaml_load
20
+ @file_read = YAML.load(file)
21
+ @file_read ? (@file_read["ruby_danfe"]||{})["options"] : {}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ require "prawn"
2
+ require "prawn/measurement_extensions"
3
+ require "barby"
4
+ require "barby/barcode/code_128"
5
+ require "barby/outputter/prawn_outputter"
6
+ require "nokogiri"
7
+ require 'ostruct'
8
+ require 'yaml'
9
+
10
+ require_relative '../ruby_danfe.rb'
11
+ require_relative 'options.rb'
12
+ require_relative 'helper.rb'
13
+ require_relative 'cst.rb'
14
+ require_relative 'document.rb'
15
+ require_relative 'version.rb'
16
+ require_relative 'dacte_generator.rb'
17
+ require_relative 'danfe_generator.rb'
18
+ require_relative 'xml.rb'
19
+ require_relative 'descricao.rb'
20
+ require_relative 'ruby_danfe.rb'
@@ -0,0 +1,49 @@
1
+ # encoding:utf-8
2
+ module RubyDanfe
3
+ def self.generate(pdf_filename, xml_filename, type = :danfe, new_options = {})
4
+ self.options = new_options if !new_options.empty?
5
+
6
+ xml_string = File.new(xml_filename)
7
+ render_file(pdf_filename, xml_string, type)
8
+ end
9
+
10
+ def self.render(xml_string, type = :danfe, new_options = {})
11
+ self.options = new_options if !new_options.empty?
12
+
13
+ pdf = generatePDF(xml_string, type)
14
+ pdf.render
15
+ end
16
+
17
+ def self.render_file(pdf_filename, xml_string, type = :danfe, new_options = {})
18
+ self.options = new_options if !new_options.empty?
19
+
20
+ pdf = generatePDF(xml_string, type)
21
+ pdf.render_file pdf_filename
22
+ end
23
+
24
+ def self.options
25
+ @options ||= RubyDanfe::Options.new
26
+ end
27
+
28
+ def self.options=(new_options = {})
29
+ @options = RubyDanfe::Options.new(new_options)
30
+ end
31
+
32
+ private
33
+ def self.generatePDF(xml_string, type = :danfe, new_options = {})
34
+ self.options = new_options if !new_options.empty?
35
+
36
+ xml = XML.new(xml_string)
37
+
38
+ if type == :danfe
39
+ generator = DanfeGenerator.new(xml)
40
+ elsif type == :danfe_nfce
41
+ generator = DanfeNfceGenerator.new(xml)
42
+ else
43
+ generator = DacteGenerator.new(xml)
44
+ end
45
+
46
+ pdf = generator.generatePDF
47
+ pdf
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module RubyDanfe
2
+ VERSION = "1.11.5"
3
+ end
@@ -0,0 +1,58 @@
1
+ module RubyDanfe
2
+ class XML
3
+ def css(xpath)
4
+ @xml.css(xpath)
5
+ end
6
+
7
+ def xpath(regex)
8
+ doc = Nokogiri::HTML(@xml.to_s)
9
+ return doc.xpath(regex)
10
+ end
11
+
12
+ def regex_string(search_string, regex)
13
+ doc = Nokogiri::HTML(search_string)
14
+ return doc.xpath(regex)
15
+ end
16
+
17
+ def initialize(xml)
18
+ @xml = Nokogiri::XML(xml)
19
+ end
20
+
21
+ def [](xpath)
22
+ node = @xml.css(xpath)
23
+ return node ? node.text : ""
24
+ end
25
+
26
+ def render
27
+ if @xml.at_css('infNFe/ide')
28
+ RubyDanfe.render @xml.to_s, :danfe
29
+ else
30
+ RubyDanfe.render @xml.to_s, :dacte
31
+ end
32
+ end
33
+
34
+ def collect(ns, tag, &block)
35
+ result = []
36
+ # Tenta primeiro com uso de namespace
37
+ begin
38
+ @xml.xpath("//#{ns}:#{tag}").each do |det|
39
+ result << yield(det)
40
+ end
41
+ rescue
42
+ # Caso dê erro, tenta sem
43
+ @xml.xpath("//#{tag}").each do |det|
44
+ result << yield(det)
45
+ end
46
+ end
47
+ result
48
+ end
49
+
50
+ def attrib(node, attrib)
51
+ begin
52
+ return @xml.css(node).attr(attrib).text
53
+ rescue
54
+ ""
55
+ end
56
+ end
57
+ end
58
+ end
data/ruby_danfe.gemspec CHANGED
@@ -1,12 +1,27 @@
1
- Gem::Specification.new do |s|
2
- s.name = 'ruby_danfe'
3
- s.version = '0.9.0'
4
- s.summary = "DANFE generator for Brazilian NFE."
5
- s.authors = ["Eduardo Rebouças"]
6
- s.email = 'eduardo.reboucas@gmail.com'
7
- s.files = ["ruby_danfe.gemspec", "lib/ruby_danfe.rb"]
8
- s.add_dependency('nokogiri')
9
- s.add_dependency('prawn')
10
- s.add_dependency('barby')
11
- s.homepage = 'https://github.com/taxweb/ruby_danfe'
1
+ # coding: utf-8
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ruby_danfe/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ruby_danfe"
7
+ spec.version = RubyDanfe::VERSION
8
+ spec.summary = "DANFE and DACTE pdf generator for Brazilian invoices and transportation docs."
9
+ spec.author = "Eduardo Reboucas"
10
+ spec.email = "eduardo.reboucas@gmail.com"
11
+ spec.homepage = "http://github.com/taxweb/ruby_danfe"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files`.split("\n")
15
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "nokogiri"
20
+ spec.add_dependency "prawn", '~> 1.0.0'
21
+ spec.add_dependency "barby"
22
+ spec.add_dependency "rake"
23
+
24
+ spec.add_development_dependency "pry"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "simplecov"
12
27
  end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ describe "RubyDanfe generated pdf files" do
4
+ let(:base_dir) { "./spec/fixtures/"}
5
+ let(:output_pdf) { "#{base_dir}output.pdf" }
6
+
7
+ before {
8
+ File.delete(output_pdf) if File.exist?(output_pdf)
9
+ RubyDanfe.options = {"quantity_decimals" => 2}
10
+ }
11
+
12
+ it "renders a basic NF-e with namespace" do
13
+ expect(File.exist?(output_pdf)).to be_false
14
+
15
+ RubyDanfe.generate(output_pdf, "#{base_dir}nfe_with_ns.xml")
16
+
17
+ expect("#{base_dir}nfe_with_ns.xml.fixture.pdf").to be_same_file_as(output_pdf)
18
+ end
19
+
20
+ it "renders another basic NF-e without namespace" do
21
+ expect(File.exist?(output_pdf)).to be_false
22
+
23
+ RubyDanfe.generate(output_pdf, "#{base_dir}nfe_without_ns.xml")
24
+
25
+ expect("#{base_dir}nfe_without_ns.xml.fixture.pdf").to be_same_file_as(output_pdf)
26
+ end
27
+
28
+ it "renders a NF-e having FCI in its items" do
29
+ expect(File.exist?(output_pdf)).to be_false
30
+
31
+ RubyDanfe.generate(output_pdf, "#{base_dir}nfe_with_fci.xml")
32
+
33
+ expect("#{base_dir}nfe_with_fci.xml.fixture.pdf").to be_same_file_as(output_pdf)
34
+ end
35
+
36
+ it "renders a NF-e of Simples Nacional using CSOSN" do
37
+ expect(File.exist?(output_pdf)).to be_false
38
+
39
+ RubyDanfe.generate(output_pdf, "#{base_dir}nfe_simples_nacional.xml")
40
+
41
+ expect("#{base_dir}nfe_simples_nacional.xml.fixture.pdf").to be_same_file_as(output_pdf)
42
+ end
43
+
44
+ it "renders a basic CT-e" do
45
+ expect(File.exist?(output_pdf)).to be_false
46
+
47
+ RubyDanfe.generate(output_pdf, "#{base_dir}cte.xml", :dacte)
48
+
49
+ expect("#{base_dir}cte.xml.fixture.pdf").to be_same_file_as(output_pdf)
50
+ end
51
+
52
+ it "renders quantity field with 4 decimals" do
53
+ expect(File.exist?(output_pdf)).to be_false
54
+
55
+ RubyDanfe.generate(output_pdf, "#{base_dir}4_decimals_nfe_simples_nacional.xml", :danfe, {"quantity_decimals" => 4})
56
+
57
+ expect("#{base_dir}4_decimals_nfe_simples_nacional.xml.fixture.pdf").to be_same_file_as(output_pdf)
58
+ end
59
+ end