sefaz 0.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -0
  3. data/Gemfile.lock +31 -17
  4. data/README.md +449 -17
  5. data/Rakefile +1 -0
  6. data/docs/CFe-SAT/Especificacao_SAT_v_ER_2_30_03.pdf +0 -0
  7. data/docs/CFe-SAT/Manual_Orientacao_SAT_v_MO_2_19_04.pdf +0 -0
  8. data/docs/CFe-SAT/Manual_do_Emulador_SAT-CF-e_Offline_2015_09_10.pdf +0 -0
  9. data/docs/CFe-SAT/_CANCEL_dataset.md +14 -0
  10. data/docs/CFe-SAT/_SALE_dataset.md +171 -0
  11. data/docs/CFe-SAT/emulador_off_line_v2_9_4.zip +0 -0
  12. data/docs/NFe-NFCe/ANEXO I - Leiaute e Regra de Valida/303/247/303/243o - NF-e e NFC-e.pdf +0 -0
  13. data/docs/NFe-NFCe/ANEXO II -Manual Especifica/303/247/303/265esT/303/251cnicas - Danfe-C/303/263digo-Barras.pdf +0 -0
  14. data/docs/NFe-NFCe/Anexo III - Manual de Conting/303/252ncia - NF-e.pdf +0 -0
  15. data/docs/NFe-NFCe/Anexo IV - Manual de Conting/303/252ncia - NFC-e.pdf +0 -0
  16. data/docs/NFe-NFCe/Manual de Orienta/303/247/303/243o ao Contribuinte - MOC - vers/303/243o 7.0 - NF-e e NFC-e.pdf +0 -0
  17. data/docs/NFe-NFCe/_dataset.md +819 -0
  18. data/lib/sefaz/base.rb +11 -0
  19. data/lib/sefaz/configuration.rb +58 -0
  20. data/lib/sefaz/exception.rb +14 -0
  21. data/lib/sefaz/refinement.rb +91 -0
  22. data/lib/sefaz/utils/connection.rb +35 -0
  23. data/lib/sefaz/utils/prawn_helper.rb +47 -0
  24. data/lib/sefaz/utils/signer.rb +122 -0
  25. data/lib/sefaz/version.rb +1 -1
  26. data/lib/sefaz/webservice/base.rb +13 -0
  27. data/lib/sefaz/webservice/nfe/auditor.rb +35 -0
  28. data/lib/sefaz/webservice/nfe/client.rb +426 -0
  29. data/lib/sefaz/webservice/nfe/connection.rb +17 -0
  30. data/lib/sefaz/webservice/nfe/dataset.rb +451 -0
  31. data/lib/sefaz/webservice/nfe/validator.rb +63 -0
  32. data/lib/sefaz/webservice/nfe/wsdl.rb +117 -0
  33. data/lib/sefaz/webservice/sat/client.rb +44 -0
  34. data/lib/sefaz/webservice/sat/dataset/cancel.rb +34 -0
  35. data/lib/sefaz/webservice/sat/dataset/sale.rb +176 -0
  36. data/lib/sefaz/webservice/sat/templates/base.rb +54 -0
  37. data/lib/sefaz/webservice/sat/templates/cupom_fiscal_55mm.rb +307 -0
  38. data/lib/sefaz/webservice/sat/templates/cupom_fiscal_80mm.rb +307 -0
  39. data/lib/sefaz.rb +45 -3
  40. data/sefaz.gemspec +5 -3
  41. metadata +80 -6
