nfse 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0356ae06d8a620fcfe5fcdd8f63f67759aa569c9fb5da003b06a790ba6622094
4
+ data.tar.gz: 56ed8bf906bc0ad0f036bdaec169eb78b35fffebf85a3a8bf9e7ac04594a19ea
5
+ SHA512:
6
+ metadata.gz: 1748e61542e41829bcaee9ac32750798db03ded918224aca35ab7eeaa48f33192f44c4d28e90b65d24906a1313b9b245f3444c37cf83935f6502b9c528994981
7
+ data.tar.gz: f6d97c3542f4b5105b2eae7983837e982d6ff926de6fb631ddc2b5cc10ca8a6e26044f5ff6a4e711af899e1ca5643c5cf3b87ee1157c5dd6f2c6b4598bdb2814
@@ -0,0 +1,122 @@
1
+ # coding: utf-8
2
+
3
+ require "prawn"
4
+ require "prawn/table"
5
+ require "prawn/measurement_extensions"
6
+ require "nokogiri"
7
+ require 'ostruct'
8
+ require 'yaml'
9
+ require 'date'
10
+ require 'time'
11
+ require 'json'
12
+
13
+
14
+ module Nfse
15
+
16
+ module Pdf
17
+ class DanfseGenerator
18
+ def initialize(xml)
19
+ @xml = Nfse::Pdf::XML.new(xml)
20
+ @pdf = Document.new
21
+ @vol = 0
22
+ end
23
+
24
+ attr_reader :municipios
25
+
26
+ def municipios
27
+ lib_path = File.expand_path("../../", __FILE__)
28
+ @municipios ||= JSON.parse(File.read(File.join(lib_path, 'municipios.json')))
29
+ end
30
+
31
+ def generatePDF
32
+ render_titulo
33
+ render_prestador
34
+ render_tomador
35
+ render_intermediario
36
+ render_discriminacao
37
+ render_valor_total
38
+ render_outras
39
+ return @pdf.render
40
+ end
41
+
42
+ private
43
+ def render_titulo
44
+ @pdf.ibox 2.55, 16.10, 0.25, 0.42, '',
45
+ "PREFEITURA DO MUNICÍPIO DE #{municipios[@xml['InfNfse/PrestadorServico/Endereco/CodigoMunicipio']].upcase} \n" +
46
+ "Secretaria Municipal da Fazenda \n" +
47
+ "NOTA FISCAL ELETRÔNICA DE SERVIÇOS - NFS-e \n" +
48
+ "RPS n° #{@xml['IdentificacaoRps/Numero']}, emitido em #{@xml['DataEmissao']}", {:align => :center, :valign => :center}
49
+
50
+ @pdf.ibox 0.85, 4.47, 16.35, 0.42, "NÚMERO DA NOTA", "#{@xml['InfNfse/Numero'].rjust(8,'0')}"
51
+ @pdf.ibox 0.85, 4.47, 16.35, 1.27, "DATA E HORA DE EMISSÃO", "#{@xml['InfNfse/DataEmissao'].gsub('T', ' ')}"
52
+ @pdf.ibox 0.85, 4.47, 16.35, 2.12, "CÓDIGO DE VERIFICAÇÃO", "#{@xml['CodigoVerificacao']}"
53
+ end
54
+
55
+ def render_prestador
56
+ @pdf.ibox 4.25, 20.57, 0.25, 2.97
57
+ @pdf.ibox 0.85, 20.57, 0.25, 2.97, '', 'PRESTADOR DE SERVIÇOS', {border: 0, style: :bold,:align => :center, :valign => :center}
58
+ @pdf.ibox 0.85, 20.57, 0.25, 3.82, "Nome/Razão Social", "#{@xml['PrestadorServico/RazaoSocial']}", {border: 0}
59
+ @pdf.ibox 0.85, 12, 0.25, 4.67, "CPF/CNPJ", "#{@xml['PrestadorServico/IdentificacaoPrestador/Cnpj'] || @xml['PrestadorServico/IdentificacaoPrestador/Cpf']}", {border: 0}
60
+ @pdf.ibox 0.85, 4.47, 12, 4.67, "Inscrição Municipal", "#{@xml['IdentificacaoPrestador/InscricaoMunicipal']}", {border: 0}
61
+ @pdf.ibox 0.85, 20.57, 0.25, 5.52, "Endereço", "#{@xml['PrestadorServico/Endereco/Endereco']}", {border: 0}
62
+ @pdf.ibox 0.85, 10, 0.25, 6.37, "Município", "#{municipios[@xml['PrestadorServico/Endereco/CodigoMunicipio']]}", {border: 0}
63
+ @pdf.ibox 0.85, 4.47, 10, 6.37, "UF", "#{@xml['PrestadorServico/Endereco/Uf']}", {border: 0}
64
+ @pdf.ibox 0.85, 4.47, 15, 6.37, "E-mail", "#{@xml['PrestadorServico/Contato/Email']}", {border: 0}
65
+ end
66
+
67
+ def render_tomador
68
+ @pdf.ibox 4.25, 20.57, 0.25, 7.22
69
+ @pdf.ibox 0.85, 20.57, 0.25, 7.22, '', 'TOMADOR DE SERVIÇOS', {border: 0, style: :bold,:align => :center, :valign => :center}
70
+ @pdf.ibox 0.85, 20.57, 0.25, 8.07, "Nome/Razão Social", "#{@xml['TomadorServico/RazaoSocial']}", {border: 0}
71
+ cpf_cnpj = @xml['TomadorServico/IdentificacaoTomador/CpfCnpj/Cnpj']
72
+ if cpf_cnpj.empty? then cpf_cnpj = @xml['TomadorServico/IdentificacaoTomador/CpfCnpj/Cpf'] end
73
+ @pdf.ibox 0.85, 12, 0.25, 8.92, "CPF/CNPJ", "#{cpf_cnpj}", {border: 0}
74
+ @pdf.ibox 0.85, 4.47, 12, 8.92, "Inscrição Municipal", "#{@xml['TomadorServico/IdentificacaoTomador/InscricaoMunicipal']}", {border: 0}
75
+ @pdf.ibox 0.85, 20.57, 0.25, 9.77, "Endereço", "#{@xml['TomadorServico/Endereco/Endereco']}", {border: 0}
76
+ @pdf.ibox 0.85, 10, 0.25, 10.62, "Município", "#{municipios[@xml['TomadorServico/Endereco/CodigoMunicipio']]}", {border: 0}
77
+ @pdf.ibox 0.85, 4.47, 10, 10.62, "UF", "#{@xml['TomadorServico/Endereco/Uf']}", {border: 0}
78
+ @pdf.ibox 0.85, 4.47, 15, 10.62, "E-mail", "#{@xml['TomadorServico/Contato/Email']}", {border: 0}
79
+ end
80
+
81
+ def render_intermediario
82
+ @pdf.ibox 1.70, 20.57, 0.25, 11.47
83
+ @pdf.ibox 0.85, 20.57, 0.25, 11.47, '', 'INTERMEDIÁRIO DE SERVIÇOS', {border: 0, style: :bold,:align => :center, :valign => :center}
84
+ @pdf.ibox 0.85, 12, 0.25, 12.32, "Nome/Razão Social", "#{@xml['IntermediarioServico/IdentificacaoIntermediarioServico/RazaoSocial']}", {border: 0}
85
+ cpf_cnpj = @xml['IntermediarioServico/IdentificacaoIntermediarioServico/CpfCnpj/Cnpj']
86
+ if cpf_cnpj.empty? then cpf_cnpj = @xml['IntermediarioServico/IdentificacaoIntermediarioServico/CpfCnpj/Cpf'] end
87
+ @pdf.ibox 0.85, 8, 12.25, 12.32, "CPF/CNPJ", "#{cpf_cnpj}", {border: 0}
88
+ end
89
+
90
+ def render_discriminacao
91
+ @pdf.ibox 9.35, 20.57, 0.25, 13.17
92
+ @pdf.ibox 0.85, 20.57, 0.25, 13.17, '', 'DISCRIMINAÇÃO DOS SERVIÇOS', {border: 0, style: :bold,:align => :center, :valign => :center}
93
+ @pdf.ibox 8, 19.57, 0.75, 14.02, "", "#{@xml['Servico/Discriminacao']}", {border: 0}
94
+ end
95
+
96
+ def render_valor_total
97
+ @pdf.ibox 1.70, 20.57, 0.25, 22.52
98
+ @pdf.ibox 0.85, 20.57, 0.25, 22.52, '', "VALOR TOTAL DO SERVIÇO = R$#{Helper.numerify(@xml['Servico/Valores/ValorServicos'])}", {border: 0, style: :bold,:align => :center, :valign => :center}
99
+ @pdf.inumeric 0.85, 4.06, 0.25, 23.37, "INSS", @xml['Servico/Valores/ValorInss']
100
+ @pdf.inumeric 0.85, 4.06, 4.31, 23.37, "IRRF", @xml['Servico/Valores/ValorIr']
101
+ @pdf.inumeric 0.85, 4.06, 8.37, 23.37, "CSLL", @xml['Servico/Valores/ValorCsll']
102
+ @pdf.inumeric 0.85, 4.06, 12.43, 23.37, "COFINS", @xml['Servico/Valores/ValorCofins']
103
+ @pdf.inumeric 0.85, 4.32, 16.49, 23.37, "PIS/PASEP", @xml['Servico/Valores/ValorPis']
104
+ @pdf.ibox 0.85, 20.57, 0.25, 24.22, "Código do Serviço", @xml['Servico/CodigoTributacaoMunicipio']
105
+ @pdf.inumeric 0.85, 3.46, 0.25, 25.07, "Valor Total das Deduções", @xml['Servico/Valores/ValorDeducoes']
106
+ @pdf.inumeric 0.85, 3.46, 3.71, 25.07 , "Base de Cálculo", @xml['Servico/Valores/BaseCalculo']
107
+ @pdf.ibox 0.85, 3.46, 7.17, 25.07, "Alíquota", @xml['Servico/Valores/Aliquota']
108
+ @pdf.inumeric 0.85, 3.46, 10.63, 25.07, "Valor do ISS", @xml['Servico/Valores/ValorIss']
109
+ @pdf.inumeric 0.85, 6.73, 14.09, 25.07, "Crédito", @xml['InfNfse/ValorCredito']
110
+ @pdf.ibox 0.85, 10.38, 0.25, 25.92, "Muncípio da Prestação do Serviço", municipios[@xml['OrgaoGerador/CodigoMunicipio']], :style => :bold
111
+ @pdf.ibox 0.85, 10.19, 10.63, 25.92, "Número Inscrição da Obra", @xml['DadosConstrucaoCivil/CodigoObra'], :style => :bold
112
+ end
113
+
114
+ def render_outras
115
+ @pdf.ibox 2.55, 20.57, 0.25, 26.77
116
+ @pdf.ibox 0.85, 20.57, 0.25, 26.77, '', 'OUTRAS INFORMAÇÕES', {border: 0, style: :bold,:align => :center, :valign => :center}
117
+ @pdf.ibox 1.70, 19.57, 0.75, 27.62, "", "#{@xml['InfNfse/OutrasInformacoes']}", {border: 0}
118
+ end
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,46 @@
1
+ #encoding: utf-8
2
+
3
+
4
+ module Nfse::Pdf
5
+ class Descricao
6
+ LINEBREAK = "\n"
7
+
8
+ def self.generate(det)
9
+ descricao = "#{det.css('prod/xProd').text}"
10
+
11
+ if need_infAdProd(det)
12
+ descricao += LINEBREAK
13
+ descricao += det.css('infAdProd').text
14
+ end
15
+
16
+ if need_fci(det)
17
+ descricao += LINEBREAK
18
+ descricao += "FCI: #{det.css('prod/nFCI').text}"
19
+ end
20
+
21
+ if need_st(det)
22
+ descricao += LINEBREAK
23
+ descricao += "ST: MVA: #{det.css('ICMS/*/pMVAST').text}% "
24
+ descricao += "* Alíq: #{det.css('ICMS/*/pICMSST').text}% "
25
+ descricao += "* BC: #{det.css('ICMS/*/vBCST').text} "
26
+ descricao += "* Vlr: #{det.css('ICMS/*/vICMSST').text}"
27
+ end
28
+
29
+ descricao
30
+ end
31
+
32
+ private
33
+ def self.need_infAdProd(det)
34
+ !det.css('infAdProd').text.empty?
35
+ end
36
+
37
+ def self.need_fci(det)
38
+ !det.css('prod/nFCI').text.empty?
39
+ end
40
+
41
+ def self.need_st(det)
42
+ det.css('ICMS/*/vBCST').text.to_i > 0
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,84 @@
1
+ require "prawn"
2
+ require "prawn/measurement_extensions"
3
+ require 'json'
4
+
5
+ module Nfse
6
+
7
+ module Pdf
8
+ class Document
9
+ def initialize(opts = {})
10
+ default_opts = {
11
+ :page_size => 'A4',
12
+ :page_layout => :portrait,
13
+ :left_margin => 0,
14
+ :right_margin => 0,
15
+ :top_margin => 0,
16
+ :botton_margin => 0
17
+ }
18
+
19
+ @document = Prawn::Document.new(default_opts.merge(opts))
20
+
21
+ @document.font "Times-Roman"
22
+ end
23
+
24
+ def method_missing(method_name, *args, &block)
25
+ @document.send(method_name, *args, &block)
26
+ end
27
+
28
+ def respond_to_missing?(method_name, include_private = false)
29
+ @document.respond_to?(method_name, include_private) || super
30
+ end
31
+
32
+ def ititle(h, w, x, y, title)
33
+ self.text_box title, :size => 10, :at => [x.cm, Helper.invert(y.cm) - 2], :width => w.cm, :height => h.cm, :style => :bold
34
+ end
35
+
36
+
37
+
38
+ def irectangle(h, w, x, y)
39
+ self.stroke_rectangle [x.cm, Helper.invert(y.cm)], w.cm, h.cm
40
+ end
41
+
42
+ def ibox(h, w, x, y, title = '', info = '', options = {})
43
+ box [x.cm, Helper.invert(y.cm)], w.cm, h.cm, title, info, options
44
+ end
45
+
46
+ def idate(h, w, x, y, title = '', info = '', options = {})
47
+ tt = info.gsub(/T.*/, '').split('-')
48
+ ibox h, w, x, y, title, "#{tt[2]}/#{tt[1]}/#{tt[0]}", options
49
+ end
50
+
51
+ def box(at, w, h, title = '', info = '', options = {})
52
+ options = {
53
+ :align => :left,
54
+ :size => 10,
55
+ :style => nil,
56
+ :valign => :top,
57
+ :border => 1
58
+ }.merge(options)
59
+ self.stroke_rectangle at, w, h if options[:border] == 1
60
+ self.text_box title, :size => 6, :style => :italic, :at => [at[0] + 2, at[1] - 2], :width => w - 4, :height => 8 if title != ''
61
+ 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]
62
+ end
63
+
64
+ def inumeric(h, w, x, y, title = '', info = '', options = {})
65
+ numeric [x.cm, Helper.invert(y.cm)], w.cm, h.cm, title, info, options
66
+ end
67
+
68
+ def numeric(at, w, h, title = '', info = '', options = {})
69
+ options = {:decimals => 2}.merge(options)
70
+ info = Helper.numerify(info, options[:decimals]) if info != ''
71
+ box at, w, h, title, info, options.merge({:align => :right})
72
+ end
73
+
74
+ def itable(h, w, x, y, data, options = {}, &block)
75
+ self.bounding_box [x.cm, Helper.invert(y.cm)], :width => w.cm, :height => h.cm do
76
+ self.table data, options do |table|
77
+ yield(table)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,101 @@
1
+ require "prawn/measurement_extensions"
2
+
3
+ module Nfse::Pdf
4
+ class Helper
5
+ def self.numerify(number, decimals = 2)
6
+ number = number.tr("\n","").delete(" ")
7
+ return "" if !number || number == ""
8
+ int, frac = ("%.#{decimals}f" % number).split(".")
9
+ int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1\.")
10
+ int + "," + frac
11
+ rescue
12
+ number
13
+ end
14
+
15
+ def self.numerify_default_zero(number, decimals = 2)
16
+ number = number.tr("\n","").delete(" ")
17
+ return "0,00" if !number || number == ""
18
+ int, frac = ("%.#{decimals}f" % number).split(".")
19
+ int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1\.")
20
+ int + "," + frac
21
+ rescue
22
+ number
23
+ end
24
+
25
+ def self.invert(y)
26
+ 28.7.cm - y
27
+ end
28
+
29
+ def self.format_quantity(qty)
30
+ return Helper.numerify(qty, RubyDanfe.options.quantity_decimals) if RubyDanfe.options.numerify_prod_qcom
31
+ qty.gsub!(",", ".")
32
+ qty[qty.rindex('.')] = ',' if qty.rindex('.')
33
+ qty
34
+ end
35
+
36
+ def self.format_datetime(string)
37
+ formated_datetime = ""
38
+
39
+ if not string.empty?
40
+ date = extract_date_time(string)
41
+ formated_datetime = date.strftime("%d/%m/%Y %H:%M:%S")
42
+ end
43
+
44
+ formated_datetime
45
+ end
46
+
47
+ def self.format_date(string)
48
+ formated_date = ""
49
+
50
+ if not string.empty?
51
+ date = Date.strptime(string, "%Y-%m-%d")
52
+ formated_date = date.strftime("%d/%m/%Y")
53
+ end
54
+
55
+ formated_date
56
+ end
57
+
58
+ def self.format_time(string)
59
+ formated_time = ""
60
+
61
+ if not string.empty?
62
+ time = Time.strptime(string, "%H:%M:%S")
63
+ formated_time = time.strftime("%H:%M:%S")
64
+ end
65
+
66
+ formated_time
67
+ end
68
+
69
+ def self.extract_time_from_date(string)
70
+ formated_time = ""
71
+
72
+ if not string.empty?
73
+ date = extract_date_time(string)
74
+ formated_time = date.strftime("%H:%M:%S")
75
+ end
76
+
77
+ formated_time
78
+ end
79
+
80
+ def self.format_cnpj(string)
81
+ formated_cnpj = ""
82
+
83
+ if not (string.empty? || string.size != 14)
84
+ formated_cnpj = string[0,2] + '.' + string[2,3] + '.' + string[5,3] +
85
+ '/' + string[8,4] + '-' + string[12,2]
86
+ end
87
+
88
+ formated_cnpj
89
+ end
90
+
91
+ private
92
+
93
+ def self.extract_date_time(string)
94
+ begin
95
+ DateTime.strptime(string, "%Y-%m-%dT%H:%M:%S")
96
+ rescue ArgumentError
97
+ DateTime.strptime(string, "%d/%m/%Y %H:%M:%S")
98
+ end
99
+ end
100
+ end
101
+ end
data/lib/danfe/xml.rb ADDED
@@ -0,0 +1,90 @@
1
+ require "prawn"
2
+ require "prawn/table"
3
+ require "prawn/measurement_extensions"
4
+ require "nokogiri"
5
+ require 'ostruct'
6
+ require 'yaml'
7
+ require 'date'
8
+ require 'time'
9
+ require 'json'
10
+
11
+
12
+ module Nfse::Pdf
13
+ class XML
14
+ def css(xpath)
15
+ @xml.css(xpath)
16
+ end
17
+
18
+ def xpath(regex)
19
+ doc = Nokogiri::HTML(@xml.to_s)
20
+ return doc.xpath(regex)
21
+ end
22
+
23
+ def regex_string(search_string, regex)
24
+ doc = Nokogiri::HTML(search_string)
25
+ return doc.xpath(regex)
26
+ end
27
+
28
+ def initialize(xml)
29
+ @xml = Nokogiri::XML(xml)
30
+ end
31
+
32
+ def [](xpath)
33
+ node = @xml.css(xpath)
34
+ return node ? node.text : ""
35
+ end
36
+
37
+ def render
38
+ if @xml.at_css('infNFe/ide')
39
+ RubyDanfe.render @xml.to_s, :danfe
40
+ elsif @xml.at_css('InfNfse/Numero')
41
+ RubyDanfe.render @xml.to_s, :danfse
42
+ else
43
+ if @xml.at_css('CTeOS')
44
+ RubyDanfe.render @xml.to_s, :dacteos
45
+ else
46
+ RubyDanfe.render @xml.to_s, :dacte
47
+ end
48
+ end
49
+ end
50
+
51
+ def collect(ns, tag, &block)
52
+ result = []
53
+ # Tenta primeiro com uso de namespace
54
+ begin
55
+ @xml.xpath("//#{ns}:#{tag}").each do |det|
56
+ result << yield(det)
57
+ end
58
+ rescue
59
+ # Caso dê erro, tenta sem
60
+ @xml.xpath("//#{tag}").each do |det|
61
+ result << yield(det)
62
+ end
63
+ end
64
+ result
65
+ end
66
+
67
+ def inject(ns, tag, acc, &block)
68
+ # Tenta primeiro com uso de namespace
69
+ begin
70
+ @xml.xpath("//#{ns}:#{tag}").each do |det|
71
+ acc = yield(acc, det)
72
+ end
73
+ rescue
74
+ # Caso dê erro, tenta sem
75
+ @xml.xpath("//#{tag}").each do |det|
76
+ acc = yield(acc, det)
77
+ end
78
+ end
79
+ acc
80
+ end
81
+
82
+ def attrib(node, attrib)
83
+ begin
84
+ return @xml.css(node).attr(attrib).text
85
+ rescue
86
+ ""
87
+ end
88
+ end
89
+ end
90
+ end