ruby_danfe 0.9.0 → 1.11.5

Sign up to get free protection for your applications and to get access to all the features.
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