data/lib/sefaz/base.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ # Classe genérica para todas outras classes base da biblioteca
5
+ class Base
6
+
7
+ def initialize
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ # Classe base para parametrização da biblioteca
5
+ class Configuration
6
+
7
+ # Configuração da formatação de valores monetários
8
+ attr_accessor :currency_format
9
+
10
+ # CF-e (Cupom Fiscal Eletrônico)
11
+ attr_accessor :cfe_qr_code_aplicativo
12
+ attr_accessor :cfe_cMP_cod_desc
13
+
14
+ # 55mm
15
+ attr_accessor :cfe_page_width_55mm
16
+ attr_accessor :cfe_margin_55mm
17
+
18
+ # 80mm
19
+ attr_accessor :cfe_page_width_80mm
20
+ attr_accessor :cfe_margin_80mm
21
+
22
+ def initialize
23
+ @currency_format = { delimiter: ".", separator: ",", unit: "", precision: 2, position: "before" }
24
+ @cfe_qr_code_aplicativo = "De olho na nota"
25
+ @cfe_cMP_cod_desc = {
26
+ "01" => "Dinheiro",
27
+ "02" => "Cheque",
28
+ "03" => "Cartão de Crédito",
29
+ "04" => "Cartão de Débito",
30
+ "05" => "Crédito Loja",
31
+ "10" => "Vale Alimentação",
32
+ "11" => "Vale Refeição",
33
+ "12" => "Vale Presente",
34
+ "13" => "Vale Combustível",
35
+ "15" => "Boleto Bancário",
36
+ "16" => "Depósito Bancário",
37
+ "17" => "Pagamento Instantâneo (PIX)",
38
+ "18" => "Transferência bancária, Carteira Digital",
39
+ "19" => "Programa de fidelidade, Cashback, Crédito Virtual",
40
+ "90" => "Sem pagamento",
41
+ "99" => "Outros"
42
+ }
43
+
44
+ # 50mm
45
+ @cfe_page_width_55mm = 55.mm
46
+ @cfe_margin_55mm = 5.mm
47
+
48
+ # 80mm
49
+ @cfe_page_width_80mm = 80.mm
50
+ @cfe_margin_80mm = 5.mm
51
+ end
52
+
53
+ def self.default
54
+ @default ||= Configuration.new
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ class ValidationError < StandardError
9
+ end
10
+
11
+ class NotImplemented < RuntimeError
12
+ end
13
+
14
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ # Módulo base de refinamento da biblioteca
5
+ module Refinement
6
+
7
+ module String
8
+ def to_hash!
9
+ Nori.new(convert_tags_to: lambda { |key| key.to_sym }).parse(self)
10
+ end
11
+
12
+ def compress!
13
+ doc = Nokogiri::XML::Document.parse(self, nil, "utf-8", Nokogiri::XML::ParseOptions::NOBLANKS)
14
+ doc.search('*').each { |node| node.remove if node.content.strip.blank? }
15
+ doc.canonicalize
16
+ end
17
+
18
+ def mask!(mask)
19
+ index = -1
20
+ mask.gsub('#') { self[index += 1] }
21
+ end
22
+
23
+ def to_number(options={})
24
+ return self.gsub(/,/, '.').to_f if self.numeric?
25
+ nil
26
+ end
27
+
28
+ def numeric?
29
+ self =~ /^(|-)?[0-9]+((\.|,)[0-9]+)?$/ ? true : false
30
+ end
31
+
32
+ def to_currency(options = {})
33
+ return self.to_number.to_currency(options) if self.numeric?
34
+ nil
35
+ end
36
+ end
37
+
38
+ module Hash
39
+ def to_xml!
40
+ Gyoku.xml(self, key_converter: :none)
41
+ end
42
+
43
+ def compress!
44
+ self.to_xml!.compress!.to_hash!
45
+ end
46
+ end
47
+
48
+ module Currency
49
+ def to_currency(options = {})
50
+ number = self
51
+ default = Configuration.default.currency_format
52
+ options = default.merge(options)
53
+ precision = options[:precision] || default[:precision]
54
+ unit = options[:unit] || default[:unit]
55
+ position = options[:position] || default[:position]
56
+ separator = precision > 0 ? options[:separator] || default[:separator] : ""
57
+ delimiter = options[:delimiter] || default[:delimiter]
58
+
59
+ begin
60
+ parts = number.with_precision(precision).split('.')
61
+ number = parts[0].to_i.with_delimiter(delimiter) + separator + parts[1].to_s
62
+ position == "before" ? unit + number : number + unit
63
+ rescue
64
+ number
65
+ end
66
+ end
67
+
68
+ def with_delimiter(delimiter = ",", separator = ".")
69
+ number = self
70
+ begin
71
+ parts = number.to_s.split(separator)
72
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
73
+ parts.join separator
74
+ rescue
75
+ self
76
+ end
77
+ end
78
+
79
+ def with_precision(precision = 3)
80
+ number = self
81
+ "%01.#{precision}f" % number
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+
88
+ String.class_eval { include SEFAZ::Refinement::String }
89
+ Hash.class_eval { include SEFAZ::Refinement::Hash }
90
+ Integer.class_eval { include SEFAZ::Refinement::Currency }
91
+ Float.class_eval { include SEFAZ::Refinement::Currency }
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Utils
5
+ # Classe base de conexão SOAP da biblioteca
6
+ class Connection
7
+
8
+ def initialize(pkcs12, wsdl, soap_header)
9
+ @conn = Savon.client(
10
+ wsdl: wsdl,
11
+ ssl_verify_mode: :none,
12
+ ssl_cert: pkcs12.certificate,
13
+ ssl_cert_key: pkcs12.key,
14
+ soap_header: soap_header,
15
+ soap_version: 2,
16
+ convert_request_keys_to: :none,
17
+ convert_response_tags_to: lambda { |key| key.to_sym },
18
+ namespace_identifier: nil
19
+ )
20
+ end
21
+
22
+ def connected?; (@conn.operations.length > 0) end
23
+ def operations; (@conn.operations) end
24
+
25
+ def build(operation, hash)
26
+ @conn.build_request(operation, message: hash)
27
+ end
28
+
29
+ def call(operation, hash)
30
+ @conn.call(operation, message: hash)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Utils
5
+ # Módulo com recursos adicionais para os templates baseados na Prawn
6
+ module PrawnHelper
7
+
8
+ def dash(doc, gap:, space:, line_width:, x1:, x2:, y:)
9
+ doc.dash(gap, space: space)
10
+ doc.line_width line_width
11
+ doc.stroke_horizontal_line x1, x2, at: y
12
+ doc.move_down 2
13
+ end
14
+
15
+ def barcode_128c(doc, value:, x:, y:, xdim: 1, height: 20, margin: 0, unbleed: 0, color: '000000')
16
+ barcode = Barby::Code128C.new(value)
17
+ outputter = Barby::PrawnOutputter.new(barcode)
18
+ outputter.x = x
19
+ outputter.y = (y - height)
20
+ outputter.xdim = xdim
21
+ outputter.height = height
22
+ outputter.margin = margin
23
+ outputter.unbleed = unbleed
24
+ outputter.color = color
25
+
26
+ doc.move_down height
27
+ outputter.annotate_pdf doc
28
+ end
29
+
30
+ def qrcode(doc, value:, x:, y:, xdim: 1, margin: 0, unbleed: 0, color: '000000')
31
+ qrcode = Barby::QrCode.new(value)
32
+ outputter = Barby::PrawnOutputter.new(qrcode)
33
+ height = (outputter.boolean_groups.length * xdim)
34
+ outputter.x = x
35
+ outputter.y = (y - height)
36
+ outputter.xdim = xdim
37
+ outputter.margin = margin
38
+ outputter.unbleed = unbleed
39
+ outputter.color = color
40
+
41
+ doc.move_down height
42
+ outputter.annotate_pdf doc
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Utils
5
+ # Classe base de assinatura de XML da biblioteca
6
+ class Signer
7
+
8
+ def initialize(xml)
9
+ @doc = Nokogiri::XML::Document.parse(xml, nil, "utf-8", Nokogiri::XML::ParseOptions::NOBLANKS)
10
+ end
11
+
12
+ def sign!(cert, key)
13
+ @cert = cert
14
+ @key = key
15
+ @doc.root.add_child(signature)
16
+ end
17
+
18
+ def to_xml
19
+ return @doc.canonicalize
20
+ end
21
+
22
+ private
23
+
24
+ def signature
25
+ # Create Signature
26
+ node = Nokogiri::XML::Node.new('Signature', @doc)
27
+ node.default_namespace = 'http://www.w3.org/2000/09/xmldsig#'
28
+ node.add_child signature_info
29
+ node.add_child signature_value
30
+ node.add_child key_info
31
+ node
32
+ end
33
+
34
+ def signature_info
35
+ # Create SignatureInfo
36
+ node = Nokogiri::XML::Node.new('SignedInfo', @doc)
37
+
38
+ # Add CanonicalizationMethod
39
+ child_node = Nokogiri::XML::Node.new('CanonicalizationMethod', @doc)
40
+ child_node['Algorithm'] = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
41
+ node.add_child child_node
42
+
43
+ # Add SignatureMethod
44
+ child_node = Nokogiri::XML::Node.new('SignatureMethod', @doc)
45
+ child_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
46
+ node.add_child child_node
47
+
48
+ # Add Reference
49
+ node.add_child reference
50
+ node
51
+ end
52
+
53
+ def signature_value
54
+ # Sign Signature
55
+ xml = Nokogiri::XML(signature_info.to_xml, &:noblanks)
56
+ xml.root["xmlns"] = 'http://www.w3.org/2000/09/xmldsig#'
57
+ sign_canon = xml.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
58
+ signature_hash = @key.sign(OpenSSL::Digest::SHA1.new, sign_canon)
59
+
60
+ # Add SignatureValue
61
+ node = Nokogiri::XML::Node.new('SignatureValue', @doc)
62
+ node.content = Base64.encode64(signature_hash).gsub("\n", '')
63
+ node
64
+ end
65
+
66
+ def key_info
67
+ # Create KeyInfo
68
+ node = Nokogiri::XML::Node.new('KeyInfo', @doc)
69
+
70
+ # Add X509 Data and Certificate
71
+ x509_data = Nokogiri::XML::Node.new('X509Data', @doc)
72
+ x509_certificate = Nokogiri::XML::Node.new('X509Certificate', @doc)
73
+ x509_certificate.content = @cert.to_pem.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, "").gsub(/\n/,"")
74
+
75
+ x509_data.add_child x509_certificate
76
+ node.add_child x509_data
77
+ node
78
+ end
79
+
80
+ def reference
81
+ inf = @doc.at_xpath("//*[@Id]")
82
+
83
+ # Calculate digest
84
+ xml_canon = inf.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
85
+ xml_digest = Base64.encode64(OpenSSL::Digest::SHA1.digest(xml_canon)).strip
86
+
87
+ # Create Reference
88
+ node = Nokogiri::XML::Node.new('Reference', @doc)
89
+ node['URI'] = "##{inf['Id']}"
90
+ node.add_child transforms
91
+
92
+ # Add Digest
93
+ child_node = Nokogiri::XML::Node.new('DigestMethod', @doc)
94
+ child_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#sha1'
95
+ node.add_child child_node
96
+
97
+ # Add DigestValue
98
+ child_node = Nokogiri::XML::Node.new('DigestValue', @doc)
99
+ child_node.content = xml_digest
100
+ node.add_child child_node
101
+ node
102
+ end
103
+
104
+ def transforms
105
+ # Add Transforms
106
+ node = Nokogiri::XML::Node.new('Transforms', @doc)
107
+
108
+ # Add Transform
109
+ child_node = Nokogiri::XML::Node.new('Transform', @doc)
110
+ child_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
111
+ node.add_child child_node
112
+
113
+ # Add Transform
114
+ child_node = Nokogiri::XML::Node.new('Transform', @doc)
115
+ child_node['Algorithm'] = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
116
+ node.add_child child_node
117
+ node
118
+ end
119
+
120
+ end
121
+ end
122
+ end
data/lib/sefaz/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SEFAZ
4
- VERSION = "0.2.0"
4
+ VERSION = "1.3.0"
5
5
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Webservice
5
+ # Classe base para todas as integrações - Webservice
6
+ class Base < SEFAZ::Base
7
+
8
+ def initialize
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SEFAZ
4
+ module Webservice
5
+ module NFE
6
+ # Auditor de XML pelo validador público da Tecnospeed
7
+ class Auditor
8
+
9
+ def initialize(xml)
10
+ @xml = xml
11
+ end
12
+
13
+ def exec(openTimeout, readTimeout)
14
+ uri = URI("https://validadornfe.tecnospeed.com.br/validar")
15
+ http = Net::HTTP.new(uri.host, uri.port)
16
+ http.use_ssl = true
17
+ http.open_timeout = openTimeout
18
+ http.read_timeout = readTimeout
19
+ content = Net::HTTP::Post.new(uri.request_uri)
20
+ content.set_form_data(txtXML: @xml)
21
+ response = http.request(content)
22
+ if response.code == "200"
23
+ url = JSON(response.body)["url"]
24
+ url_esc = url.to_s.sub("{", "%7B").sub("}", "%7D")
25
+ txt_uri = URI(url_esc)
26
+ body = CGI.unescape(Net::HTTP.get(txt_uri))
27
+ return [:ok, JSON(body)]
28
+ end
29
+ return [:error, {}]
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end