inovadora_xml 0.0.1 → 0.0.6
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.
- data/inovadora_xml-0.0.2.gem +0 -0
- data/inovadora_xml.gemspec +4 -3
- data/lib/inovadora_xml.rb +93 -2
- data/lib/inovadora_xml/assinador.rb +122 -0
- data/lib/inovadora_xml/custom_exceptions.rb +6 -0
- data/lib/inovadora_xml/custom_exceptions/server_error.rb +11 -0
- data/lib/inovadora_xml/modules.rb +9 -0
- data/lib/inovadora_xml/modules/dados_nfse_service.rb +143 -0
- data/lib/inovadora_xml/modules/formatador_nfse.rb +36 -0
- data/lib/inovadora_xml/modules/gerador_xml.rb +44 -0
- data/lib/inovadora_xml/modules/xml_retorno.rb +105 -0
- data/lib/inovadora_xml/servidor_wsdl.rb +47 -0
- data/lib/inovadora_xml/version.rb +1 -1
- metadata +38 -12
|
Binary file
|
data/inovadora_xml.gemspec
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
-
|
|
3
2
|
lib = File.expand_path("../lib", __FILE__)
|
|
4
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
4
|
require "inovadora_xml/version"
|
|
@@ -15,6 +14,7 @@ Gem::Specification.new do |spec|
|
|
|
15
14
|
|
|
16
15
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
17
16
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
+
|
|
18
18
|
end
|
|
19
19
|
spec.bindir = "exe"
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
@@ -23,7 +23,8 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.17"
|
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
26
|
-
spec.add_development_dependency "nokogiri", "1.6.0"
|
|
27
26
|
spec.add_development_dependency "signer", "1.4.2"
|
|
28
|
-
spec.
|
|
27
|
+
spec.add_dependency "nokogiri", ">= 1.4.0"
|
|
28
|
+
spec.add_dependency "savon", "1.2.0"
|
|
29
|
+
spec.add_dependency "wasabi", "2.5.0"
|
|
29
30
|
end
|
data/lib/inovadora_xml.rb
CHANGED
|
@@ -1,6 +1,97 @@
|
|
|
1
|
+
#encoding: utf-8
|
|
2
|
+
require 'action_pack'
|
|
3
|
+
|
|
1
4
|
require "inovadora_xml/version"
|
|
2
5
|
|
|
6
|
+
require "inovadora_xml/assinador"
|
|
7
|
+
require "inovadora_xml/servidor_wsdl"
|
|
8
|
+
|
|
9
|
+
|
|
3
10
|
module InovadoraXml
|
|
4
|
-
|
|
5
|
-
|
|
11
|
+
extend ActiveSupport::Autoload
|
|
12
|
+
attr_accessor :xml_nfse, :schema, :metodo_soap, :xml_gerado, :retorno, :wsdl, :cert, :key, :errors
|
|
13
|
+
autoload :Modules
|
|
14
|
+
autoload :CustomExceptions
|
|
15
|
+
|
|
16
|
+
class Xml
|
|
17
|
+
attr_accessor :xml_nfse, :schema, :metodo_soap, :xml_gerado, :retorno, :wsdl, :cert, :key, :errors
|
|
18
|
+
|
|
19
|
+
include InovadoraXml::Modules::FormatadorNfse
|
|
20
|
+
include InovadoraXml::Modules::GeradorXml
|
|
21
|
+
|
|
22
|
+
def initialize(*args)
|
|
23
|
+
self.errors = ActiveModel::Errors.new(self)
|
|
24
|
+
self.xml_gerado = self.gerar_xml()
|
|
25
|
+
|
|
26
|
+
self.validar_args()
|
|
27
|
+
# self.valid?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def comunicar(request_args = {})
|
|
31
|
+
if self.valid?
|
|
32
|
+
servidor = InovadoraXml::ServidorWsdl.new(
|
|
33
|
+
self.envelopar(),
|
|
34
|
+
self.metodo_soap,
|
|
35
|
+
self.wsdl,
|
|
36
|
+
self.cert,
|
|
37
|
+
self.key,
|
|
38
|
+
request_args
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
unless servidor.enviar
|
|
42
|
+
raise InovadoraXml::CustomExceptions::ServerError.new(servidor), "Server Error"
|
|
43
|
+
end
|
|
44
|
+
#usage: self.retorno.to_xml ou self.retorno.body
|
|
45
|
+
self.retorno = servidor.retorno
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
rescue InovadoraXml::CustomExceptions::ServerError => e
|
|
49
|
+
e.object.errors.full_messages.each {|e| "Erro na comunicação: #{self.errors.add(:base, e)}"}
|
|
50
|
+
rescue Exception => e
|
|
51
|
+
self.errors.add(:base, e.message)
|
|
52
|
+
ensure
|
|
53
|
+
return self.errors.blank?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def validar_args
|
|
57
|
+
self.errors.add(:base, "Metodo SOAP não especificado") if self.metodo_soap.blank?
|
|
58
|
+
self.errors.add(:base, "Schema não especificado") if self.schema.blank?
|
|
59
|
+
self.errors.add(:base, "WSDL não especificado") if self.wsdl.blank?
|
|
60
|
+
|
|
61
|
+
return self.errors.blank?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def valid?
|
|
65
|
+
erros = []
|
|
66
|
+
schema = open(self.schema)
|
|
67
|
+
xsd = Nokogiri::XML::Schema(schema)
|
|
68
|
+
xsd.validate(self.xml_gerado).each do |error|
|
|
69
|
+
unless error.message.to_s.include?("{http://www.w3.org/2000/09/xmldsig#}Signature") ||
|
|
70
|
+
error.message.to_s.include?("{http://www.w3.org/2000/09/xmldsig#}SignedInfo")
|
|
71
|
+
erros << "(Linha: " + error.line.to_s + ") => " + error.message.to_s
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
erros.each {|erro| self.errors.add(:base, erro)}
|
|
76
|
+
rescue Errno::ENOENT => e
|
|
77
|
+
self.errors.add(:base, "Arquivo schema não existe")
|
|
78
|
+
rescue Exception => e
|
|
79
|
+
self.errors.add(:base, e)
|
|
80
|
+
ensure
|
|
81
|
+
self.errors.blank?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def assinar(tag_assinar)
|
|
85
|
+
assinador = InovadoraXml::Assinador.new(self.xml_gerado, tag_assinar, self.cert, self.key)
|
|
86
|
+
assinador.assinar_xml
|
|
87
|
+
|
|
88
|
+
assinador.errors.full_messages.each do |error|
|
|
89
|
+
self.errors.add(:base, error)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
self.xml_gerado = assinador.xml_assinado if assinador.errors.blank?
|
|
93
|
+
ensure
|
|
94
|
+
return self.errors.blank?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
6
97
|
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#encoding=utf-8
|
|
2
|
+
module InovadoraXml
|
|
3
|
+
class Assinador
|
|
4
|
+
attr_accessor :xml_assinar, :xml_assinado, :tag_assinar, :cert, :key, :errors, :parametro
|
|
5
|
+
|
|
6
|
+
def initialize(xml, tag_assinar, cert, key)
|
|
7
|
+
self.errors = ActiveModel::Errors.new(self)
|
|
8
|
+
|
|
9
|
+
self.xml_assinar = xml.to_s
|
|
10
|
+
self.tag_assinar = tag_assinar
|
|
11
|
+
self.cert = cert
|
|
12
|
+
self.key = key
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def assinar_xml()
|
|
16
|
+
if self.valid?
|
|
17
|
+
chave_privada = OpenSSL::PKey::RSA.new(File.read(self.key))
|
|
18
|
+
certificado = OpenSSL::X509::Certificate.new(File.read(self.cert))
|
|
19
|
+
|
|
20
|
+
self.xml_assinado = Nokogiri::XML(self.xml_assinar.to_s, &:noblanks)
|
|
21
|
+
self.xml_assinado.xpath("//xmlns:#{self.tag_assinar}").each do |node|
|
|
22
|
+
|
|
23
|
+
# 1. Digest Hash for all XML
|
|
24
|
+
xml_canon = node.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
|
|
25
|
+
xml_digest = Base64.encode64(OpenSSL::Digest::SHA1.digest(xml_canon)).strip
|
|
26
|
+
|
|
27
|
+
# 2. Add Signature Node
|
|
28
|
+
signature = Nokogiri::XML::Node.new('Signature', self.xml_assinado)
|
|
29
|
+
signature.default_namespace = 'http://www.w3.org/2000/09/xmldsig#'
|
|
30
|
+
node.after(signature)
|
|
31
|
+
|
|
32
|
+
# 3.1 Create Signature Info
|
|
33
|
+
signature_info = Nokogiri::XML::Node.new('SignedInfo', self.xml_assinado)
|
|
34
|
+
|
|
35
|
+
# 3.2 Add CanonicalizationMethod
|
|
36
|
+
child_node = Nokogiri::XML::Node.new('CanonicalizationMethod', self.xml_assinado)
|
|
37
|
+
child_node['Algorithm'] = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
|
|
38
|
+
signature_info.add_child child_node
|
|
39
|
+
|
|
40
|
+
# 3.3 Add SignatureMethod
|
|
41
|
+
child_node = Nokogiri::XML::Node.new('SignatureMethod', self.xml_assinado)
|
|
42
|
+
child_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
|
|
43
|
+
signature_info.add_child child_node
|
|
44
|
+
|
|
45
|
+
# 3.4 Create Reference
|
|
46
|
+
reference = Nokogiri::XML::Node.new('Reference', self.xml_assinado)
|
|
47
|
+
reference['URI'] = "##{node.first.last}"
|
|
48
|
+
|
|
49
|
+
# 3.5 Add Transforms
|
|
50
|
+
transforms = Nokogiri::XML::Node.new('Transforms', self.xml_assinado)
|
|
51
|
+
|
|
52
|
+
child_node = Nokogiri::XML::Node.new('Transform', self.xml_assinado)
|
|
53
|
+
child_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
|
|
54
|
+
transforms.add_child child_node
|
|
55
|
+
|
|
56
|
+
child_node = Nokogiri::XML::Node.new('Transform', self.xml_assinado)
|
|
57
|
+
child_node['Algorithm'] = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
|
|
58
|
+
transforms.add_child child_node
|
|
59
|
+
|
|
60
|
+
reference.add_child transforms
|
|
61
|
+
|
|
62
|
+
# 3.6 Add Digest
|
|
63
|
+
child_node = Nokogiri::XML::Node.new('DigestMethod', self.xml_assinado)
|
|
64
|
+
child_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#sha1'
|
|
65
|
+
reference.add_child child_node
|
|
66
|
+
|
|
67
|
+
# 3.6 Add DigestValue
|
|
68
|
+
child_node = Nokogiri::XML::Node.new('DigestValue', self.xml_assinado)
|
|
69
|
+
child_node.content = xml_digest
|
|
70
|
+
reference.add_child child_node
|
|
71
|
+
|
|
72
|
+
# 3.7 Add Reference and Signature Info
|
|
73
|
+
signature_info.add_child reference
|
|
74
|
+
signature.add_child signature_info
|
|
75
|
+
|
|
76
|
+
# 4 Sign Signature
|
|
77
|
+
sign_canon = signature_info.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
|
|
78
|
+
signature_hash = chave_privada.sign(OpenSSL::Digest::SHA1.new, sign_canon)
|
|
79
|
+
signature_value = Base64.encode64(signature_hash).gsub("\n", '')
|
|
80
|
+
|
|
81
|
+
# 4.1 Add SignatureValue
|
|
82
|
+
child_node = Nokogiri::XML::Node.new('SignatureValue', self.xml_assinado)
|
|
83
|
+
child_node.content = signature_value
|
|
84
|
+
signature.add_child child_node
|
|
85
|
+
|
|
86
|
+
# 5 Create KeyInfo
|
|
87
|
+
key_info = Nokogiri::XML::Node.new('KeyInfo', self.xml_assinado)
|
|
88
|
+
|
|
89
|
+
# 5.1 Add X509 Data and Certificate
|
|
90
|
+
x509_data = Nokogiri::XML::Node.new('X509Data', self.xml_assinado)
|
|
91
|
+
x509_certificate = Nokogiri::XML::Node.new('X509Certificate', self.xml_assinado)
|
|
92
|
+
x509_certificate.content = certificado.to_s.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, "").gsub(/\n/,"")
|
|
93
|
+
|
|
94
|
+
x509_data.add_child x509_certificate
|
|
95
|
+
key_info.add_child x509_data
|
|
96
|
+
|
|
97
|
+
# 5.2 Add KeyInfo
|
|
98
|
+
signature.add_child key_info
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Return XML
|
|
102
|
+
self.xml_assinado.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
|
|
103
|
+
end
|
|
104
|
+
rescue Exception => e
|
|
105
|
+
self.errors.add(:base, e.message)
|
|
106
|
+
ensure
|
|
107
|
+
return self.errors.blank?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def valid?
|
|
111
|
+
self.errors.add(:base, "Chave não informada" ) unless self.key.present?
|
|
112
|
+
self.errors.add(:base, "Certificado não informado" ) unless self.cert.present?
|
|
113
|
+
self.errors.add(:base, "Arquivo chave não encontrado") unless File.exists?(self.key.to_s)
|
|
114
|
+
self.errors.add(:base, "Arquivo certificado não encontrado") unless File.exists?(self.cert.to_s)
|
|
115
|
+
|
|
116
|
+
rescue Exception => e
|
|
117
|
+
self.errors.add(:base, e.message)
|
|
118
|
+
ensure
|
|
119
|
+
return self.errors.blank?
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module InovadoraXml
|
|
2
|
+
module Modules
|
|
3
|
+
# extend ActiveSupport::Autoload
|
|
4
|
+
autoload :FormatadorNfse, "inovadora_xml/modules/formatador_nfse"
|
|
5
|
+
autoload :GeradorXml, "inovadora_xml/modules/gerador_xml"
|
|
6
|
+
autoload :DadosNfseService, "inovadora_xml/modules/dados_nfse_service"
|
|
7
|
+
autoload :XmlRetorno, "inovadora_xml/modules/xml_retorno"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# encoding=utf-8
|
|
2
|
+
module InovadoraXml
|
|
3
|
+
module Modules
|
|
4
|
+
module DadosNfseService
|
|
5
|
+
def get_numero_lote_rps
|
|
6
|
+
self.fnnfse.build_fnnfses_sancionada if self.fnnfse.fnnfses_sancionada.blank?
|
|
7
|
+
(self.fnnfse.fnnfses_sancionada.numero_rps = self.fnnfse.class.connection.select_value("SELECT nextval('gestho.prox_nro_rps_nfse')")) if self.fnnfse.fnnfses_sancionada.numero_rps.to_s == ""
|
|
8
|
+
|
|
9
|
+
self.fnnfse.fnnfses_sancionada.numero_lote_rps = self.fnnfse.class.connection.select_value("SELECT nextval('gestho.prox_nro_lote_rps_nfse')")
|
|
10
|
+
self.fnnfse.fnnfses_sancionada.save
|
|
11
|
+
return self.fnnfse.fnnfses_sancionada.numero_lote_rps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_itens_servicos
|
|
15
|
+
case self.parametro.fornecedor
|
|
16
|
+
when 7..8 # Publica e #NF-em
|
|
17
|
+
get_itens_servicos_publica
|
|
18
|
+
else
|
|
19
|
+
"Não implementado para esse fornecedor"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get_itens_servicos_publica
|
|
24
|
+
itens_da_nota= "{"
|
|
25
|
+
if self.fnnfse.descritivo_servico.to_s == ""
|
|
26
|
+
unless self.fnnfse.fnpacote.nil?
|
|
27
|
+
itens_da_nota += "[" +
|
|
28
|
+
"[Descricao=#{self.fnnfse.try(:fnpacote).try(:descricao)} #{fnnfse.obs_pacote}]" +
|
|
29
|
+
"[Quantidade=1.0]" +
|
|
30
|
+
"[ValorUnitario=#{self.fnnfse.valor_pacote}]" +
|
|
31
|
+
"[Deducoes=0]" +
|
|
32
|
+
"[DescontoCondicionado=0]" +
|
|
33
|
+
"[DescontoIncondicionado=#{self.fnnfse.valor_pacote_desconto.to_f.round(2)}]" +
|
|
34
|
+
"[Aliquota=#{get_aliquota}]" +
|
|
35
|
+
"[itemServico=#{get_item_lista_servico}]" +
|
|
36
|
+
"]"
|
|
37
|
+
end
|
|
38
|
+
self.fnnfse.fnnfseis.each do |fnnfsei|
|
|
39
|
+
qtde = fnnfsei.qtde.to_f.round(2) == 0 ? 1.0 : fnnfsei.qtde.to_f.round(2)
|
|
40
|
+
itens_da_nota += "[" +
|
|
41
|
+
"[Descricao=#{fnnfsei.ftforma.nome}]" +
|
|
42
|
+
"[Quantidade=#{qtde}]" +
|
|
43
|
+
"[ValorUnitario=#{(fnnfsei.valor.to_f / qtde).round(8)}]" +
|
|
44
|
+
"[Deducoes=0]" +
|
|
45
|
+
"[DescontoCondicionado=0]" +
|
|
46
|
+
"[DescontoIncondicionado=#{fnnfsei.desconto.to_s.to_d}]" +
|
|
47
|
+
"[Aliquota=#{get_aliquota}]" +
|
|
48
|
+
"[itemServico=#{get_item_lista_servico}]" +
|
|
49
|
+
"]"
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
itens_da_nota += "[" +
|
|
53
|
+
"[Descricao=#{self.fnnfse.descritivo_servico}]" +
|
|
54
|
+
"[Quantidade=1.0]" +
|
|
55
|
+
"[ValorUnitario=#{self.fnnfse.valor_servicos}]" +
|
|
56
|
+
"[Deducoes=0]" +
|
|
57
|
+
"[DescontoCondicionado=0]" +
|
|
58
|
+
"[DescontoIncondicionado=#{self.fnnfse.valor_descontos}]" +
|
|
59
|
+
"[Aliquota=#{get_aliquota}]" +
|
|
60
|
+
"[itemServico=#{get_item_lista_servico}]" +
|
|
61
|
+
"]"
|
|
62
|
+
end
|
|
63
|
+
itens_da_nota += "}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def get_itens_servicos_texto
|
|
67
|
+
itens_da_nota = ""
|
|
68
|
+
if self.fnnfse.descritivo_servico.to_s == ""
|
|
69
|
+
if self.fnnfse.try(:fnpacote).present?
|
|
70
|
+
itens_da_nota += "#{self.fnnfse.try(:fnpacote).try(:descricao)} #{fnnfse.obs_pacote} " +
|
|
71
|
+
"Valor: #{self.fnnfse.valor_pacote} " +
|
|
72
|
+
"Desconto: #{self.fnnfse.valor_pacote_desconto.to_f.round(2)}"
|
|
73
|
+
end
|
|
74
|
+
if self.fnnfse.fnnfseis.size > 1
|
|
75
|
+
self.fnnfse.fnnfseis.each do |fnnfsei|
|
|
76
|
+
qtde = fnnfsei.qtde.to_f.round(2) == 0 ? 1.0 : fnnfsei.qtde.to_f.round(2)
|
|
77
|
+
itens_da_nota += "- #{fnnfsei.ftforma.nome} Qtde: #{qtde} " +
|
|
78
|
+
"Valor: #{((fnnfsei.valor.to_f / qtde).round(8)).round(2)} " +
|
|
79
|
+
(fnnfsei.desconto.to_s.to_d>0 ? "Desconto: #{fnnfsei.desconto.to_s.to_d.round(2)}" : "") +
|
|
80
|
+
" \n"
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
fnnfsei = self.fnnfse.fnnfseis.first
|
|
84
|
+
itens_da_nota += "#{fnnfsei.ftforma.nome}" rescue ""
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
itens_da_nota += "#{self.fnnfse.descritivo_servico} " +
|
|
88
|
+
"Valor: #{self.fnnfse.valor_servicos.to_d.round(2)} " +
|
|
89
|
+
(self.fnnfse.valor_descontos.to_d>0 ? "Desconto: #{self.fnnfse.valor_descontos.to_d.round(2)}" : "")
|
|
90
|
+
end
|
|
91
|
+
itens_da_nota
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def get_informacoes_complementares
|
|
95
|
+
mensagem = self.fnnfse.observacao.to_s.strip
|
|
96
|
+
if self.fnnfse.cliente_type == "Paciente"
|
|
97
|
+
intern = self.fnnfse.fnnfses_interns.try(:first).try(:intern)
|
|
98
|
+
unless intern.blank?
|
|
99
|
+
mensagem += " Intern:#{intern.id}-#{intern.paciente.try(:nome).try(:strip)}"
|
|
100
|
+
mensagem += " Conv.:#{self.fnnfse.get_convenio.try(:nome).try(:strip)}"
|
|
101
|
+
mensagem += " CPF:#{intern.paciente.cpf}" if intern.paciente.cpf.to_s.strip != "" && intern.paciente.cpf.to_s.strip != "0"
|
|
102
|
+
mensagem += " Nasc:#{intern.paciente.data_nascimento.to_s_br}"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
mensagem = nil if mensagem.to_s.strip.blank?
|
|
106
|
+
mensagem
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def get_item_lista_servico
|
|
110
|
+
self.hospital.iss_cd_lei_116 || '4.03'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def get_aliquota
|
|
114
|
+
self.fnnfse.percentual_iss || 0.0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def get_cnpj_prestador
|
|
118
|
+
self.hospital.cgc.to_s.gsub(/[^0-9]/, "")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def get_inscricao_municipal_prestador
|
|
122
|
+
self.hospital.inscricao_municipal.to_s.gsub(/[^0-9]-/, "")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def get_codigo_municipio_prest_servico
|
|
126
|
+
self.hospital.cidade_ibge.try(:ibge_com_digito)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def persistir_fnnfses_xml(xml, tipo_documento = 17)
|
|
130
|
+
xml_tratado = Nokogiri::XML(CGI.unescapeHTML(xml.to_xml.to_s), &:noblanks)
|
|
131
|
+
xml_tratado = xml_tratado.to_xml(indent: 2).to_s
|
|
132
|
+
if xml_tratado.blank?
|
|
133
|
+
xml_tratado = "xml não recebido da prefeitura"
|
|
134
|
+
end
|
|
135
|
+
# $b = {fnnfse: self.fnnfse, xml_envio: self.xml_gerado().to_s, xml_retorno: xml_tratado, errors: self.errors, tipo_documento: tipo_documento}
|
|
136
|
+
|
|
137
|
+
retorno_service = InovadoraXml::Modules::XmlRetorno.new(fnnfse: self.fnnfse, xml_envio: self.xml_gerado().to_s, xml_retorno: xml_tratado, errors: self.errors, tipo_documento: tipo_documento)
|
|
138
|
+
|
|
139
|
+
retorno_service.save
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#encoding: utf-8
|
|
2
|
+
module InovadoraXml
|
|
3
|
+
module Modules
|
|
4
|
+
module FormatadorNfse
|
|
5
|
+
def data_formato_br(data)
|
|
6
|
+
# Padrão: 11/11/2011
|
|
7
|
+
data = data.to_time if data.class == String
|
|
8
|
+
data.strftime('%d/%m/%Y') unless data.nil? || data.to_s == ""
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def data_nfse(data)
|
|
12
|
+
# Padrão: 2010-11-11
|
|
13
|
+
data = data.to_time if data.class == String
|
|
14
|
+
data.strftime('%Y-%m-%d') unless data.nil? || data.to_s == ""
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def data_hora_nfse(data)
|
|
18
|
+
# Padrão: 2010-09-16T14:50:00
|
|
19
|
+
data = data.to_time if data.class == String
|
|
20
|
+
data.strftime('%Y-%m-%dT%H:%M:%S') unless data.nil?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def data_hora_gmt(data)
|
|
24
|
+
# Padrão: 2010-09-16T14:50:00
|
|
25
|
+
data = data.to_time if data.class == String
|
|
26
|
+
data.strftime('%Y-%m-%dT%H:%M:%S%:z') unless data.nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def hora_nfse(data)
|
|
30
|
+
# Padrão: 11:26:00
|
|
31
|
+
data = data.to_time if data.class == String
|
|
32
|
+
data.strftime('%H:%M:%S') unless data.nil?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#encoding: utf-8
|
|
2
|
+
module InovadoraXml
|
|
3
|
+
module Modules
|
|
4
|
+
module GeradorXml
|
|
5
|
+
|
|
6
|
+
def elemento(elemento, valor = nil)
|
|
7
|
+
if valor.class == String
|
|
8
|
+
valor.strip!
|
|
9
|
+
valor = I18n.transliterate(valor)
|
|
10
|
+
regex = /[^a-zA-Z0-9,.\-_\/:\[\]\=\$\@\{\}\/\n ]/
|
|
11
|
+
#regex = /[^a-zA-Z0-9,.\-_\/:\[\]\=\$\@\{\}\/ ]/
|
|
12
|
+
valor = valor.gsub(regex, "")
|
|
13
|
+
end
|
|
14
|
+
self.xml_nfse.send(elemento, valor) {
|
|
15
|
+
yield if block_given?
|
|
16
|
+
} unless valor.nil?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def grupo(elemento, id = nil)
|
|
20
|
+
if id.present?
|
|
21
|
+
self.xml_nfse.send(elemento, id) {
|
|
22
|
+
yield if block_given?
|
|
23
|
+
}
|
|
24
|
+
else
|
|
25
|
+
self.xml_nfse.send(elemento) {
|
|
26
|
+
yield if block_given?
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def construir(elemento_raiz)
|
|
32
|
+
builder = Nokogiri::XML::Builder.new() do |xml|
|
|
33
|
+
self.xml_nfse = xml
|
|
34
|
+
elemento(elemento_raiz, get_namespace) {
|
|
35
|
+
yield
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
xml_builder = builder.to_xml(indent: 2, encoding: "UTF-8")
|
|
39
|
+
doc = Nokogiri::XML(xml_builder, &:noblanks)
|
|
40
|
+
return doc
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#encoding=utf-8
|
|
2
|
+
module InovadoraXml
|
|
3
|
+
module Modules
|
|
4
|
+
class XmlRetorno
|
|
5
|
+
extend ActiveModel::Naming # Requer dependencias para uso do ActiveModel::Errors
|
|
6
|
+
attr_accessor :errors, :fnnfse, :fnnfses_xml, :numero_nota, :link_nota, :protocolo_prefeitura,
|
|
7
|
+
:codigo_verificacao_nfse, :datahora_aprovacao,
|
|
8
|
+
:cancelada, :data_hora_cancelamento
|
|
9
|
+
|
|
10
|
+
def initialize args={}
|
|
11
|
+
self.errors = args[:errors] || ActiveModel::Errors.new(self)
|
|
12
|
+
self.fnnfse = args[:fnnfse]
|
|
13
|
+
validar_dados_iniciais(args)
|
|
14
|
+
persistir_fnnfses_xml(args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validar_dados_iniciais(args)
|
|
18
|
+
if self.fnnfse.nil?
|
|
19
|
+
self.errors.add(:base, "NFSe não encontrada")
|
|
20
|
+
end
|
|
21
|
+
if args.blank? || args[:xml_envio].blank?
|
|
22
|
+
self.errors.add(:base, "XML para envio está em branco")
|
|
23
|
+
end
|
|
24
|
+
if args.blank? || args[:xml_retorno].blank?
|
|
25
|
+
self.errors.add(:base, "XML de retorno está em branco")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def valid?
|
|
30
|
+
self.errors.blank?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def retornou_dados_nota?
|
|
34
|
+
self.errors.add(:base, "Número da nota não encontrado no XML de retorno da prefeitura") if self.numero_nota.blank?
|
|
35
|
+
# self.errors.add(:base, "Link da nota não encontrado no XML de retorno da prefeitura") if self.link_nota.blank?
|
|
36
|
+
self.errors.add(:base, "Código verificação da nota não encontrado no XML de retorno da prefeitura") if self.codigo_verificacao_nfse.blank?
|
|
37
|
+
self.errors.add(:base, "Data aprovação da nota não encontrado no XML de retorno da prefeitura") if self.datahora_aprovacao.blank?
|
|
38
|
+
self.valid?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def save
|
|
42
|
+
if retornou_dados_nota?
|
|
43
|
+
if self.valid?
|
|
44
|
+
self.fnnfse.nao_validar_nota_autenticada = true
|
|
45
|
+
self.fnnfse.fnnfses_sancionada.numero_nota = self.numero_nota
|
|
46
|
+
self.fnnfse.fnnfses_sancionada.link_nota = self.link_nota
|
|
47
|
+
self.fnnfse.fnnfses_sancionada.codigo_verificacao_nfse = self.codigo_verificacao_nfse
|
|
48
|
+
self.fnnfse.fnnfses_sancionada.datahora_aprovacao = self.datahora_aprovacao
|
|
49
|
+
self.fnnfse.fnnfses_sancionada.protocolo_prefeitura = self.protocolo_prefeitura
|
|
50
|
+
self.fnnfse.save
|
|
51
|
+
end
|
|
52
|
+
if self.cancelada && self.data_hora_cancelamento.present?
|
|
53
|
+
self.fnnfse.reload
|
|
54
|
+
fnnfses_sancionada = self.fnnfse.fnnfses_sancionada
|
|
55
|
+
fnnfses_sancionada.cancelada = true
|
|
56
|
+
fnnfses_sancionada.motivo_cancelamento = fnnfses_sancionada.motivo_cancelamento.to_s + " Data/Hora cancelamento: #{self.data_hora_cancelamento.to_s_br}"
|
|
57
|
+
fnnfses_sancionada.save
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get_texto_errors
|
|
63
|
+
texto_errors = '<erros>\n'
|
|
64
|
+
texto_errors += "<erro>XML apresentou erros na geração e transmissão para o webservice</erro>\n"
|
|
65
|
+
if self.errors.present?
|
|
66
|
+
self.errors.full_messages.each do |msg|
|
|
67
|
+
texto_errors += "<erro>#{msg}</erro>\n"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
texto_errors += '</erros>\n'
|
|
71
|
+
texto_errors
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def persistir_fnnfses_xml(args)
|
|
75
|
+
xml_resposta_ws = (args[:xml_retorno] rescue get_texto_errors)
|
|
76
|
+
if xml_resposta_ws.blank?
|
|
77
|
+
xml_resposta_ws = get_texto_errors
|
|
78
|
+
end
|
|
79
|
+
self.fnnfses_xml = FnnfsesXml.create(
|
|
80
|
+
fnnfse_id: self.fnnfse.id,
|
|
81
|
+
tipo_documento: args[:tipo_documento],
|
|
82
|
+
xml_envio: (args[:xml_envio] rescue get_texto_errors),
|
|
83
|
+
xml_resposta: xml_resposta_ws
|
|
84
|
+
)
|
|
85
|
+
if self.fnnfses_xml.try(:get_erros).present?
|
|
86
|
+
self.fnnfses_xml.get_erros.each do |erro|
|
|
87
|
+
self.errors.add(:base, "Erro retornado pela prefeitura: #{erro}")
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if self.errors.blank?
|
|
92
|
+
self.numero_nota = self.fnnfses_xml.get_numero_nota if (self.fnnfses_xml.get_numero_nota.present?)
|
|
93
|
+
self.link_nota = self.fnnfses_xml.get_link_nota if (self.fnnfses_xml.get_link_nota.present?)
|
|
94
|
+
self.codigo_verificacao_nfse = self.fnnfses_xml.get_codigo_verificacao_nfse if (self.fnnfses_xml.get_codigo_verificacao_nfse.present?)
|
|
95
|
+
self.datahora_aprovacao = self.fnnfses_xml.get_data_emissao if (self.fnnfses_xml.get_data_emissao.present?)
|
|
96
|
+
self.protocolo_prefeitura = self.fnnfses_xml.get_protocolo_prefeitura if (self.fnnfses_xml.get_protocolo_prefeitura.present?)
|
|
97
|
+
if (self.fnnfses_xml.get_data_hora_cancelamento.present? rescue false)
|
|
98
|
+
self.cancelada = true
|
|
99
|
+
self.data_hora_cancelamento = self.fnnfses_xml.get_data_hora_cancelamento
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#encoding: utf-8
|
|
2
|
+
module InovadoraXml
|
|
3
|
+
class ServidorWsdl
|
|
4
|
+
attr_accessor :xml_enviar, :metodo_soap, :wsdl, :args_request, :cert, :key, :retorno, :errors
|
|
5
|
+
|
|
6
|
+
def initialize(xml_enviar, metodo_soap, wsdl, cert = nil, key = nil, args_request = {})
|
|
7
|
+
self.xml_enviar = xml_enviar
|
|
8
|
+
self.metodo_soap = metodo_soap
|
|
9
|
+
self.wsdl = wsdl
|
|
10
|
+
|
|
11
|
+
self.cert = cert
|
|
12
|
+
self.key = key
|
|
13
|
+
|
|
14
|
+
self.args_request = args_request
|
|
15
|
+
|
|
16
|
+
self.errors = ActiveModel::Errors.new(self)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def enviar
|
|
20
|
+
begin
|
|
21
|
+
cliente = estabelecer_cliente()
|
|
22
|
+
|
|
23
|
+
response = cliente.request(self.metodo_soap, args_request) do
|
|
24
|
+
soap.xml = self.xml_enviar
|
|
25
|
+
end
|
|
26
|
+
self.retorno = response
|
|
27
|
+
rescue Savon::SOAP::Fault => fault
|
|
28
|
+
self.errors.add(:base, fault.to_s)
|
|
29
|
+
rescue Savon::HTTP::Error => fault2
|
|
30
|
+
self.errors.add(:base, fault2.to_s)
|
|
31
|
+
rescue Exception => e
|
|
32
|
+
self.errors.add(:base, e.message)
|
|
33
|
+
ensure
|
|
34
|
+
return self.errors.blank?
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def estabelecer_cliente
|
|
39
|
+
Savon::Client.new do |wsdl, http|
|
|
40
|
+
wsdl.document = self.wsdl
|
|
41
|
+
http.auth.ssl.cert_file = self.cert unless self.cert.blank?
|
|
42
|
+
http.auth.ssl.cert_key_file = self.key unless self.key.blank?
|
|
43
|
+
http.auth.ssl.verify_mode = :none
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inovadora_xml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.6
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2020-06-12 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -44,13 +44,13 @@ dependencies:
|
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '10.0'
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
|
-
name:
|
|
47
|
+
name: signer
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
49
49
|
none: false
|
|
50
50
|
requirements:
|
|
51
51
|
- - '='
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 1.
|
|
53
|
+
version: 1.4.2
|
|
54
54
|
type: :development
|
|
55
55
|
prerelease: false
|
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -58,23 +58,23 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - '='
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 1.
|
|
61
|
+
version: 1.4.2
|
|
62
62
|
- !ruby/object:Gem::Dependency
|
|
63
|
-
name:
|
|
63
|
+
name: nokogiri
|
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
|
65
65
|
none: false
|
|
66
66
|
requirements:
|
|
67
|
-
- - '
|
|
67
|
+
- - ! '>='
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: 1.4.
|
|
70
|
-
type: :
|
|
69
|
+
version: 1.4.0
|
|
70
|
+
type: :runtime
|
|
71
71
|
prerelease: false
|
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
|
73
73
|
none: false
|
|
74
74
|
requirements:
|
|
75
|
-
- - '
|
|
75
|
+
- - ! '>='
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 1.4.
|
|
77
|
+
version: 1.4.0
|
|
78
78
|
- !ruby/object:Gem::Dependency
|
|
79
79
|
name: savon
|
|
80
80
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -83,7 +83,7 @@ dependencies:
|
|
|
83
83
|
- - '='
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: 1.2.0
|
|
86
|
-
type: :
|
|
86
|
+
type: :runtime
|
|
87
87
|
prerelease: false
|
|
88
88
|
version_requirements: !ruby/object:Gem::Requirement
|
|
89
89
|
none: false
|
|
@@ -91,6 +91,22 @@ dependencies:
|
|
|
91
91
|
- - '='
|
|
92
92
|
- !ruby/object:Gem::Version
|
|
93
93
|
version: 1.2.0
|
|
94
|
+
- !ruby/object:Gem::Dependency
|
|
95
|
+
name: wasabi
|
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
|
97
|
+
none: false
|
|
98
|
+
requirements:
|
|
99
|
+
- - '='
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: 2.5.0
|
|
102
|
+
type: :runtime
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
none: false
|
|
106
|
+
requirements:
|
|
107
|
+
- - '='
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 2.5.0
|
|
94
110
|
description:
|
|
95
111
|
email:
|
|
96
112
|
- evanor.libardoni@gmail.com
|
|
@@ -106,8 +122,18 @@ files:
|
|
|
106
122
|
- Rakefile
|
|
107
123
|
- bin/console
|
|
108
124
|
- bin/setup
|
|
125
|
+
- inovadora_xml-0.0.2.gem
|
|
109
126
|
- inovadora_xml.gemspec
|
|
110
127
|
- lib/inovadora_xml.rb
|
|
128
|
+
- lib/inovadora_xml/assinador.rb
|
|
129
|
+
- lib/inovadora_xml/custom_exceptions.rb
|
|
130
|
+
- lib/inovadora_xml/custom_exceptions/server_error.rb
|
|
131
|
+
- lib/inovadora_xml/modules.rb
|
|
132
|
+
- lib/inovadora_xml/modules/dados_nfse_service.rb
|
|
133
|
+
- lib/inovadora_xml/modules/formatador_nfse.rb
|
|
134
|
+
- lib/inovadora_xml/modules/gerador_xml.rb
|
|
135
|
+
- lib/inovadora_xml/modules/xml_retorno.rb
|
|
136
|
+
- lib/inovadora_xml/servidor_wsdl.rb
|
|
111
137
|
- lib/inovadora_xml/version.rb
|
|
112
138
|
homepage:
|
|
113
139
|
licenses:
|