inovadora_xml 0.0.8 → 0.0.9
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 +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
|