inovadora_xml 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +43 -0
- data/lib/inovadora_xml.rb +97 -0
- 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 +3 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad13d1d25c4701914abef86f01658c92389afd78bff13f083d05c9837c414014
|
4
|
+
data.tar.gz: db01f6d4256b679ba6b0aa196c339a2dd2316de06a648c42f700f36c7358abc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3299357a42af7a8ac44bac322e5585ff84655a44313af5e9d43c883889af4b9d3f94c79fda5eedc8f9a0caf6725a8ecade429dfb0ae63f8651fb9c4e087a6ca9
|
7
|
+
data.tar.gz: a36581b84ad314bb3b80c6f107628fcf2bf0baaab27f84035a4feda52875225b95bba7b8298f86930123ae6a46e8e56b86c26dee2f81d7a8d6a13c512728cddc
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# InovadoraXml
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/inovadora_xml`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'inovadora_xml'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install inovadora_xml
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/inovadora_xml. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
36
|
+
|
37
|
+
## License
|
38
|
+
|
39
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
40
|
+
|
41
|
+
## Code of Conduct
|
42
|
+
|
43
|
+
Everyone interacting in the InovadoraXml project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/inovadora_xml/blob/master/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'action_pack'
|
3
|
+
|
4
|
+
require "inovadora_xml/version"
|
5
|
+
|
6
|
+
require "inovadora_xml/assinador"
|
7
|
+
require "inovadora_xml/servidor_wsdl"
|
8
|
+
|
9
|
+
|
10
|
+
module InovadoraXml
|
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
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Junior Libardoni
|
@@ -100,7 +100,19 @@ email:
|
|
100
100
|
executables: []
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
|
-
files:
|
103
|
+
files:
|
104
|
+
- README.md
|
105
|
+
- lib/inovadora_xml.rb
|
106
|
+
- lib/inovadora_xml/assinador.rb
|
107
|
+
- lib/inovadora_xml/custom_exceptions.rb
|
108
|
+
- lib/inovadora_xml/custom_exceptions/server_error.rb
|
109
|
+
- lib/inovadora_xml/modules.rb
|
110
|
+
- lib/inovadora_xml/modules/dados_nfse_service.rb
|
111
|
+
- lib/inovadora_xml/modules/formatador_nfse.rb
|
112
|
+
- lib/inovadora_xml/modules/gerador_xml.rb
|
113
|
+
- lib/inovadora_xml/modules/xml_retorno.rb
|
114
|
+
- lib/inovadora_xml/servidor_wsdl.rb
|
115
|
+
- lib/inovadora_xml/version.rb
|
104
116
|
homepage:
|
105
117
|
licenses:
|
106
118
|
- MIT
|